’,‘?’,‘,’)存在规定的求值顺序,运算符和
都是先求左值,如果有需要再求右值,表达式a?b:c也是先求a的值如果有需要再计算b或c,而逗号运算符,先对左侧操作求值,然后该值被丢弃,再对右侧操作求值。f(x,y)中x和y的求值顺序时未定义的。g((x,y))中x和y的求值顺序是先计算x,丢弃之后再计算y,而函数g传入的参数也是只有一个。赋值操作符并不保证任何求值顺序,比如下面的例子:x=y[i++],并不能保证左侧先执行。不要使用位运算符(,
,~)来替代逻辑运算符(,
,!),有时候运行结果正常只是巧合,意义不同,并且逻辑运算符有求值顺序,能够避免错误访问。整数溢出,这里不是类型长度导致的溢出,而是指有符号整型的溢出,两个有符号整型计算,结果可能会溢出。而溢出的结果是未定义的,各编译器实现不同,因此不能使用类似下面的方式来检测溢出if(a+b0),而应该把他们都转换成无符号整型进行比较if((unsigned)a+(unsigned)bINT_MAX)main函数应该有返回值。连接4.1什么是连接器连接器不理解C语言,编译器的责任是把C语言翻译成连接器能够理解的形式。连接器的作用是把编译器或者汇编器生成的若干目标模块,整合成一个载入模块或者一个可执行文件的实体。程序中每个函数或者外部变量没有声明为static,就都是一个外部对象。连接器通常把一个目标模块看成是一组外部对象组成。工作过程:连接器的输入是目标模块,输出是载入模块,连接器读入目标模块和库文件,同时生成载入模块,对每个目标模块中的每个外部对象,连接器都要检查载入模块,看是否已有同名的外部对象,如果没有就写入载入模块,如果有就进行同名冲突处理。一个目标模块中引用了其他目标模块的外部对象时,生成载入模块时需要记录这些引用,直到读入定义该外部对象的模块时,修改载入模块中的标记,标记该外部对象不再是未定义的。4.2声明与定义变量的定义如果出现在所有函数体之外,称为外部对象的定义。外部变量的定义如果没有指定初始值,那么多次定义可能能够编译通过,但是不应该这样做,每个外部变量只应该定义一次。4.3命名冲突与static修饰符为了避免可能出现的命名冲突,如果一个函数仅被同一文件中的其他函数调用,我们应该把它声明为static。4.4形参实参返回值任何一个函数,在被调用的每个文件中,都在第一次被调用之前进行了声明或者定义就不会出现参数或者返回值的错误。也就是说在某些时候,调用函数之前可以不声明或者定义该函数,此时返回值和参数有有一些默认的规则。这不是常规做法?4.5检查外部类型保证一个特定的名称的所有外部定义在每个目标模块中具有相同的类型,是程序员的责任,编译器可能检测不到这种错误。如:externintn;longn;charfilename[]=/ect/passwd;externchar*filename;4.6头文件避免上述问题的方法是,外部对象在一个地方声明,这个地方应该是一个头文件中。定义外部变量的地方也应高包含该头文件。库函数5.1getchar返回整形的getchar,看一个例子c=getchar,如果c被定义为char类型,结果是c无法容纳下所有字符,包括EOF,有时候编译器在这类情况下会把返回结果截断。5.2文件操作,为了保持与过去不能同时进行读写操作的程序的向下兼容性,一个输入操作,不能随后紧跟着一个输出操作,如果需要同时进行输入和输出操作,需要在其中插入fseek函数5.3缓冲输出与内存分配程序输出的两种方式:第一种叫做及时处理,另外一种方式是先暂存起来,然后大块写入。前者旺旺造成较高的系统负担。通过调用库函数setbuf(stdout,buf)来实现。在使用setbuf时,需要注意buf变量的生命周期,在指针执行打印之前buf不能被释放。5.4使用errno检测错误在调用库函数时,应该先检查返回值,在确认函数执行失败的情况下,再查看errno查看错误原因。这么做的原因是在执行成功情况下errno也可能被上次库函数设置,而没有清零。5.5signal信号处理函数非常复杂棘手,而且具有一些从本质上而言的不可移植性,signal处理函数应该尽可能的简单,并把他们组织在一起。事件处理函数必须是不对全局变量产生影响的可重入的函数。常用的做法是打印错误信息,然后调用longjmp或者exit退出程序。预处理器宏只对程序文本起作用。宏提供了一种对组成程序字符进行变换的方式,而并不是作用于程序中的对象。6.1注意宏定义中的空格6.2宏不是函数,所以我们需要:宏定义中的每个参数都用括号括起来宏定义整个表达式也应该用括号括起来宏中的参数可能被求值多次,所以要确保宏的参数没有副作用,不如不能传递类似(a++)这类的参数宏可能产生非常庞大的表达式6.3宏不是语句,看下面的例子#defineassert(e)if(!e)assert_error(__FILE__,__LINE__)if(x0y0)assert(xy)elseassert(yx)最终展开之后else错误的宏定义中的if结合了。6.4宏不是类型定义#defineT1structfoo*T1a,b;展开表达式,b并不是我们想要的类型。因此我们应该使用typedef来定义类型。可移植性缺陷7.1c标准的变更7.2标示符名称的限制7.3整数的大小7.4字符是有符号整数还是无符号整数字符转换为较大的数时,如果字符的最高位是1,那么转换成无符号数还是有符号数?可以声明为unsignedchar类型,这样编译器在转换时,只是把多余的位补0注意,不能使用(unsigned)c的方式获得无符号整数,因为这个操作会先把c转换为int,而这个结果可能是非预期的。7.5移位运算向右移位时多出来的空位是由0来填充,还是由符号位副本来填充?无符号数进行移位,空位用0来填充,有符号数移位,可以用0也可以用符号位来填充,所以,如果