基础C语言知识串串香13☞一些杂散但值得讨论的问题

八、一些杂散但值得讨论的问题

8.1、操作系统的理解:

<1>它是一个管理阶级者,管理所有资源,负责调配优化等操作。这样想象,就像裸机一样的话,要实现LED闪烁的进程、串口传输的进程、蜂鸣器等这些,他们都要抢占一些资源,这个时候没有操作系统,就乱成一锅粥,当有了OS的时候,它就专门负责资源的调配,让各个任务都能很好的实施,起一个决策者的作用。

<2>如果我们要做一个产品,软件系统到底应该是裸机还是基于操作系统呢?本质上取决于产品本身的复杂度。只有极简单的功能、使用极简单的CPU(譬如单片机)的产品才会选择用裸机开发;一般的复杂性产品都会选择基于操作系统来开发。

<3>操作系统负责管理和资源调配,应用程序负责具体的直接劳动,他们之间的接口就是API函数。当应用程序需要使用系统资源(譬如内存、譬如CPU、譬如硬件操作)时就通过API向操作系统发出申请,然后操作系统响应申请帮助应用程序执行功能。

8.2、C库函数和API的关系:

<1>从内核的角度看,需要考虑提供哪些有用的API函数,并不需要关注它们如何被使用。故编程时用API函数会感觉到不好用,没有做优化。系统只负责做出一个可以用的API,没有考虑到用户使用的方便性。所以c库函数对API做了一些优化,让用户使用库函数更容易达到我们想要的目的。

<2>库函数实质还是用的API,或者调用了一个API,也或者调用了更多的API,只不过是做了更多的优化。比如 库函数fopen ,而API是open.

<3>有的库函数没有用到API,比如strcpy函数(复制字符串)和atoi函数(转换ASCII为整数),因为它们并不需要向内核请求任何服务。

8.3、不同平台(windows、linux、裸机)下库函数的差异

(1)不同操作系统API是不同的,但是都能完成所有的任务,只是完成一个任务所调用的API不同。

(2)库函数在不同操作系统下也不同,但是相似性要更高一些。这是人为的,因为人下意识想要屏蔽不同操作系统的差异,因此在封装API成库函数的时候,尽量使用了同一套接口,所以封装出来的库函数挺像的。但是还是有差异,所以在一个操作系统上写的应用程序不可能直接在另一个操作系统上面编译运行。于是乎就有个可移植性出来了。

(3)跨操作系统可移植平台,譬如QT、譬如Java语言。

8.4、

<1>main()函数的写法

int main(int argc, char **argv) ;
int main(int argc ,char *argv[ ] ); 

这两种写法是一样的。二重指针等同于指针数组。

<2>不管是主函数还是功能函数,它都应该有一个返回值,而主函数的返回值是给调用的那个人的。main函数从某种角度来讲代表了我当前这个程序,或者说代表了整个程序。main函数的开始意味着整个程序开始执行,main函数的结束返回意味着整个程序的结束。谁执行了这个程序,谁就调用了main。谁执行了程序?或者说程序有哪几种被调用执行的方法?一个程序当然会运行,那么就是调用了main( ).

<3>inux下一个新程序执行的本质

(1)表面来看,linux中在命令行中去./xx执行一个可执行程序

(2)我们还可以通过shell脚本来调用执行一个程序

(3)我们还可以在程序中去调用执行一个程序(fork exec)

总结:我们有多种方法都可以执行一个程序,但是本质上是相同的。linux中一个新程序的执行本质上是一个进程的创建、加载、运行、消亡。linux中执行一个程序其实就是创建一个新进程然后把这个程序丢进这个进程中去执行直到结束。

新进程是被谁开启?在linux中进程都是被它的父进程fork出来的。

分析:命令行本身就是一个进程,在命令行底下去./xx执行一个程序,其实这个新程序是作为命令行进程的一个字进程去执行的。

总之一句话:一个程序被它的父进程所调用。

结论:main函数返回给调用这个函数的父进程。父进程要这个返回值干嘛?父进程调用子进程来执行一个任务,然后字进程执行完后通过main函数的返回值返回给父进程一个答复。这个答复一般是表示子进程的任务执行结果完成了还是错误了。(0表示执行成功,负数表示失败,正规的要求返回失败的原因,返回-1表示什么,返回-2又表示什么,然后父进程好做相应的处理)

