C Traps and Pitfalls 随笔

C陷阱于缺陷这本书到图书馆借了很久,一直都没有细细的看,现完整的看了之后把认为重要的,一般人可能忽视的问题给做了笔记,希望有所帮助

 

2010.02.25
二维数组:
int calendar[12][31];
int * p;
int i;
calendar[4]的含义:
calendar[4]是calendar数组的第5个元素,是calendar数组中12个有着31个整型元素的数组之一。
szieof(calendar[4])的结果是31与sizeof(int)的乘积。
p=calendar[4];表示指针p指向calendar[4]中下标为0的元素。
i=calendar[4][7];i=*(calendar[4]+7);i=*(*(calendar+4)+7);为同样的功能。
p=calendar;非法,calendar是一个二维数组,即"数组的数组",被转换为一个指向数组的指针;p为指向整型变量的指针。
int (*monthp)[31];monthp=calendar:为合法的。monthp指向calendar的第一个元素
calendar[i][j]=0;与*(*(calendar+i)+j)=0;等价。
for(monthp=calendar;monthp< &calendar[12];monthp++)//可以用monthp步进的方式便利数组calendar;
for(monthp=calendar;monthp< &calendar[12];monthp++)
    int * dayp;
    for(dayp=* monthp;dayp<&(*monthp)[31];dayp++)
        *dayp=0;
总结:
    可能在C语言中二维数组不是连续分配的,第一维中的每一个元素为第二维数组的首地址。
非数组指针使用:
char * r;
r=malloc(strlen(s)+strlen(t)+1);
if(!r) //error control;
strcpy(r,s);
strcat(r,t);
//control;
fress(r);
malloc函数可能无法提供请求的内存,将返回空指针。
strlen返回字符串中所包含的字符数目,作为结束标志的空字符未计算在内。strlen(s)的值为n,则字符串实际需要n+1个字符的空间。

char hello[]="hello";
printf("%s/n",hello);与printf("%s/n",&hello[0])等效。

char *p,*q;
p="xyz";//p的值不是"xyz",p而是指向字符数组的气质元素的指针;
q=p;//p和q指向内存中同一地址的指针。该赋值语句并没有同时复制内存中的字符
q[1]='Y';//q,p指向的字符串都改变。

数组中实际不存在的“溢界”元素的地址位于数组所占内存之后,这个地址可以用于复制和比较,引用该元素则非法。

static:
static int a;表示a的作用域限制在一个源文件中,对于其他源文件,a是不可见的。static也适用于函数,如果函数f需要调用g,而且只有函数f需要调用g,我们可以把函数f与g都放在同一个源文件中,并且声明g为static。

getchar()返回int类型,如果定义 char c=getchar(),这意味着c无法容易所有可能的字符,特别是,可能无法容下EOF。最终有两种结果:一种是:某些合法的输入字符在被"截断"后使得c的取值和EOF相同;另一种是:c根本不可能渠道EOF这个值。

更新文件顺序:
贝勒保持与过去不能同时读写操作的程序的向下兼容性,一个输入操作不能随后直接紧跟一个输出输出操作,反之亦然。如果要同时进行输入和输出操作,必须在其中插入fseek函数的调用。
FILE * fp;
struct record rec;
....
while(fread((char* )&rec,sizeof(rec),1,fp)==1)
{
    /*----operation---*/
    if(/*rec must rewrite*/)
    {
        fseek(fp,-(long)sizeof(rec),1);
        fwrite((char *)&rec,sizeof(rec),1,fp);   
    }
}
问题出在:如果一个记录需要被重新写入到文件,fwrite函数得到执行,对这个文件执行的下一个操作将是循环开始的fread函数。因此在fwrite函数调用与fread函数调用之间缺少了一个fseek函数的调用,无法完成上述操作。需在fwrite后加入    feek(fp,0L,1);

缓冲输出与内存分配:
setbuf(stdout.buf);语句将通知输入/输出库,所有写入stdout的输出都应该使用buf作为输出缓冲区,知道buf缓冲区被填满或者程序员直接调用fflush,buf缓冲区的内容才实际写入到stdout中。缓冲区的大小由系统头文件<stdio.h>中的BUFSIZ定义。
#include<stdio.h>
int main()
{
    int c;
    char buf[BUFSIZ];
    setbuf(stdout,buf);
    while((c=getchar())!=EOF)
    putchar(c);
    return 0;
}
程序中有细微的错误。程序中对库函数setbuf的调用,通知了输入/输出库所有字符的标准输出应该首先缓存到buf中。buf缓冲区的最后一次清空是在main函数结束之后,作为程序交回控制给操作系统之前C运行库所必须进行的清理工作的一部分。但是,在此之前buf字符数组已经被释放了。避免这种错误两种方法。1,让缓冲数组成为静态数组,也可以把buf声明移到main函数之外。2,动态分配缓冲区,在程序中不主动释放缓冲区。

