C语言入门小册子
在 C 语言中,有许多常见的编程陷阱,即使是经验丰富的程序员也容易陷入其中:
-
混淆 "="(赋值运算符)与 "=="(等于运算符)
例如:if (x = 1) { /* 错误! */ }
及
for (x == 1; ... /* 错误! */ )
正确的应该是:
if (x == 1) { /* 正确。 */ } for (x = 1; ... /* 正确。 */ )
-
混淆表达式中的运算符优先级
在不确定时,使用括号来强制运算符的优先级。使用一些额外的括号从来不会有坏处。 -
混淆结构体成员操作符
如果struct_val
是结构体,struct_ptr
是指向结构体的指针,那么:struct_val->myname /* 错误 */ struct_ptr.myname /* 错误 */
-
在
printf()
和scanf()
中使用错误的格式化代码
例如,使用%f
打印一个int
类型的值,可能导致输出异常。 -
记住数组的实际基索引是 0,最终索引比声明的大小小 1
例如:int data[20]; ... for (x = 1; x <= 20; ++x) { printf("%d\n", data[x]); }
当
x
为 20 时会给出无效的结果。由于 C 语言不进行边界检查,这个问题可能很难捕获。 -
混淆多维数组的语法
如果data[10][10]
是一个二维数组,那么:data[2][7] /* 正确,选取数组中的元素 */
然而:
data[2, 7] /* 错误,未标记为错误 */
-
混淆字符串和字符常量
以下是一个字符串:"Y"
与字符常量:
'Y'
在进行字符串与字符常量比较时,容易产生问题。
-
忘记字符串以空字符(
\0
)结束
这意味着字符串总是比它存储的文本大一个字符。如果在逐字符创建字符串时,程序没有将空字符加到末尾,也会导致问题。 -
未分配足够的内存给字符串,或者如果使用了指针,完全没有为其分配内存。
-
忽视库函数的返回值
大多数库函数返回错误代码;虽然不需要检查每次调用printf()
,但在关键操作中不要忽视错误代码。
当然,忘记存储函数返回的值(当这是唯一获取该值的方式时)是非常低级的错误,但人们偶尔会犯这种错误。
-
有重复的库函数名称
编译器通常不会捕获这种错误。 -
忘记为库函数指定头文件。
-
在应该指定指针作为函数参数的地方指定了变量,反之亦然
如果函数通过参数返回一个值,必须指定该参数为指针:myfunc( &myvar );
以下是错误的:
myfunc( myvar );
记住,即使一个函数不返回值,它也可能需要一个指针作为参数,尽管作为规则,这不是一个好的编程实践。
-
使用嵌套的
if
和else
语句时混淆
避免这个问题的最佳方法是始终使用大括号。避免复杂的if
语句也很重要;如果有选择,使用switch
。即使是简单的if
语句,使用switch
也很有用,因为如果需要扩展结构时,它会更方便。 -
忘记分号(虽然编译器通常会捕获这个错误),或者在不应该有分号的地方加了一个分号(编译器通常不会捕获这个错误)
例如:for (x = 1; x < 10; ++x); /* 错误! */ { printf("%d\n", x); /* 不会打印任何东西 */ }
-
在
switch
语句中忘记break
语句
如前所述,忘记break
会导致执行从一个switch
子句流向下一个子句。 -
不小心混用签名和无符号值,或不同的数据类型
这可能导致非常微妙的错误。特别需要注意的一点是将单字符变量声明为unsigned char
。许多 I/O 函数期望的是unsigned int
类型的值,且无法正确标记 EOF。建议将函数参数强制转换为适当的类型,即使看似类型转换已经能够自动处理。 -
变量名混淆
为了确保代码的可移植性,建议确保标识符的前六个字符是唯一的。 -
过于复杂和聪明的代码
程序往往是棘手的,尽管它可能工作正常,但最终会被修改并迁移到不同的语言中。保持代码结构清晰,做简单直接的事情,除非这样做会带来无法接受的代价。