C语言对现代编程语言有着巨大的影响,许多现代编程语言都借鉴了大量C语言的特性。以下几种非常具有代表性:
本章介绍了C语言的一些基本概念,包括预处理指令,函数,变量和语句。
/*
显示双关语“To C, or not to C: that is the question.”
*/
//pun.c
#include
int main(void){
printf("To C, or not to C: that is the question.\n");
return 0;
}
接下来对这段代码做简要介绍:
第一行#include 是必不可少的,它包含了C语言标准输入/输出库的相关信息,程序的可执行代码都在main函数中,这个函数代表“主”程序。main函数中的第一行代码是用来显示期望信息的。printf函数来自标准输入/输出库,可以产生完美的格式化输出。代码\n告诉printf函数执行完消息显示后要进行换行操作。第二行代码return 0;表明程序终止时会向操作系统返回值0。
为了把程序转换为机器可以执行的形式,通常包括下面3个步骤:
预处理器(preprocessor)。预处理器执行以#开头的命令(指令)。编译器(compiler)了。编译器会把程序翻译成机器指令(目标代码),此时的程序还是不可以运行的。链接器(linker)把由编译器产生的目标代码和所需的其他附加代码整合在一起,生成完全可执行的程序。这些附加代码包括程序中用到的库函数(如printf函数)。GCC,作为最流行的C编译器之一,它随Linux发行,但也有面向其他很多平台的版本。这种编译器的使用与传统的UNIX cc编译器类似。编译程序pun.c可以使用以下命令:
gcc -o pun pun.c
集成开发环境是一个软件包,我们可以在其中编辑、编译、链接、执行甚至调试程序。
简单C程序一般具有如下形式:
指令
int main(void){
语句
}
即使是最简单的C程序也依赖3个关键的语言特性:指令(在编译前修改程序的编辑命令)、函数(被命名的可执行代码块,如main函数)和语句(程序运行时执行的命令)。
#include 这条指令说明,在编译前把中的信息“包含”到程序中。C语言拥有大量类似于的头(header),每个头都包含一些标准库的内容。C语言不同于其他的编程语言,它没有内置的“读”和“写”命令。输入/输出功能由标准库中的函数实现。#开始的。这个字符可以把C程序中的指令和其他代码区分开来。指令默认只占一行,每条指令的结尾没有分号或其他特殊标记。事实上,C程序就是函数的集合,函数分为两大类,一类是程序员编写的函数,另一类则是作为C语言实现的一部分提供的函数。后者称为库函数,因为他们属于一个由编译器提供的函数“库”。
return语句来指定所“返回”的值。main函数必须有的。return 0;有两个作用,一是使main函数终止,二是指出main函数的返回值是0,表示程序正常终止。语句是程序运行时执行的命令。
pun.c就用了返回语句和函数调用语句。printf函数显示字面串时,最外层的双引号不会出现。\n表示换行。每一个程序都应该包含识别信息,即程序名、编写日期、作者、程序的用途以及其他相关信息。
/*标记注释的开始,*/标记注释的结束。//开始。//有两个主要优点:首先,因为注释会在行末自动终止,所以不会出现未终止的注释意外吞噬部分程序的情况;其次,因为每行注释前面都必须有//,所以多行注释看上去更加醒目。大多数程序在产生输出之前往往需要执行一系列的计算,因此需要在程序执行过程中有一种临时存储数据的方法,C语言中的这类存储单元被称为变量。
float型变量通常比int型变量慢。float型变量所存储的数值往往只是实际数值的一个近似值。比如在一个float型变量中存储0.1,以后可能会发现变量的值为0.099 999 999 999 999 87,这是舍入造成的误差。在使用变量之前必须对其进行声明(为编译器所做的描述)。为了声明变量,首先要指定变量的类型,然后说明变量的名字。
int height;说明height是一个int型变量,这意味着变量height可以存储一个整数值。float profit,loss;,注意每一条完整的声明都要以分号结尾。main函数包含声明时,必须把声明放置在语句之前。int main(void){
声明
语句
}
C99标准中,声明可以不在语句之前。例如,main函数中可以先有一个声明,后面跟一条语句,然后再跟一个声明。变量通过赋值(assignment)的方式获得值。
height = 8;
length = 12;
width = 10;
8、12和10分别赋值给变量height、length和width,其中8、12和10称为常量(constant)。float型变量时,最好在该常量后面加一个字母f(代表float)。比如profit = 21.34f,不加f可能触发编译器的警告。printf("height: %d\n", height);
%d用来指明在显示过程中变量height的值的显示位置。%d仅用于int型变量。%f则显示float型变量。默认情况下,%f会显示出小数点后6位数字。如果要强制%f显示小数点后p位数字,可以把p放置在%和f之间。比如printf("Profit: $%.2f\n", profit)。
C语言没有限制调用一次printf可以显示的变量数量。
当程序开始执行时,某些变量会被自动设置为
0(static类型),而大多数变量则不会(auto类型)。没有默认值并且尚未在程序中被赋值的变量是未初始化的。
int height, length, width = 10这个例子中,只有变量width拥有初始化器10,而前面两个变量没有(两个变量没有初始化)。printf("%d\n", height * length * width);
printf显示表达式的值的能力说明了C语言的一个通用原则:在任何需要数值的地方,都可以使用具有相同类型的表达式。
scanf和printf中的字母f都表示“格式化”的意思。两个函数都需要使用格式串(format string)来指定输入数据或输出数据的形式。
scanf函数需要知道将获得的输入数据的格式,而printf函数需要知道输出数据的显示格式。
int型值,可以使用下面的scanf函数调用:scanf("%d", &i);
/*
字符串“%d”说明scanf读入的是一个整数,而i是一个int型变量,用来存储scanf读入的输入。
读入一个float型值时,需要一个形式略有不同的scanf调用。
*/
scanf("%f", &x);
#define INCHES_PER_POUND 166,这里的#define预处理指令,此行的结尾也没有分号。define RECIPROCAL_OF_PI (1.0f / 3.14159f),当宏包括运算符时,建议使用括号把表达式括起来。注意,宏的名字只用了大写字母。这是大多数C程序员遵循的规范,并不是C语言本身的要求。
在编写程序时,需要对变量、函数、宏和其他实体进行命名。这些名字称为标识符。
times10 get_next_char _done这三个是合法的,而10times get-next-char这两个是不合法的。symbolTable currentPage nameAndAddress。关键字,对C编译器而言都有着特殊的意义,因此关键字不能作为标识符来使用。
volume = height * length * width;。main函数中,应该对他们进行缩进。问1:GCC是什么的简称?
答:GCC最初是GNU C Compiler的简称,现在指GNU Compiler Collection,这是因为最新版本的GCC能够编译用Ada、C、C++、Fortran、Java 和Objective-C等多种语言编写的程序。
问2:明白了,但GNU又是什么意思呢?
答:GNU指的是“GNU’s Not UNIX!”(发音为guh-NEW),它是自由软件基金会(Free Software Foundation)的一个项目。自由软件基金会是由Richard M. Stallman发起的一个组织,旨在抗议对UNIX 软件授权的各种限制。从它的网站可以看出,自由软件基金会认为用户应该可以自由地“运行、复制、发布、研究、改变和改进”软件。GNU 项目从头开始重写了许多传统的UNIX 软件,并使公众能够免费地获得。
GCC 和其他GNU软件对于Linux操作系统来说是至关重要的。Linux本身只是操作系统的“内核”
(处理程序调度和基本输入/输出服务的部分),为了获得具备完整功能的操作系统,GNU 软件是必要的。
问3:GCC发现程序中错误的能力如何?
答:GCC有多个命令行选项来控制程序检查的彻底程度。使用这些选项可以帮助我们有效地找出程序中潜在的故障区域。下面是一些比较常用的选项。
–Wall :使编译器在检测到可能的错误时生成警告消息。(–W后面可以加上具体的警告代码,-Wall表示“所有的–W选项”。)为了获得最好的效果,该选项应与–O选项结合使用。
–W :除了–Wall生成的警告消息外,还需要针对具体情况的额外警告消息。
–pedantic :根据C标准的要求生成警告消息。这样可以避免在程序中使用非标准特性。
–ansi :禁用GCC的非标准C特性,并启用一些不太常用的标准特性。
–std=c89或–std=c99指明使用哪个版本的C编译器来检查程序。这些选项常常可以结合使用:
% gcc –O –Wall –W –pedantic –std=c99 –o pun pun.c
问4:为什么C语言如此简明扼要?如果在C语言中用
begin和end代替{和},用integer代替int,等等,程序似乎更加易读。
答:据说,C程序的简洁性是由开发该语言时贝尔实验室的环境造成的。第一个C语言编译器是运行在DEC PDP-11计算机(一种早期的小型计算机)上的,而程序员用电传打字机(实际上是一种与计算机相连的打字机)输入程序和打印列表。因为电传打字机的速度非常慢(每秒钟只能打出10 个字符),所以在程序中尽量减少字符数量显然是十分有利的。
问5:在某些C语言书中,
main函数的结尾使用的是exit(0)而不是return 0,二者是否一样呢?
答:当出现在main函数中时,这两种语句是完全等价的:二者都终止程序执行,并且向操作系统返回0值。使用哪种语句完全依据个人喜好而定。
问6:如果
main函数末尾没有return语句会产生什么后果?
答:return语句不是必需的;如果没有return语句,程序一样会终止。在C89中,返回给操作系统的值是未定义的。在C99中,如果main函数声明中的返回类型是int(如我们的例子所示),程序会向操作系统返回0;否则程序会返回一个不确定的值。
问7:编译器是完全移除注释还是用空格替换注释呢?
答:一些早期的编译器会删除每条注释中的所有字符,使得语句
a/**/b = 0;
可能被编译器理解成
ab = 0;
然而,依据C标准,编译器必须用一个空格字符替换每条注释语句,因此上面提到的技巧并不可行。我们实际上会得到下面的语句:
a b = 0;
问8:如何发现程序中未终止的注释?
答:如果运气好的话,程序将无法通过编译,因为这样的注释会导致程序非法。如果程序可以通过编译,也有几种方法可以用。通过用调试器逐行地执行程序,就会发现是否有些行被跳过了。某些集成开发环境会使用特别的颜色把注释和其他代码区分开来。如果你使用的是这样的开发环境,就很容易发现未终止的注释,因为误把程序文本包含到注释中会导致颜色不同。此外,诸如lint( 1.2 节)之类的程序也可以提供帮助。
问9:在一个注释中嵌套另一个注释是否合法?
答:传统风格的注释(/*…*/)不允许嵌套。例如,下面的代码就是不合法的:
/*
/*** WRONG ***/
*/
第2行的符号*/会和第1行的/*相匹配,所以编译器会把第3行的*/标记为一个错误。
C语言禁止注释嵌套有些时候也是个问题。假设我们编写了一个很长的程序,其中包含了许多短小的注释。为了临时屏蔽程序的某些部分(比如在测试过程中),我们首先会想到用/*和*/“注释掉”相应的程序行。但是,如果这些代码行中包含有传统风格的注释,这种方法就行不通了。不过,C99注释(以//开始的注释)可以嵌套在传统风格的注释中,这是这类注释的另一个优势。
后面我们将看到,可以用一种更好的方法来屏蔽部分程序( 14.4 节)。
问10:float 类型的名字由何而来?
答:float是floating-point的缩写形式,它是一种存储数的方法,而这些数中的小数点是“浮动的”。float类型的值通常分成两部分存储:小数部分(或者称为尾数部分)和指数部分。例如,12.0这个数可以以1.5×2^3的形式存储,其中1.5是小数部分,而3是指数部分。有些编程语言把这种类型称为real类型而不是float类型。
问11:为什么浮点常量需要以字母f结尾?
答:完整的解释见第7章。这里只简单回答一下:包含小数点但不以f结尾的常量是double(double precision的缩写)型的。double型的值比float型的值存储得更精确,并且可以比float型的值大,因此在给float型变量赋值时需要加上字母f。如果不加f,编译器可能会生成一条警告消息,告诉你存储到float型变量中的数可能超出了该变量的取值范围。
问12:对标识符的长度真的没有限制吗?
答:是,又不是。C89标准声称标识符可以是任意长,但只要求编译器记住前31个字符( C99中是63个字符)。因此,如果两个名字的前31个字符都相同,编译器可能会无法区分它们。
更复杂的情况是,C标准对于具有外部链接(18.2节)的标识符有特殊的规定,而大多数函数名属于这类标识符。因为链接器必须能识别这些名字,而一些早期的链接器又只能处理短名字,所以在C89中标识符只有前6个字符才是有效的。此外,C89还不区分字母的大小写。因此ABCDEFG和abcdefg可能会被作为相同的名字处理。(C99中,前31个字符有效,且字母区分大小写。)
大多数编译器和链接器比标准所要求的更宽松,因此实际使用中这些规则都不是问题。不要担心标识符太长,还是注意不要把它们定义得太短吧。
问13:缩进时应该使用多少空格?
答:这是个难以回答的问题。如果预留的空间过少,会不易察觉到缩进;如果预留的空间太多,则可能会导致行宽超出屏幕(或页面)的宽度。许多C程序员采用8个空格(即一个制表键)来缩进嵌套语句,这可能太多了。研究表明,缩进3个空格是最合适的,但许多程序员不太习惯于非2的幂次。我习惯于缩进3或4个空格,但是考虑到页面的需要,本书采用了2 个空格的缩进方式。
本文章为之前两次笔记的补充,欢迎各位大佬阅读学习,有不对的地方还请指正,不胜感激。后续章节笔记会持续更新,敬请期待!(书名:《C语言程序设计:现代方法(第2版·修订版)》)