2014年12月14日19: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
2014年12月14日20:01:28
如果f是一个函数,f( );是一个函数调用语句,而f;却是一个什么也不做的语句。
更确切的说这个语句是计算函数f的地址,却并不调用该函数。
2014年12月14日20:36:32
if(x==0)
if(y==0)
error();
else{
z=x+y;
f(&z);
}
这段代码,我的第一眼认为,没有啥错误啊,但是,其实这里隐藏着很大的陷阱。看到这段代码,我们很容易想到语句结构中的一句语句:
比如:
while(1)
while(1)
printf(“***\n”);
printf(“++++\n”);
这个语句会死循环执行第一个printf函数,不会执行第二个printf函数。这个很容易理解,如果给他们加个大括号,这样他们就不会出现一些意想不到的结果了。
比如:
While(1)
{
While
{
printf(“***\n”);
printf(“++++\n”);
}
}
再看看上面的情况,if-else语句的正确执行方式是:
if(x==0){
if(y==0){
error();
}
else{
z=x+y;
f(&z);
}
}
这个不像是while语句中只会执行下面的一句语句下面的一个语句还可以,if-else语句中,else会与同一个括号中上一个最近的if结合
2014年12月29日18:36:45
以前一直不知道位运算中的&、|、!~是什么情况,今天看了一下位运算,感觉好简单,前两者跟运算符&&、||差不多,只不过位运算中操作是按二进制的方法操作的,比如10和12他们的二进制为:1010和1100,进行&运算,每一位比较出来的结果为: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。首先写出二进制的-7(1000 0000 0000 0111)取反后(1111 1111 1111 1000),这时需要将这个反码+1,(1111 1111 1111 1001)变成补码,然后再取反变成原码(0000 0000 0000 0110),即是一个二级制数,再把它变成十进制为6,暂且这样理解。网上的资料比较多,介绍正负数的源码和补码,反码的问题很清楚。
2014年12月30日19: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。
2015年1月5日19:57:28
1、在外部声明时,C语言中默认变量为0,例如int a;则编译器默认a=0;
2、extern的用法,详见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的功能还可以,比较好,以前都不知道用它。
2015年1月6日20: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陷阱和缺陷采访录)。