读《C陷阱和缺陷》(六)

2014121419:52:26

#include<stdio.h>

struct logrcc{

int date;

int time;

int code;

}

main()

{

printf("Hello world !\n");

}

这段代码,在DEV编译器上不会出错,但在linux上报警告,但是仍然可以运行。刚开始看这段代码时,感觉是错的,但是它绝对是运行通过了,这是c陷阱的分号用法的不规范造成的。因为编译器默认main函数返回值为struct logrcc类型了。

#include<stdio.h>

struct logrcc{

int date;

int time;

int code;

}

myfun()

{

printf("Hello world !\n");

}

int main()

{

 myfun();

return 0;

}

如果把代码换成这个这样,我想,错误就会更隐蔽了。他在linux上也不会报错。

详情参考:《C Traps and Pitfalls》【美】Andrew Koenig著。P26

 

2014121420:01:28

如果f是一个函数,f( );是一个函数调用语句,而f;却是一个什么也不做的语句。

更确切的说这个语句是计算函数f的地址,却并不调用该函数。

 

2014121420:36:32

if(x==0)

if(y==0)

error();

else{

z=x+y;

f(&z);

}

这段代码,我的第一眼认为,没有啥错误啊,但是,其实这里隐藏着很大的陷阱。看到这段代码,我们很容易想到语句结构中的一句语句:

比如:

while1

while1

printf(“***\n”);

printf(“++++\n”);

这个语句会死循环执行第一个printf函数,不会执行第二个printf函数。这个很容易理解,如果给他们加个大括号,这样他们就不会出现一些意想不到的结果了。

比如:

While1

{

While

{

printf(“***\n”);

printf(“++++\n”);

 

}

}

再看看上面的情况,if-else语句的正确执行方式是:

ifx==0{

ify==0{

error();

}

else{

z=x+y

f&z);

}

}

这个不像是while语句中只会执行下面的一句语句下面的一个语句还可以,if-else语句中,else会与同一个括号中上一个最近的if结合

 

2014122918:36:45

以前一直不知道位运算中的&|、!~是什么情况,今天看了一下位运算,感觉好简单,前两者跟运算符&&||差不多,只不过位运算中操作是按二进制的方法操作的,比如1012他们的二进制为:10101100,进行&运算,每一位比较出来的结果为:1000,结果是8。进行|运算,每一位比较的结果是:1110,结果是14。不同是~运算符,我的理解是,把按位运算的数变成2进制,然后取反。取反后,根据情况将他们返回到二进制数,然后再变成十进制,这是计算机的操作方法。例如:7(二进制0000 0000 0000 0111----取反后(1111 1111 1111 1000)这是计算机的二进制补码形式存储在计算机中的,我们要找到他的反码,然后根据情况,得到二进制数,再化成十进制。则(1111 1111 1111 1000)一看是某个负数的补码,所以先取反码,再加一。取反后(1000 0000 000 0111,注意首位是符号位不能变),加一后(1000 0000 000 1000),结果为:-8。再来看看-7。首先写出二进制的-71000 0000 0000 0111)取反后(1111 1111 1111 1000),这时需要将这个反码+1,(1111 1111 1111 1001)变成补码,然后再取反变成原码(0000 0000 0000 0110),即是一个二级制数,再把它变成十进制为6,暂且这样理解。网上的资料比较多,介绍正负数的源码和补码,反码的问题很清楚。

 

2014123019:09:41

#include<stdio.h>

int main()

{

 int a[3]={5,9,6};

 int temp=10;

 printf("temp = %d\n",temp);

 a[3]=12;

 printf("temp = %d\n",temp);

 printf("%d,%d,%d,%d",a[0],a[1],a[2],a[3]);

return 0;

}

我们先看看这个代码,我们首先定义了一个长度为3的数组,然后初始化5 9 6,然后再定义一个变量temp赋初始值为10,然后我们将啊a[3]赋值为12;最后的答案为:

temp = 10

temp = 12

5,9,6,12

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

Process exited after 0.03524 seconds with return value 0

请按任意键继续. . .

我们发现temp的值被换成了12,这就是数据的越位,在系统中,编译器不会检查到这个错误,这也是C语言的缺陷之一。

原因很简单,在内存中,数据都是按顺序存储的,首先我们定义了一个长度为3的数组,在数组之后又定义了一个变量temp,他们在内存中的地址都是有顺序的,在函数未结束之前,他们的内存不被释放,也不能被占用,但是我们找到了他们的地址,然后修改地址里的数据,这样就可以说先去的。当在数组没有越位的情况下,这个不会出错,会如实的输出答案为5 9 6,但是我们找到了temp的地址,也就是通过a[3]找到的,然后修改a[3]里的数值,就是修改temp变量的值,这是计算机可以执行的操作,然后我们就看到了上面的结果,5 9 6 12

 

20151519:57:28

1、在外部声明时,C语言中默认变量为0,例如int a;则编译器默认a=0

2extern的用法,详见http://zhidao.baidu.com/question/96882132.html

3、在同一个文件中,函数多次调用一个变量或者是一个函数,我们可以用static来修饰他们一下。

4

#include<stdio.h>

int main()

{

    float a=1.6;

    float b=3.6;

    double c=52.3;

    double d=54.7;

    printf("a+b = %g\nc+d = %g\n",a+b,c+d);

    printf("a+b = %f\nc+d = %lf\n",a+b,c+d);

    return 0;

}

[root@localhost myself自己测试]# ./123

a+b = 5.2

c+d = 107

a+b = 5.200000

c+d = 107.000000

[root@localhost myself自己测试]# 

看结果,%g的功能还可以,比较好,以前都不知道用它。

 

 

20151620:09:44

1、今天看了宏的用法,感觉以前用宏真是太简单,太随意了,还没发现会出现那种情况。

宏并不是函数,也不是语句,他只是一个我们定义的一个类似公式、函数或是语句,当调用时展开公式能够计算结果(我自己的理解)。

2、预防代码写错的方法还有就是:

(1)while( c == '\t' || c == ' ' c == '\n' )

{

    c=getc(f);

}

(2)while( 't'== c || ' ' == c || '\n' ==c )

{

    c=getc(f);

}

第一种方法写的有点不好,因为,如果在c == \t 的赋值过成功,少了一个‘=’号,就会发生致命的错误。

所以在此我们可以直接表达我们的思想的方法 ,就是写成\t == c的样式。这样,即使少了一个‘=’,我们的编译器立刻就会检查出来错误,因为c对‘t’的赋值是非法的。这样避免赋值的错误程序可以避免了。

总结:

看了一遍C陷阱和缺陷,感觉自己对C语言还是知之甚少啊,里面好多涉及的东西还没有接触到,比如代码在计算机中内部真正的运行的方式,这些关系到了计算机组成原理的方面的书籍了。以前看的时候是为了找找C语言上语法上的缺陷,现在看来,这不只有语法上的,还有很多,语法上的,我们多敲电脑,说不定就可以遇见,但是许多细节的问题,也许我们永远都不知道。我还需要继续努力,来补充自己的知识。我发现,自己的C++上的知识忘得也差不多了,下一步,还要去学习C++

送给今后自己的几句话:

1)写代码前,要想想这个代码要干啥。

2)不要为了让程序能够运行而写程序。

3)避免使用指针,提倡使用程序库,使用类来表示概念(C陷阱和缺陷采访录)。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值