📄 c programing practice.htm
字号:
checksize 是在出错时返回 true 还是在不出错时返回 true;而 <BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>if (validsize(x))</PRE></TD></TR></TBODY></TABLE><BR><BR>则使函数的意图很明确。
<BR>声明 <BR><BR>所有的外部数据声明前都应加上 extern 关键字。
<BR><BR>“指针”限定符“*”应紧邻变量名而不是类型。例如,应使用 <BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>char *s, *t, *u;</PRE></TD></TR></TBODY></TABLE><BR><BR>而不是
<BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>char* s, t, u;</PRE></TD></TR></TBODY></TABLE><BR><BR>后一条语句没有错,但可能不是我们期望的,因为没有将“t”和“u”声明为指针。
<BR>头文件
<BR><BR>头文件应按功能组织在一起,即,对单独子系统的声明应在单独的头文件中。此外,当代码从一个平台移植到另一个平台时有可能发生更改的声明应位于单独的头文件中。
<BR><BR>避免使用与库头文件名相同的专用头文件名。语句 #include "math.h"
如果在当前目录中找不到所期望文件的话,会包括标准库 math 头文件。如果这是您期望的结果,可以注释掉这行 include
语句。 <BR><BR>最后说明一点,对头文件使用绝对路径名不是一个好主意。C
编译器的“include-path”选项(在许多系统上为 -I — 大写的
i)是处理众多专用头文件库的首选方法;它允许在不改变源文件的情况下重新组织目录结构。 <BR>scanf
<BR><BR>在重要的应用程序中永远不要使用 scanf。它的错误检测不够完善。请看下面的示例: <BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>#include <stdio.h> <BR> <BR> int main(void) <BR> { <BR> int i; <BR> float f; <BR> <BR> printf("Enter an integer and a float: "); <BR> scanf("%d %f", &i, &f); <BR> <BR> printf("I read %d and %f\n", i, f); <BR> return 0; <BR> }</PRE></TD></TR></TBODY></TABLE><BR><BR>测试运行
<BR><BR>Enter an integer and a float: 182 52.38 <BR><BR>I read
182 and 52.380001 <BR><BR>另一个测试运行 <BR><BR>Enter an integer and
a float: 6713247896 4.4 <BR><BR>I read -1876686696 and
4.400000 <BR>++ 和 --
<BR><BR>当对语句中的变量使用递增或递减运算符时,该变量不应在语句中出现一次以上,因为求值的顺序取决于编译器。编写代码时不要对顺序作假设,也不要编写在某一机器上能够如期运作但没有明确定义的行为的代码:
<BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>int i = 0, a[5]; <BR> <BR> a[i] = i++; /* assign to a[0]? or a[1]? */</PRE></TD></TR></TBODY></TABLE><BR>不要被表面现象迷惑
<BR><BR>请看以下示例: <BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>while (c == '\t' || c = ' ' || c == '\n') <BR> c = getc(f);</PRE></TD></TR></TBODY></TABLE><BR><BR>乍一看,
while 子句中的语句似乎是有效的 C 代码。但是,使用赋值运算符而不是比较运算符却产生了语义上不正确的代码。=
的优先级在所有运算符中是最低的,因此将以下列方式解释该语句(为清晰起见添加了括号): <BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>while ((c == '\t' || c) = (' ' || c == '\n')) <BR> c = getc(f);</PRE></TD></TR></TBODY></TABLE><BR><BR>赋值运算符左边的子句是:
<BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>(c == '\t' || c)</PRE></TD></TR></TBODY></TABLE><BR><BR>它不会产生左值。如果
c 包含制表符,则结果是“true”,并且不会执行进一步的求值,而“true”不能位于赋值表达式的左边。
<BR>意图要明确。
<BR><BR>当您编写的代码可以解释成另一种意图时,使用括号或用其它方法以确保您的意图清楚。如果您以后必须处理该程序的话,这有助于您理解您当初的意图。如果其他人要维护该代码,这可以让维护任务变得更简单。
<BR><BR>用能预见可能出现错误的方式编码,有时是可行的。例如,可以将常量放在比较等式的左边。即,不编写:
<BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>while (c == '\t' || c == ' ' || c == '\n') <BR> c = getc(f);</PRE></TD></TR></TBODY></TABLE><BR><BR>而是编写:
<BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>while ('\t' == c || ' ' == c || '\n' == c) <BR> c = getc(f);</PRE></TD></TR></TBODY></TABLE><BR><BR>用以下方法却会得到编译器诊断:
<BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>while ('\t' = c || ' ' == c || '\n' == c) <BR> c = getc(f);</PRE></TD></TR></TBODY></TABLE><BR><BR>这种风格让编译器发现问题;上面的语句是无效的,因为它试图对“\t”赋值。
<BR>意想不到的麻烦。 <BR><BR>各种 C
实现通常在某些方面各有不同。坚持使用语言中可能对所有实现都是公共的部分会有帮助。通过这样做,您更容易将程序移植到新的机器或编译器,并且不大会遇到编译器特殊性所带来的问题。例如,考虑字符串:
<BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>/*/*/2*/**/1</PRE></TD></TR></TBODY></TABLE><BR><BR>这里利用了“最大适合(maximal
munch)”规则。如果可以嵌套注释,则可将该字符串解释为: <BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>/* /* /2 */ * */ 1</PRE></TD></TR></TBODY></TABLE><BR><BR>两个
/* 符号与两个 */ 符号匹配,因此该字符串的值为 1。如果注释不嵌套,那么在有些系统上,注释中的 /*
就被忽略。在另一些系统上会针对 /* 发出警告。无论哪种情况,该表达式可解释为: <BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>/* / */ 2 * /* */ 1</PRE></TD></TR></TBODY></TABLE><BR><BR>2
* 1 求值得 2。 <BR>清空输出缓冲区
<BR><BR>当应用程序异常终止时,其输出的尾部常常会丢失。应用程序可能没有机会完全清空它的输出缓冲区。输出的某一部分可能仍在内存中,并且永远不会被写出。在有些系统上,这一输出可能有几页长。
<BR><BR>以这种方式丢失输出会使人误解,因为它给人的印象是程序在它实际失败很久之前就失败了。解决这一问题的方法是强制将输出从缓冲区清除,特别是在调试期间。确切的方法随系统的不同而有所不同,不过也有常用的方法,如下所示:
<BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>setbuf(stdout, (char *) 0);</PRE></TD></TR></TBODY></TABLE><BR><BR>必须在将任何内容写到标准输出之前执行该语句。理想情况下,这将是主程序中的第一条语句。
<BR>getchar() — 宏还是函数 <BR><BR>以下程序将其输入复制到其输出: <BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>#include <stdio.h> <BR> <BR> int main(void) <BR> { <BR> register int a; <BR> <BR> while ((a = getchar()) != EOF) <BR> putchar(a); <BR> }</PRE></TD></TR></TBODY></TABLE><BR>从该程序除去
#include 语句将使该程序无法编译,因为 EOF 将是未定义的。 <BR><BR>我们可以用以下方法重新编写该程序:
<BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>#define EOF -1 <BR> <BR> int main(void) <BR> { <BR> register int a; <BR> <BR> while ((a = getchar()) != EOF) <BR> putchar(a); <BR> }</PRE></TD></TR></TBODY></TABLE><BR><BR>这在许多系统上都可行,但在有些系统上运行要慢很多。
<BR><BR>因为函数调用通常要花较长时间,所以常常把 getchar 实现为宏。这个宏定义在 stdio.h
中,所以当除去 #include <STDIO.H>时,编译器就不知道 getchar 是什么。在有些系统上,假设
getchar 是返回一个 int 的函数。 <BR><BR>实际上,许多 C 实现在其库中都有 getchar
函数,部分原因是为了防止这样的失误。于是,在 #include <STDIO.H>遗漏的情况下,编译器使用 getchar
的函数版本。函数调用的开销使程序变慢。 putchar 有同样的问题。 <BR>空指针
<BR><BR>空指针不指向任何对象。因此,为了赋值和比较以外的目的而使用空指针都是非法的。 <BR><BR>不要重新定义
NULL 符号。NULL
符号应始终是常量值零。任何给定类型的空指针总是等于常量零,而与值为零的变量或与某一非零常量的比较,其行为由实现定义。
<BR><BR>反引用 null 指针可能会导致奇怪的事情发生。 <BR>a+++++b 表示什么?
<BR><BR>解析它的唯一有意义的方法是: <BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>a ++ + ++ b</PRE></TD></TR></TBODY></TABLE><BR><BR>然而,“最大适合”规则要求将它分解为:
<BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>a ++ ++ + b</PRE></TD></TR></TBODY></TABLE><BR><BR>这在语法上是无效的:它等于:
<BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>((a++)++) + b</PRE></TD></TR></TBODY></TABLE><BR><BR>但
a++ 的结果不是 左值 ,因此作为 ++
的操作数是不可接受的。于是,解析词法不明确性的规则使得以语法上有意义的方式解析该示例变得不可能。当然,谨慎的办法实际上是在不能完全确定它们的意义的情况下,避免这样的构造。当然,添加空格有助于编译器理解语句的意图,但(从代码维护的角度看)将这一构造分割成多行更可取:
<BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>++b; <BR> (a++) + b;</PRE></TD></TR></TBODY></TABLE><BR>小心处理函数
<BR><BR>函数是 C 中最常用的结构概念。它们应用于实现“自顶向下的”问题解决方法 —
即,将问题分解成越来越小的子问题,直到每个子问题都能够用代码表示。这对程序的模块化和文档记录有帮助。此外,由许多小函数组成的程序更易于调试。
<BR><BR>如果有一些函数参数还不是期望的类型,则将它们强制转换为期望的类型,即使您确信没有必要也应该这样做,因为(如果不转换的话)它们可能在您最意料不到的时候给您带来麻烦。换句话说,编译器通常将函数参数的类型提升和转换成期望的数据类型以符合函数参数的声明。但是,在代码中以手工方式这样做可以清楚地说明程序员的意图,并且在将代码移植到其它平台时能确保有正确的结果。
<BR><BR>如果头文件未能声明库函数的返回类型,那就自己声明它们。用 #ifdef/#endif
语句将您的声明括起来,以备代码被移植到另一个平台。 <BR><BR>函数原型应当用来使代码更健壮,使它运行得更快。
<BR>悬空 else <BR><BR>除非知道自己在做什么,否则应避免“悬空 else”问题: <BR><BR>
<TABLE cellSpacing=0 cellPadding=5 width=450 bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE>if (a == 1) <BR> if (b == 2) <BR> printf("***\n"); <BR> else <BR> printf("###\n");</PRE></TD></TR></TBODY></TABLE><BR><BR>规则是
else 附加至最近的 if。当有疑虑时,或有不明确的可能时,添加花括号以说明代码的块结构。 <BR>数组界限
<BR><BR>检查所有数组的数组界限,包括字符串,因为在您现在输入“fubar”的地方,有人可能会输入“floccinaucinihilipilification”。健壮的软件产品不应使用
gets()。 <BR><BR>C
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -