c traps and pitfalls 阅读笔记

最新更新:09-05-2011 =

======================


----------------位域问题----------------------

  1 #include <stdio.h>
  2 
  3 struct test {
  4     unsigned char data:2;
  5     unsigned char data1:3;
  6     unsigned char data2:3;
  7 };
  8 
  9 int main(void)
 10 {
 11         struct test t = {
 12                 .data = 1,
 13         };
 14 
 15         t.data = 7;
 16         printf("sizeof(t)=%d\n", sizeof(t));
 17 //      printf("sizeof(t.data)=%d\n", sizeof(t.data)); //compile error
 18         printf("t.data = %d\n", t.data);
 19 
 20         return 0;
 21 }

结果是:
sizeof(t)=1
t.data = 3

----------------------------------------------


*   用双引号引起的字符串,代表的是一个指向无名数组起始字符的指针,
  该数组被双引号之间的字符以及一个额外的二进制值为零的字符'\0'
  初始化。
*  strlen(str) 返回的是字符串的不包括结尾的'\0'在内的字符的个数。
   下面分析一个例子:
   char * r, *malloc();
   r = malloc(strlen(s) + strlen(t));
  strcpy(r, s);
  strcat(r, t);
   错误分析:
   1. malloc可能无法提供请求的内存,所以要检查malloc的返回值。
    2. r 作为局部变量会被自动释放,就会造成内存泄漏,所以必须显式调用free()
    3. malloc并没有分配到足够的内存。因为strlen(s)的值为n,那么需要占用n+1个
       字符空间,多出一个给'\0'。

* 用单引号引起的一个字符实际上代表一个整数。
 
*  任何C变量的声明都由两部分组成:类型和一组类似表达式的声明符。
  对该表达式求值应该返回一个声明中给定的类型的结果。
    比如 float * p(), 那么*p() 应该返回一个 float 类型的值,所以
    p 是一个返回 float * 类型值的指针。
* 类型转换符: 把声明中的变量名和末尾的分号去掉,将剩余部分用
           圆括号括起来即可。
* 如果 fp 是个指向函数的指针,调用时:(* fp)();
------> 例子1
 void (*signal(int, void (*) (int))) (int);
 <===> typedef void (*HANDLER) (int);
             HANDLER signal(int, HANDLER);
-----> 例子2
    (* (void (*) () ) 0) ();
 注意: (*0) (); 并不能调用地址0处的函数,因为 * 需要一个指针做操作数。
------------------------------------我的积累----------------
#include <stdio.h>

int f1(int a)
{
        return ++ a;
}

int f2(int a)
{
        return 5;
}

int (*fun(int a, int (*f)(int)))(int)
{
    printf("f returned: %d\n", (*f)(a));

    return &f2;
}

int main(void)
{
      printf("In main:%d\n", (*fun(1, f1))(2));
       return 0;
}
=======================================
==== 错误例子

struct a{
...
}
main(void){
...
}
表示 main的返回类型为 struct a 类型。

==== swich 语句
每个 case 结尾容易忘记break;
如果程序真不需要break,那么最好在结尾注明,这是个好的编程习惯。

===== else问题
* else始终与同一对花括号内最近未匹配的if结合。
- 例子
 if (x == 0)
     if(y == 0) error();
else{
     z = x + y;
}
实际上被解释为:

if (x == 0) {
   if (y == 0) error();
   else {
        z = x + y;
   }
}

* 建议:良好的编程习惯是:即使是一条语句也加上{}.
比如:
if (x > 0) {
    x = 1;
} else {
   x = -1;
}
==============指针和数组
*   c语言中只有一位数组,而且数组的大小必须在编译时就作为一个常数确定下来。
        数组的元素可以是任何类型,也可是另一个数组。
