本文汇总了一些容易犯错、不易察觉的C编程错误。有时候,你会遇到莫名其妙的错误而不知所措,本文中的一些例子可以给你提供经验,防患于未然。
#include <stdio.h> #define f(a,b) a##b #define g(a) #a #define h(a) g(a) int main() { printf("%s\n", h(f(1,2))); printf("%s\n", g(f(1,2))); return 0; }
说明:输出结果为
12
f(1,2)
宏的解开不像函数那样执行;函数是由里及外展开,而宏则不是。
#include <stdio.h> #define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0])) int array[] = {23,34,12,17,204,99,16}; int main() { int d; for(d=-1;d <= (TOTAL_ELEMENTS-2);d++) printf("%d\n",array[d+1]); return 0; }
说明:无任何输出。sizeof的返回值是一个unsinged int,为此在比较int d 和TOTAL_ELEMENTS两个值都被转换成了unsigned int来进行比较,这样就导致-1被转换成一个非常大的值,以至于for循环不满足条件。
#include <stdio.h> void foobar1(void) { printf("In foobar1\n"); } void foobar2() { printf("In foobar2\n"); } int main() { char ch = 'a'; foobar1(); foobar2(); foobar2(33, ch); foobar1(33); return 0; }
说明:在foobar1(33);报错error: too many arguments to function ‘foobar1’。func(void)是没有参数,而func()等于参数类型未知,其调用者可以传入若干个参数,但在func()中,所有的参数将被忽略
#include <stdio.h> #include <unistd.h> int main() { while(1) { fprintf(stdout,"hello-std-out"); fprintf(stderr,"hello-std-err"); sleep(1); } return 0; }
说明:stdout和stderr是不是同设备描述符。stdout是块设备,stderr则不是。对于块设备,只有当下面几种情况下才会被输入,1)遇到回车,2)缓冲区满,3)flush被调用。而stderr则会直接输出。
#include <stdio.h> int main() { int i=43; printf("%d\n",printf("%d",printf("%d",i))); return 0; }
说明:程序会输出4321,因为printf返回值是输出的字符个数。
#include <stdio.h> int main() { int a=1; switch(a) { int b=20; case 1: printf("b is %d\n",b); break; default: printf("b is %d\n",b); break; } return 0; }
说明:该程序在编译时,可能会出现一条warning: unreachable code at beginning of switch statement。我们以为进入switch后,变量b会被初始化,其实并不然,因为switch-case语句会把变量b的初始化直接就跳过了。所以,程序会输出一个随机的内存值。
#include <stdio.h> int main() { char str[80]; printf("Enter the string:"); scanf("%s",str); printf("You entered:%s\n",str); return 0; }
说明:这个程序的潜在问题是,如果用户输入了超过80个长度的字符,那么就会有数组越界的问题了,你的程序很有可以及会crash了。
#include <stdio.h> int main() { int i; i = 10; printf("i : %d\n",i); printf("sizeof(i++) is: %d\n",sizeof(i++)); printf("i : %d\n",i); return 0; }
说明:sizeof不是一个函数,是一个操作符,其求i++的类型的size,这是一件可以在程序运行前(编译时)完全的事情,所以,sizeof(i++)直接就被4给取代了,在运行时也就不会有了i++这个表达式。所以输出是
i : 10
sizeof(i++) is: 4
i : 10
#include <stdio.h> #include <stdlib.h> #define SIZEOF(arr) (sizeof(arr)/sizeof(arr[0])) #define PrintInt(expr) printf("%s:%d\n",#expr,(expr)) int main() { /* The powers of 10 */ int pot[] = { 0001, 0010, 0100, 1000 }; int i; for(i=0;i<SIZEOF(pot);i++) PrintInt(pot[i]); return 0; }
说明:在C/C++中,以0开头的数字都是八进制的。所以,输出是
pot[i]:1
pot[i]:8
pot[i]:64
pot[i]:1000
#include <stdio.h> int main() { char dummy[80]; printf("Enter a string:\n"); scanf("%[^r]",dummy); printf("%s\n",dummy); return 0; }
说明:输入输入 Hello, World;那么输出为Hello, Wo;scanf中的”%[^r]“的意思是遇到字符r就结束了。
//file1.c int arr[80]; //file2.c extern int *arr; int main() { arr[1] = 100; printf("%d\n", arr[1]); return 0; }
说明:该程序可以编译通过,但运行时会出错。原因是,在另一个文件中用 extern int *arr来外部声明一个数组并不能得到实际的期望值,他们的类型并不匹配。所以导致指针实际并没有指向那个数组。注意:一个指向数组的指针,并不等于一个数组。修改:extern int arr[]。
#include <stdio.h> int main() { float a = 12.5; printf("%d\n", a); printf("%d\n", (int)a); printf("%d\n", *(int *)&a); return 0; }
说明:
该项程序输出如下:
594771784(经多次运行,该数为随机数)
12
1095237632
原因是:浮点数是4个字节,12.5f 转成二进制是:01000001010010000000000000000000,十六进制是:0×41480000,十进制是:1095237632。
float: 1位符号位(s)、8位指数(e),23位尾数(m,共32位)
double: 1位符号位(s)、11位指数(e),52位尾数(m,共64位)
然后,我们还需要了解一下printf由于类型不匹配,所以,会把float直接转成double,注意,12.5的float和double的内存二进制完全不一样。
我的电脑是小端序字节。
double型表示12.5f的十六进制为:
而我们的%d要求是一个4字节的int,对于double的内存布局,我们取用前四个字节。
这个示例向我们说明printf并不是类型安全的,这就是为什么C++要引如cout的原因了。