(4) main函数的返回值应当是int 型。父进程要求是int型的,如果写成 float 型,则返回就为0,这样是要出错的。

8.5 用shell脚本来看main()的返回值。如:#!/bin/sh 其文本格式为 .sh

./a.out
echo $?

8.6、argc、argv与main函数的传参:当我们的父进程不需要传参时,就用 int main(void);当我们需要传参时,就应该是int main(int argv ,函数char *argc[ ]);它默认本身就是一个参数,占了argv[0]这个位置,它里面存的是 ./a.out(这个相应变化)

如:./a.out boy girl ;

argv=3; 
argc[0]="./a.out"; 
argc[1]="boy"; 
argc[2]="girl" ;
 printf("%s\n",argc[0]);

解释:argv表示传了多少个参数,argc实质是存的一个指针,也就是一个地址,只是没有一个被绑定的变量名而已。这个地址指向一个字符串,一般字符串都和指针相关。所以可以称之为字符串数组,每一个都存了一个字符串。

在程序内部如果要使用argc,那么一定要先检验argv,先检验个数,然后使用。

8.7、void类型的本质:即使空型又是未知类型,看具体情况。比如一个函数void表示不返回, void * malloc(20);就是未知类型。

(1)编程语言分2种:强类型语言和弱类型语言。强类型语言中所有的变量都有自己固定的类型,这个类型有固定的内存占用,有固定的解析方法;弱类型语言中没有类型的概念,所有变量全都是一个类型(一般都是字符串的),程序在用的时候再根据需要来处理变量。就如:makefile、html语言。

(2)C语言就是典型的强类型语言,C语言中所有的变量都有明确的类型。因为C语言中的一个变量都要对应内存中的一段内存,编译器需要这个变量的类型来确定这个变量占用内存的字节数和这一段内存的解析方法。

(3)void类型的正确的含义是:不知道类型,不确定类型,还没确定类型、未知类型,但是将来一定有类型。

*(4)void a;(编译器可以通过)定义了一个void类型的变量,含义就是说a是一个指针,而且a肯定有确定的类型,只是目前我还不知道a的类型,还不确定,所以标记为void。void “修饰”的是指针,因为指针就是内存地址,它不知道指向的另一个变量是哪一种类型,而变量一定是确定的,void a;就是错误的。

8.9、C语言中的NULL

NULL在C/C++中的标准定义

(1)NULL不是C语言关键字,本质上是一个宏定义,其保护指针的作用,不要让他乱开枪。

(2)NULL的标准定义:

ifdef _cplusplus // 条件编译c++环境
define NULL 0
else
define NULL (void *)0 // 这里对应C语言的情况
endif

解释:C++的编译环境中,编译器预先定义了一个宏_cplusplus,程序中可以用条件编译来判断当前的编译环境是C++的还是C的。

NULL的本质解析:NULL的本质是0,但是这个0不是当一个数字解析,而是当一个内存地址来解析的,这个0其实是0x00000000,代表内存的0地址。(void *)0这个整体表达式表示一个指针,这个指针变量本身占4字节,地址在哪里取决于指针变量本身,但是这个指针变量的值是0,也就是说这个指针变量指向0地址(实际是0地址开始的一段内存)。如 char *p=NULL;因为0地址本身就不是我们来访问的,所以 *p时是不可访问的。在程序运行的逻辑上就不会出错。

正规写:

int *p = NULL;// 定义p时立即初始化为NULL
p = xx;
if (NULL != p)
{
*p // 在确认p不等于NULL的情况下才去解引用p
}

(1)'\0'是一个转义字符,他对应的ASCII编码值是0,内存值是0,一个char空间。

(2)'0'是一个字符,他对应的ASCII编码值是48,内存值是int型48,一个char空间。

(3)0是一个数字,没有ASCll编码, 内存值是int型0,一个int空间。

(4)NULL是一个表达式,是强制类型转换为void *类型的0,内存值是0(内存地址),一个int空间。

8.9.1、运算中的临时匿名变量

<1>“小动作”:高级语言在运算中允许我们大跨度的运算。意思就是低级语言中需要好几步才能完成的一个运算,在高级语言中只要一步即可完成。譬如C语言中一个变量i要加1,在C中只需要i++即可,看起来只有一句代码。但实际上翻译到汇编阶段需要3步才能完成:第1步从内存中读取i到寄存器,第2步对寄存器中的i进行加1,第3步将加1后的i写回内存中的i。