* int calendar[12][31];这个语句声明了calendar是一个数组,该数组拥有12哥数组类型的元素,
其中每一个元素都是一个拥有31个整型元素的数组(而不是 一个拥有31个数组类型的元素的数组,
其中每一个元素 又是一个拥有12个整型元素的数组)。
 为什么呢?
  *
   a[i] <===>  *(a+i) <===> *(i+a) <===> i[a] //不提倡
*
a[4][7] <===> *(*(a+4)+7)
*
==============  08_08_2011
-- 复制指针并不复制指针所指向的数据。
-- 任何时候,常数0 被转化为指针使用时,这个指针绝对不能被解除引用。
   if (p == (char*) 0) ... // 正确
   if (strcmp(p, (char*)  == 0) ... //错误
-- 运算符的求值顺序:
   &&, || 首先对左侧操作数求值,只在需要时才对右侧操作数求值(对于&&是左侧为真,对于||是左侧为假时)。
   a?b:c中,首先对a求值。根据a的值再求b或者c的值。
   逗号运算符,首先对左侧操作数求值,然后该值被“丢弃”,再对右侧操作数求值。
-- 可能出现错误:
  i = 0;
  while (i < n)
        y[i] = x[i++];
 这段代码假设了y[i]的地址在i 的自增操作执行之前被求值,这一点是没有任何保证的。
 最好换成如下的写法:
 i = 0;
 while ( i < n) {
   y[i] = x[i];
   i ++;
 }

==== 整数的溢出
C语言中存在两种整数算术运算,有符号运算和无符号运算。在无符号运算中没有“溢出”一说:所有无符号运算都是以
2的n次方为模,n为结果的位数。如果参与运算的一个是有符号数,一个是无符号数,那么有符号整数会被转化为
无符号整数,“溢出”也不会发生。
* 当两个操作数都是有符号数时,“溢出“就有可能发生,而且"溢出"的结果是未定义的。
 例子:
 假定 a和b是两个非负整型变。那么检查是否会溢出的方法可以如下:
  if ((unsigned) a + (unsigned) b > INT_MAX)
      complain();
  此处 INT_MAX代表最大的整数值。ANSI C在limits.h中定义了INT_MAX.
另一个方法是:
 if (a > INT_MAX - b)
    complain();
 更多相关的介绍: http://blog.csdn.net/duduhaha/article/details/624123

=== main函数的返回值问题
大多数C语言实现都通过main函数的返回值告知操作系统该函数的执行是成功还是失败。典型情况是,
返回值为0代表执行成功,返回值非零表示执行失败。
=== 连接器
连接器的输入是一组目标模块和库文件。输出是一个载入模块。对于每个目标模块中的每个外部对象,
连接器都要检查载入模块,看是否有同名的外部对象。如果没有,连接器就将该外部对象加到载入模块中,
如果有,连接器就开始处理命名冲突。

===函数参数相关问题
例子:
#include <stdio.h>
int main(int argc, char* argv[])
{
    int i;
    char c;

    for (i = 0; i < 5; i++) {
        scanf("%d", &c);
        printf("%d", i);
    }

    printf("\n");
     return 0;
 
}
在我的系统上运行结果是:
1 2 3 4 5
00000
... 而且一直不会结束,最后只好ctrl + c
原因: c被声明为 char类型,而不是int 类型。程序中scanf函数得到的是一个指向字符的指针,scanf并不能分辨
这种情况,它只是将这个指向字符的指针作为指向整数的指针而接受,并在在指针指向的位置存储一个整数。
因为整数所占的存储空间大于字符所占空间,字符c附近的内存(i的低端部分)将被覆盖。因此,每次读入
一个数值,i的低端都会被覆盖为0,所以循环一直进行。

============外部类型的检查
保证一个特定名称的所有外部声明都有相同的类型,而且是严格意义上的“相同类型”。
如下就是一个错误的例子:

char filename[] = "/etc/passwd";
而在另一个文件中包含声明:
extern char* filename;
第一个声明中, filename 是一个字符数组,第二个,是一个指针。内存布局是不同的。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值