C 语言入门/C 语言常见编程问题
C 语言中存在一些常见的编程陷阱,即使是经验丰富的程序员也会陷入其中
1: 将“=”(赋值运算符)与“==”(等号运算符)混淆。例如
if ( x = 1 ){ /* wrong! */ } ... if ( x == 1 ){ /* good. */ }
以及
for ( x == 1; ... /* wrong! */ ... for ( x = 1; ... /* good. */
2: 混淆表达式中运算符的优先级。如有疑问,请使用括号来强制优先级。使用一些额外的括号绝不会有害。
3: 混淆结构成员运算符。如果“struct_val”是一个结构体,而“struct_ptr”是指向该结构体的指针,那么
struct_val->myname
是错误的,
struct_ptr.myname
也是错误的。
4: 对“printf()”和“scanf()”使用不正确的格式代码。例如,使用“%f”打印“int”会导致奇怪的输出。
int data[20]; ... for ( x = 1; x <= 20; ++x ) { printf( "%d\n", data[x] ); }
5: 记住数组的实际基索引为 0,最后一个索引比声明的大小小 1。例如
当“x”为 20 时,将给出无效的结果。由于 C 语言不执行边界检查,因此这可能很难发现。
6: 混淆多维数组的语法。如果
data[10][10]
是一个二维数组,那么
data[2][7]
将选择该数组中的一个元素。但是
data[ 2, 7 ]
将给出无效的结果,但不会被 C 语言标记为错误。
7: 混淆字符串和字符常量。以下是字符串
"Y"
与字符常量
'Y'
相反。这可能会在将字符串与字符常量进行比较时造成麻烦。
8: 忘记字符串以空字符 ('\0') 结尾。这意味着字符串将始终比它存储的文本大一个字符。如果字符串是逐字符创建的,并且程序没有在末尾添加空字符,也会导致问题。
9: 忘记为字符串分配足够的内存——或者,如果声明了指针,则完全没有为其分配任何内存。
10: 忘记检查库函数的返回值。大多数库函数都返回一个错误代码;虽然可能不希望检查每次“printf()”调用,但在关键操作中要小心不要忽略错误代码。
当然,忘记存储函数返回的值(当这是获取该值的唯一方法时)是一个愚蠢的行为,但人们偶尔会做这样的事。
11: 拥有重复的库函数名称。编译器不会始终捕获此类错误。
12: 忘记为库函数指定头文件。
13: 将变量作为参数传递给函数,而应该传递指针,反之亦然。如果函数通过参数返回值,则意味着它必须指定为指针
myfunc( &myvar );
以下将不起作用
myfunc( myvar );
记住,即使函数不返回值,也可能需要指针作为参数,不过作为规则,这不是一个好的编程实践。
14: 在使用嵌套的“if”和“else”语句时感到困惑。避免此问题的最佳方法是始终使用括号。避免复杂的“if”结构也是一个好主意;如果有选择,请使用“switch”。即使对于简单的“if”语句,使用“switch”也很有用,因为它更容易扩展结构,如果需要的话。
15: 忘记分号——尽管编译器通常会捕获这一点——或者在不应该出现的地方添加分号——它通常不会这样做。例如
for( x = 1; x < 10; ++x ); { printf( "%d\n", x ) }
永远不会打印任何内容。
16: 忘记“switch”结构中的“break”语句。如前所述,这样做只会导致执行从“switch”的一个子句流向另一个子句。
17: 粗心地混合和滥用有符号和无符号值,或不同数据类型。这可能会导致一些非常微妙的错误。要注意的一个特殊问题是将单个字符变量声明为“unsigned char”。许多 I/O 函数将期望“unsigned int”类型的返回值,并且无法正确地标记 EOF。建议将函数参数强制转换为适当的类型,即使看起来类型转换会自行处理也是如此。
18: 变量名称混淆。为了确保代码的可移植性,建议此类标识符的前六个字符是唯一的。
19: 通常,过于复杂和巧妙的代码。程序是令人讨厌的东西,即使它有效,也必须修改甚至移植到不同的语言。保持干净的结构,做简单直接的事情,除非它带来了不可接受的代价。