C 语言基本概念:问与答

目录汇总:C 语言入门教程:面向萌新小白的零基础入门教程

问:GCC 是什么的简称?

答:GCC 最初是 GNU C compiler 的简称。现在指 GNU Compiler Collection,这是因为最新版本的 GCC 能够编译用 Ada、C、C++、Fortran、Java 和 Objective-C 等多种语言编写的程序。

问:明白了,但 GNU 又是什么意思呢?

答:GNU 指的是“GNU’s Not UNIX!”(发音为 guh-NEW),它是自由软件基金会(Free Software Foundation)的一个项目。自由软件基金会是由 Richard M. Stallman 发起的一个组织,旨在抗议对 UNIX 软件授权的各种限制。从它的网站可以看出,自由软件基金会认为用户应该可以自由地“运行、复制、发布、研究、改变和改进”软件。GNU 项目从头开始重写了许多传统的 UNIX 软件,并使公众能够免费地获得。

GCC 和其他 GNU 软件对于 Linux 操作系统来说是至关重要的。Linux 本身只是操作系统的“内核”(处理程序调度和基本输入/输出服务的部分),为了获得具体完整功能的操作系统,GNU 软件是必要的。

网站 https://www.gnu.org 提供了更多有关 GNU 项目的信息。

问:GCC 有什么过人之处呢?

答:我们说 GCC 重要,不仅仅是因为它能免费获取、能编译很多语言。GCC 还可以在许多操作系统下运行,并为多种不同的 CPU 生成代码(支持所有广为使用的操作系统和 CPU)。GCC 是许多基于 UNIX 的操作系统(包括 Linux、BSD 和 Mac OS X)的主要编译器,并广泛用于商业软件开发。有关 GCC 的更多信息请参考 https://gcc.gnu.org

问:GCC 发现程序中错误的能力如何?

答:GCC 有多个命令行选项来控制程序检查的彻底程度。使用这些选项可以帮助我们有效地找出程序中潜在的故障区域。下面是一些比较常用的选项。

-Wall 使编译器在检测到可能的错误时生成警告消息。(-W后面可以加上具体的警告代码,-Wall表示“所有的-W选项”。)为了获得最好的效果,该选项应与-O选项结合使用。
-W 除了-Wall生成的警告消息外,还需要针对具体情况的额外警告消息。
-pedantic 根据C标准的要求生成警告消息。这样可以避免在程序中使用非标准特性。
-ansi 禁用GCC的非标准C特性,并启用一些不太常用的标准特性。
-std=c89-std=c99 指明使用哪个版本的C编译器来检查程序。

这些选项常常可以结合使用:

% gcc -O -Wall -W -pedantic -ansi -std=c99 -o pun pun.c

问:为什么 C 语言如此简明扼要?如果在 C 语言中用 begin 和 end 代替 {},用 integer 代替 int,如此等等,程序似乎更加易读。

答:据说,C 程序的简洁性是由开发该语言时贝尔实验室的环境造成的。第一个 C 语言编译器是运行在 DEC PDP-11 计算机(一种早期的小型计算机)上的,而程序员用电传打字机(实际上是一种与计算机相连的打字机)录入程序和打印列表。由于电传打字机的速度非常慢(每秒钟只能打出 10 个字符),所以在程序中尽量减少字符数量显然是十分有利的。

问:在某些 C 语言书中,main 函数的结尾使用的是 exit(0) 而不是 return 0,二者是否一样呢?

答:当出现在 main 函数中时,这两种语句是完全等价的:二者都终止程序执行,并且向操作系统返回 0 值。使用哪种语句完全依据个人喜好而定。

问:如果 main 函数末尾没有 return 语句会产生什么后果?

答:return 语句不是必需的;如果没有 return 语句,程序一样会终止。在 C89 中,返回给操作系统的值是未定义的。在 C99 中,如果 main 函数声明中的返回类型是 int(如我们的例子所示),程序会向操作系统返回 0;否则程序会返回一个不确定的值。

问:编译器是完全移除注释还是用空格替换掉注释呢?