使用errno检测错误
/*调用库函数*/
if(errno)
/*operation*/
错误原因在于:在库函数调用没有失败的情况下,并没有强制要求库函数一定要设置errno为0,这样,errno的值为前一个执行失败的库函数设置的值。
errno=0;
/*调用库函数*/
if(errno)
/*operation*/
错误。库函数在调用成功时,既没有强制要求对errno清零,但同时也没有禁止设置errno。因此,在调用幻术时,应该首先检测作为错误提示的返回值,确定程序执行已经失败。然后,再检查errno,来搞清楚原因:
/*调用库函数*/
if(返回的错误值)
检查 errno

预处理器:
宏提供了一种对组成c程序的字符进行变换的方式,而并不作用域程序中的对象。
#define f (x) ((x)-1)被定义为(x)((x)-1),不能忽略宏定义红的空格。

字符是有符号整数还是无符号整数
编译器在转换char类型到int类型时,需要作出选择:应该将字符作为有符号数还是无符号书处理?如果是前一种情况,编译器在将char类型的数扩展到int类型时,应该复制符号位;如果是后一种情况,编译器只需在多余的位上直接填充0即可。
如果一个字符的最高位为1,编译器是将该字符作为有符号数还是无符号数?它决定了一个8位字符的取值范围是从-128到127,还是从0-255。
错误的认识:如果c是一个字符变量,使用(unsigned)c就可以得到与c等价的无符号整数。这会失败的,因为字符c转换成无符号整数时,c将首先被转换成int型整数,而此时可能得到非预期的结果。
正确的方式是使用语句(unsigned)c,因为一个unsigned char类型的字符转换成无符号整数时无需首先转换成int型整数,而是直接进行转换。

移位运算符
1.在向右移位时,空出的位是由0填充,还是由符号位的副本填充?
与具体的c语言实现有关。如果被移位的对象是无符号数,则空出的位由0填充。如果被移位的对象是有符号数,那么既可以用0进行填充,也可以用符号位的副本填充空出的位。
2.移位计数(即移位错作的位数)允许的范围?
如果被移位的对象长度是n位,则移位计数在0到(n-1)之间。

内存位置0
null指针不指向任何对象。某些c语言实现对内存位置0强加了硬件级的保护,在其上工作的程序如果错误的使用了一个null指针,将立即终止执行。其他一些c语言实现对内存位置0只允许读,不允许写。在这种情况下,一个null指针似乎指向的是某个字符串,但其内容同时不过是一堆垃圾信息。。还有一些c语言实现对内存位置0既可以读,也可以写,如果误用了null指针,很可能覆盖操作系统的部分内容,造成灾难。

大小写转换
#define toupper(c) ((c)+'A'-'a')
#define tolower(c) ((c)+'a'-'A')

'0'+5的值和‘5’的值相同,这种假定,在机器的字符集中数字是顺序排列的,没有间隔的才成立。
字符串常量可以用来表示一个字符数组,所以数组名出现的地方可以用字符串常量来替换:
"0123456789"[n%10]可以将整数转换成字符。

printf函数:
%d, 打印有符号整数
%u,打印无符号整数。
%s,打印字符串,参数应该是一个字符指针,待输出的字符始于该指针所指向的地址,知道出现一个空字符('/0')结束。如果%s对应的字符串并不是以空字符作为结束标志,printf函数将不断打印出其后的字符,知道内存中某处找到一个空字符。
printf(s),将字符串s中的任何%字符视为一个格式项的标志,因而其后的字符会被视为格式码。
%g,%f,%e,打印浮点数。
%g打印不需要按列对齐的浮点数。它在打印对应的数值(必须是浮点数或双精度)时,会去掉该数值尾缀的零,保留六位有效数字,输出的数字被四舍五入,而不是直接截断。当一个数的绝对值大于999999,按%g打印则采用科学计数发,也四舍五入到6位有效数字。对于比较小的数值,除非该数的指数小于或等于-5,%g采用采用科学计数法来表示。
%e:一律显式按照指数形式,%e格式将打印出小数点后6位有效数字,而非%g格式打印出的数是总共6位有效数字。
%f:强制禁止使用指数形式来表示浮点数。在数值精度方面,%f格式项的要求与%e格式项相同,即小数点后6位有效数字。
%E,%G和对应的%e,%g行为方式相同,出了使用大写的E代替小写的e来表示指数形式。

修饰符
如果一个short整数作为任何一个函数(也包括printf函数)的参数出现,会被自动扩展为一个正常长度的整数。%ld,%lo,%lx,%lu格式符与不加修饰符的行为方式完全相同,只要求long型整数作为参数。l修饰符只对用于整数的格式码有意义。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值