C专家编程-Chapter2

 无论在什么情况下看到malloc(strlen(str)); 几乎可以断定它是错误的,而malloc(strlen(str)+1);才是正确地。因为其他的字符串处理库函数几乎都包含一个额外空间,用于容纳字符串结尾的'\0',所以人们容易忽略strlen这个特殊情况。

多做之过:
switch:fall through的存在要求自己注意添加break到每个case中。标准中规定switch语句中可有257个case标签,从而满足8bit字符的所有情况(256个可能的值和EOF)。 switch语句忽略除了标签后的所有语句,所以可以在switch的左括弧后面定义局部存储变量,但尽量不要这样做,容易混乱。const并不是真正的常量,例如
const int two =2;
swith(2){
case 1: printf();
case two: printf(); //这句会提示需要常量表达式,从而说明two并不是常量
}

break语句挑出的是最近的循环语句或者switch语句,而不是if语句。

ANSI C中相邻的字符串常量将被自动合并成一个字符串。如char *ave[]={
"color",
"big",
"Cray"
"kill"
};这样ave[2]就成了"Craykill"。而字符串的数目也比以前少了一个,当访问ave[3]是就不知道是内存的什么值了。

定义C函数时,在缺省情况下函数名字是全局可见的。可以在函数的名字前面价格extern关键字,也可以不加,效果是一样的。这个函数对于链接到它所在的目标文件的任何东西是可见的,如果想限制这个函数的访问就必须价格static关键字,从而只在这个文件内可见。由于对于默认的全局可见,用户编写的函数会取代相应的同名的库函数。

误做之过:同一个符号在C中的不同的上下文条件下会有多种用途。例如 int apple = sizeof(int)*p; 编译器认为p是未定义的符号。说明sizeof先执行sizeof(int)在和p相乘。

运算符优先级:.的优先级高于* *p.f ==*(p.f)       []高于*  int *ap[]表示ap四个元素为int指针的数组。 ==和!=高于位操作符和赋值符。算数运算符高于移位运算符,逗号运算符级别最低。如果在表达式中如果有布尔操作、算术运算、位操作等混合计算,你始终应该在适当的地方加上括号,使之清楚明了。虽然有了优先级和结合性告诉你哪些符号组成一个意群,但是意群内部如何进行计算的次序是未定义的。如x = f() +g()*h(); g()和h()的返回值组成一个意群,但是g()和h()的调用可能以任何顺序出现,例如h()的调用可能早于g(),类似f()可能在乘法之前也可能在乘法之后执行。如果编程时要依赖这些意群的计算的先后顺序,那就是不好的编程风格。

结合性:统一优先级的运算符出现在一个表达式中的运算顺序。例如a=b=c。所有的赋值符都具有右结合性,表达式最右边的最先执行,上例中c先赋值给b,然后b再赋值给a。而位操作符等则是从左向右依次执行。但是在函数调用中,各个参数的计算顺序是不确定的。
gets()从输入流中读入一个字符串,但是它并不检查缓冲区空间,如果他读入的的字符数量超过了缓冲区的空间,他就会愉快的将多出来的字符继续写入到堆栈中,这就覆盖了堆栈中原有的内容。C的官方手册中强烈建议用fgets()取代gets()。

少做之过:
C语言中,自动变量在堆栈中分配内存。当包含自动变量的函数或代码块退出时,它们所占用的自动内存便被回收,它们的内容肯定会被下个被调用的函数所覆盖。以下为几个方案:
1.返回一个指向字符串常量的指针
char * func() {return "Only works for simple strings";}
这是最简单的解决方案。并不适用。
2.使用全局声明的数组
char *fun(){
my_global_array[i] = 
return my_global_array;
}
这适用于自己创建字符串的情况,缺点在于任何人任何时候都有可能修改这个数组,而且该函数的下一次调用也会覆盖这个数组的内容。
3.使用静态数组
char * func(){
static char buffer[20];
return buffer;
}
这就可以防止别人修改这个数组,只有拥有指向该数组的指针的函数(通过参数传递给它)才能修改这个静态数组。但是该函数的下一次调用将覆盖这个数组的内容,所以调用者必须在此之前使用或者备份数组的内容。和全局数组一样,大型缓冲区如果闲置不用是非常浪费内存空间的。
4.显示分配一些内存,保存返回值。
char * func(){
char * s = malloc(120);
return s;
}
这个方法具有静态数组的优点,而且每次调用时都会创建一个缓冲区,所以不会被后来的函数调用所覆盖。它适用于多线程的代码。缺点在于程序员必须承担内存管理的责任,这项任务可能容易也可能复杂。如果内存尚在使用就释放或者出现内存泄漏(不在使用的内存位回收),就会产生令人难以置信的bug。
5.最佳的方法是调用分配内存来保存函数的返回值。为提高安全性,调用者应该同时指定缓冲区的大小。
void func(char * result, int size){
strncpy(result,"abced",size);
}
buffer = malloc(size);
func(buffer,size)
free(buffer);
如果程序员可以在同一代码块中同时进行malloc和free操作,内存管理是最为轻松的。

posted @ 2010-01-14 05:54  莫忆往西  阅读(126)  评论(0编辑  收藏  举报