答:一些早期的编译器会删除每条注释中的所有字符,使得语句

1
a/**/b = 0;

可能被编译器理解成

1
ab = 0;

然而,依据 C 标准,编译器必须用一个空格字符替换每条注释语句,因此上面提到的技巧并不可行。我们实际上会得到下面的语句:

1
a b = 0;

问:如何发现程序有没有未终止的注释?

答:如果运气好的话,程序将无法通过编译,因为这样的注释会导致程序非法。如果程序可以通过编译,也有几种方法可以用。通过用调试器逐行地执行程序,就会发现是否有些行被跳过了。某些集成开发环境会使用特别的颜色把注释和其他代码区分开来。如果你使用的是这样的开发环境,就会很容易发现未终止的注释,因为误把程序文本包含到注释中会导致颜色不同。此外,诸如 lint 之类的程序也可以提供帮助。

问:在一个注释中嵌套另一个注释是否合法?

答:传统风格的注释(/*...*/)不允许嵌套。例如,下面的代码就是不合法的:

1
2
3
/*
    /*** WRONG ***/
*/

第 2 行的符号 */ 会和第一行的 /* 相匹配,所以编译器将会把第 3 行的 */ 标记为一个错误。

C 语言禁止注释嵌套有些时候也是个问题。假设我们编写了一个很长的程序,其中包含了许多短小的注释。为了临时屏蔽程序的某些部分(比如在测试过程中),我们首先会想到用 /**/“注释掉”相应的程序行。但是,如果这些代码行中包含有传统风格的注释,这种方法就行不通了。不过,C99 注释(以 // 开始的注释)可以嵌套在传统风格的注释中,这是这类注释的另一个优势。

问:float 类型的名字由何而来?

答:float 是 floating-point 的缩写形式,它是一种存储数的方法,而这些数中的小数点是“浮动的”。float 类型的值通常分成两部分存储:小数部分(或者称为尾数部分)和指数部分。例如,12.0 这个数可以以 $1.5\times2^3$ 的形式存储,其中 1.5 是小数部分,而 3 是指数部分。有些编程语言把这种类型称为 real 类型而不是 float 类型。

问:为什么浮点常量需要以字母 f 结尾?

答:这里只简单回答一下:包含小数点但却不以f结尾的常量是 double(double precision 的缩写)型的。double 型的值比 float 型的值存储得更精确,并且可以存储比 float 型更大的值,因此在给 float 型变量赋值时需要加上字母 f。如果不加 f,编译器可能会生成一条警告消息,告诉你存储到 float 型变量中的数可能超出了该变量的取值范围。

*问:对标识符的长度真的没有限制吗?

答:是,又不是。C89 标准声称标识符可以任意长,但却只要求编译器记住前 31 个字符(C99 中是 63 个字符)。因此,如果两个名字的前 31 个字符都相同,编译器可能会无法区别它们。

更复杂的情况是,C 标准对于具有外部链接的标识符有特殊的规定,而大多数函数名都属于这类标识符。因为链接器必须能识别这些名字,而一些早期的链接器又只能处理短名字,所以在 C89 中只有前 6 个字符才是有效的。此外,还不区分字母的大小写。因此 ABCDEFG 和 abcdefg 可能会被作为相同的名字处理。(C99 中,前 31 个字符有效,且字母区分大小写。)

大多数编译器和链接器都比标准所要求的宽松,所以实际使用中这些规则都不是问题。不要担心标识符太长,还是注意不要把它们定义得太短吧。

问:缩进时应该使用多少空格?

答:这是个难以回答的问题。如果预留的空间过少,会不易察觉到缩进;如果预留的太多,则可能会导致行宽超出屏幕(或页面)的宽度。许多 C 程序员采用 8 个空格(即一个制表键)来缩进嵌套语句,这可能太多了。研究表明,缩进 3 个空格是最合适的,但许多程序员不太习惯于非 2 的幂次。我通常习惯于缩进 3 或 4 个空格,但是考虑到页面的需要,本教程采用了 2 个空格的缩进方式。

(完)

comments powered by Disqus