C语言陷阱---丢三落四

引言

对于 C 语言初学者而言,丢三落四的毛病比较严重,而某些错误,编译器也不会给出任何错误或警告的提示,以致于当程序编译通过而能运行时,浑然不知自己已经掉入了 C 的陷阱中。

注:本文如无特别说明,示范代码均在 Win7 64位英文系统, Dev-C++  4.9 环境下编译执行。


接收标准输入时普通变量缺少取地址&符号

1. 示范代码

#include<stdio.h>
int main()
{
	int a;
	printf("Input an integer: ");
	scanf("%d", a);              /* 缺少取址符号 & */ 
	printf("a = %d\r\n", a);
	
	return 0;
}


2. 错误分析

示范代码能够编译通过且能“正常”运行,但是当输入一个整数时,会出现运行时错误而导致程序崩溃。

问题的关键在于 scanf 函数接收标准输入时,接收输入值的参数必须为某个地址。由于代码声明整型变量 a 且没有初始化,a 的值是随机的。当用户输入值后,会将值保存到变量 a 的值所在地址中,该值由于随机性,往往是非法的地址,所以导致程序发送运行时错误而崩溃。


使用switch语句时case分支缺少break语句

1. 示范代码

#include<stdio.h>
int main()
{
	char a;
	printf("Please input a score rank from list (A, B, C, D): ");
	scanf("%c", &a);
	switch(a) 
	{
		case 'A':
			printf("Excellent!\n");
			break;
		case 'B':
			printf("Well Done!\n");     /* 缺少 break 语句?*/ 
		case 'C':
			printf("Good!\n");
			break;
		case 'D':
			printf("You should work hard!\n");
			break;
		default:
			printf("Unknown rank: %c", a);
	}
}


2. 错误分析

以上示范代码,当输入除 ”B“以外的字符时,都能按照原意运行。但是当输入”B“时,输出结果变成了如下所示:

Please input a score rank from list (A, B, C, D): B
Well Done!
Good!
该问题就在于在判断 ‘B’ 的 case分支中,缺少了 break 语句,导致执行完该分支后,继续往下执行,直到在 ‘C’的case分支中遇到 break 语句才终止。该示范代码比较简单,估计不太容易犯这种低级错误。但是当case分支很多,而且每个分支的代码比较复杂时(分支太复杂,本身就是个坏的编程风格),就很容易丢掉 break 语句而导致执行逻辑错误了。

申请内存时忽略了字符串结束符'\0'

1. 示范代码

#include<string.h>
#include<stdlib.h>
#include<stdio.h>
int main()
{
	char *src_str = "Hello!";
	int len = strlen(src_str);
	char *dst_str = (char*)malloc(len);
	printf("%d\n", len);
	if(dst_str)
	{
		//strcpy(dst_str, src_str);
		memcpy(dst_str, src_str, len);
		printf("%s\n", dst_str);
		free(dst_str);
	}
	return 0;
}


2. 错误分析

示范代码输出结果如下(多次运行程序,! 后面的字符输出结果都不太一样):

6
Hello!?
出错原因在于计算字符串长度时,忽略了字符串后面的 "\0"符号也占一个字符,上述代码,必须修改为 int len = strlen(src_str) + 1 才正确。

循环条件中边界错误导致越界

1. 示范代码

#include<stdio.h>
int main()
{
	int a[5], i;
	
	/* Initiate the array */
	for(i = 0; i <= 5; i++)
	{
		a[i] = i;
	}
	/* Print the array */
	for(i = 0; i <= 5; i++)
	{
		printf("%d\t", a[i]);
	}
	return 0;
}

2. 错误分析

示范代码输出结果如下:
0       1       2       3       4       5

貌似越界了程序也没崩溃,一切正常!但是,这正是大项目中埋雷的“绝佳实践”,神不知鬼不觉, Bug 已在你手中诞生!等到某天程序突然崩溃或运行异常了,也不知道错在哪儿!
本示范代码由于错误地将循环条件中的 " i < 5 " 写成了 " i <= 5 ",导致访问数组越界,将把该数组低地址的一个整数空间给踩了。为了验证我所说的,将上述代码修改一下,如下所示:
#include<stdio.h>
int main()
{
	int a[5];
	int b = 100;
	int i;
	
	printf("b = %d\n", b);
	/* Initiate the array */
	for(i = 0; i <= 5; i++)
	{
		a[i] = i;
	}
	/* Print the array */
	for(i = 0; i <= 5; i++)
	{
		printf("%d\t", a[i]);
	}
	printf("\nb = %d", b);
	return 0;
}
输出结果如下:
b = 100
0       1       2       3       4       5
b = 5
除了初始化变量 b 以外,我们只是对 b 进行打印操作,“理论上”应该不会改变它的值才对啊?!试想一下,如果是在某个复杂的算法函数中,在你毫无知觉的情况下,所使用的变量 b 已经被莫名其妙修改,你作何感想?现在能够理解我前面的话,不是危言耸听了吧?

总结

编程是一件严肃细致的工作,程序员,尤其是 C 语言程序员,必须时刻保持严谨细致的作风,一丝不苟,才能减少错误的发生,避免一不小心掉入 C 语言的陷阱中,然后在茫茫代码中寻找那由于自己粗心大意而造成的低级错误。

你可能感兴趣的:(switch,C语言,陷阱)