跳转到内容

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: 通常,过于复杂和巧妙的代码。程序是令人讨厌的东西,即使它有效,也必须修改甚至移植到不同的语言。保持干净的结构,做简单直接的事情,除非它带来了不可接受的代价。

华夏公益教科书