<2> float a=12.3; int b=(int)a; (int )a 就是匿名变量;先找一个内存空间,里面存(int)a; 然后把这个值赋值给b;最后匿名值销毁。

float a; int b=10; a=b/3;左边是3.00000; 右边是3;其中有个匿名变量,先找一个内存空间,里面存 b/3; 然后把它再转换成float型,再赋值个a;最后匿名值销毁。

8.9.2 分析DEBUG宏

学习级:

define DEBUG #undef DEBUG 是注销 DEBUG 宏
ifdef DEBUG
define debug(x) printf(x)
else
define debug(x)
endif

应用级:

ifdef DEBUG
define DBG(...) fprintf(stderr, " DBG(%s, %s( ), %d): ", FILE, FUNCTION, LINE); fprintf(stderr, VA_ARGS)
else
define DBG(...)
endif

解 释:
<1>...表示变参,提示编译器不要对参数个数斤斤计较,不要报错; 其实完全可以把 ...换成 cdw 也是可以的,只是非要装一下而已。

<2> FILE 和 FUNCTION和 LINE 都是c库函数的宏定义,分别表示要输出的这句话属于哪个文件名、属于哪个函数名、在第几行。

<3> 在 fprintf(stderr,"cdw");其中stderr是c库函数中宏定义了的,这是VC6.0找到的 #define stderr (&_iob[2]) ;也就是说stderr是一个被宏定义了的指针,它是标准错误输出流对象(stderr),输出到屏幕上。

fprintf()是C/C++中的一个格式化写—库函数,位于头文件中,其作用是格式化输出到一个流/文件中;(重点是流/文件)

printf()函数是格式化输出函数, 一般用于向标准输出设备按规定格式输出(重点是标准输出设备,有时候输出的不一定显示在屏幕上,只是编译器规定显示到屏幕上而已。)

总结:也就是说printf()其实不是输出屏幕上的,只是这个标准输出设备中,编译器规定显示到屏幕上而已,而真正输出到屏幕是fprintf(stderr,"cdw");其中stderr就是输出到屏幕上的流。它也可以fprintf( FILE *stream, const char *format,...),这个就是输出到文件流中的。

比如:一般情况下,你这两个语句运行的结果是相同的,没有区别,只有一下情况才有区别:运行你的程序的时候,命令行上把输出结果进行的转向,比如使用下面的命令把你的程序a.c运行的结果转向到记事本文件a.txt:a.exe > a.txt

在这样的情况,如果使用printf输出错误信息,会保存到a.txt文件里面,如果使用fprintf输出错误,会显示在屏幕上。

<4>上面中的VA_ARGS也是一个宏定义,表示预处理时实际的参数。
如:DBG("tiaoshi.\n");

则允许的效果是 DBG(debug.c, main( ), 14): tiaoshi.

内核级:

ifdef DEBUG_S3C_MEM
define DEBUG(fmt, args...)printk(fmt, ##args)
else
define DEBUG(fmt, args...)do {} while (0)
endif

往期文章列表:****往期热文:
基础C语言知识串串香(1)

基础C语言知识串串香(2)

基础C语言知识串串香(3)

基础C语言知识串串香(4)

基础C语言知识串串香(5)

基础C语言知识串串香(6)

基础C语言知识串串香(7)

基础C语言知识串串香(8)

基础C语言知识串串香(9)

基础C语言知识串串香(10)

基础C语言知识串串香(11)

基础C语言知识串串香(12)


===========我是华丽的分割线===========


更多知识:
点击关注专题:嵌入式Linux&ARM

或浏览器打开:https://www.jianshu.com/c/42d33cadb1c1

或扫描二维码:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; ...
    朱森阅读 3,422评论 3 44
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,082评论 1 32
  • 今天小编给大家带来c语言学习之路--由浅入深(快速掌握c基础)。温馨提示:亮点在最后! 1.第一个C程序:Hell...
    云上伞阅读 590评论 0 1
  • 1.在C++ 程序中调用被C 编译器编译后的函数,为什么要加extern “C”? 答:首先,extern是C/C...
    曾令伟阅读 916评论 0 4
  • 南国落瑛三千里 丽景东风九万甲 一壶晨露逍遥曲 半杯年华莽莽荫
    茗苡Benjamin阅读 220评论 0 0