zoukankan      html  css  js  c++  java
  • 《C语言程序设计:现代方法(第2版)》第2章 C语言基本概念

    第2章 C语言基本概念

    某个人的常量可能是其他人的变量。

    本章介绍了C语言的一些基本概念,包括预处理命令、函数、变量和语句。即使是编写最简单的C程序,也会用到这些基本概念。后续几章将会对这些概念进行更详细的描述。

    首先,2.1节给出一个简单的C程序,并且描述了如何对这个程序进行编译和链接。接着,2.2节讨论如何使程序通用。2.3节说明如何添加说明性解释,即通常所说的注释。2.4节介绍变量,变量是用来存储程序执行过程中可能会发生改变的数据的。2.5节说明利用scanf函数把数据读入变量的方法。就如2.6节介绍的那样,常量是程序执行过程中不会发生改变的数据,用户可以对其进行命名。最后,2.7节解释C语言的命名(标识符)规则,2.8节给出了C程序的布局规范。

    2.1 编写一个简单的C程序

    与用其他语言编写的程序相比,C程序较少要求“形式化的东西”。一个完整的C程序可以只有寥寥数行。

    程序:显示双关语

    在Kernighan和Ritchie编写的经典C语言著作The C Programming Language一书中,第一个程序是极其简短的。它仅仅输出了一条hello, world消息。与大多数C语言书籍的作者不同,我不打算用这个程序作为第一个C程序示例,而更愿意尊重另一个C语言的传统:显示双关语。下面是一条双关语:

    To C, or not to C: that is the question.
    

    下面这个名为pun.c的程序会在每次运行时显示上述消息。

    /**
     * pun.c
     */
    
    #include <stdio.h>
    
    int main() {
        printf("To C, or not to C: that is the question.
    ");
        return 0;
    }
    

    2.2节会对这段程序中的一些格式进行详尽的说明,这里仅做简要介绍。程序中第一行

    #include <stdio.h>
    

    是必不可少的,它“包含”了C语言标准输入/输出库的相关信息。程序的可执行代码都在main函数中,这个函数代表“主”程序。main函数中的第一行代码是用来显示期望信息的。printf函数来自标准输入/输出库,可以产生完美的格式化输出。代码 告诉printf函数执行完消息显示后要进行换行操作。第二行代码,

    return 0;
    

    表明程序终止会向操作系统返回值0。

    2.1.1 编译和链接

    尽管pun.c程序十分简短,但是为运行这个程序而包含的内容可能比想象的要多。首先,需要生成一个含有上述程序代码名为pun.c的文件(使用任何文本编辑器都可以创建该文件)。文件的名字无关紧要,但是编译器通常要求带上文件的扩展名.c。

    接下来,就需要把程序转化为机器可以执行的形式。对于C程序来说,通常包含下列3个步骤。

    • 预处理。首先程序会被送交给预处理器(preprocessor)。预处理器执行以#开头的命令(通常称为指令)。预处理器有点类似于编辑器,它可以给程序添加内容,也可以对程序进行修改。
    • 编译。修改后的程序现在可以进入编译器(compiler)了。编译器会把程序翻译成机器指令(即目标代码)。然而,这样的程序还是不可以运行的。
    • 链接。在最后一个步骤中,链接器(linker)把由编译器产生的目标代码和所需的其他附加代码整合在一起,这样才最终产生了完全可执行的程序。这些附加代码包括程序中用到的库函数(如printf函数)。

    幸运的是,上述过程往往是自动实现的,因此人们会发现这项工作不是太艰巨。事实上,由于预处理器通常会和编译器集成在一起,所以人们甚至可能不会注意到它在工作。

    根据编译器和操作系统的不同,编译和链接所需的命令也是多种多样的。在UNIX系统环境下,通常把C编译器命名为cc。为了编译和链接pun.c程序,需要在终端或命令行窗口录入如下命令:

    % cc pun.c
    

    (字符%是UNIX系统的提示符,不需要输入。)在使用编译器cc时,系统自动进行链接操作,而无需单独的链接命令。

    在编译和链接好程序后,编译器cc会把可执行程序放到默认名为a.out的文件中。编译器cc有许多选项,其中有一个选项(-o选项)允许为含有可执行程序的文件选择名字。例如,假设要把文件pun.c生成的可执行文件命名为pun,那么只需录入下列命令:

    % cc -o pun pun.c
    

    GCC编译器

    GCC编译器是最流行的C编译器之一,它随Linux发行,但也有面向其他很多平台的版本。这种编译器的使用与传统的UNIX cc编译器相似。例如,编译程序pun.c可以使用以下命令:

    % gcc -o pun pun.c
    

    2.1.2 集成开发环境

    到目前为止,我们一直通过在操作系统提供的特殊窗口中键入命令的方式来调用“命令行”编译器。事实上,还可以使用集成开发环境(integrated development environment,IDE)进行编译。集成开发环境是一个软件包,我们可以在其中编辑、编译、链接、执行甚至调试程序。组成集成开发环境的各个部分可以协调工作。例如,当编译器发现程序中有错误时,它会让编辑器把包含出错代码的行突出显示出来。集成开发环境有很多种,本书不打算一一讨论它们,但我建议读者了解一下自己的平台上可以运行哪些集成开发环境。

    2.2 简单程序的一般形式

    下面一起来仔细研究一下pun.c程序,并且由此归纳出一些通用的程序格式。简单的C程序一般具有如下格式:

    指令
    int main(void)
    {
    	语句
    }
    

    在这个模版以及本书的其他类似模版中,所有以Courier字体显示的语句都代表实际的C语言程序代码,而所有以中文楷体显示的部分则表示需要由程序员提供的内容。

    注意任何使用大括号来标出main函数的起始和结束。C语言使用{}的方式非常类似于其他语言中begin和end的用法。这也说明了有关C语言一个共识:C语言极其依赖缩写词和特殊符号,这是C程序非常简洁(或者不客气地说含义模糊)的一个原因。

    即使是最简单的C程序也依赖3个关键的语言特性:指令(在编译前修改程序的编辑命名)、函数(被命名的可执行代码块,如main函数)和语句(程序运行时执行命令)。下面将详细讨论这些特性。

    2.2.1 指令

    在编译C程序之前,预处理器会首先对其进行编辑。我们把预处理器执行的命令称为指令。第14章和第15章会详细讨论指令,这里只关注#include指令。

    程序pun.c由下列这行指令开始:

    #include <stdio.h>
    

    这条指令说明,在编译前把<stdio.h>中的信息"包含"到程序中。<stdio.h>包含了关于C标准输入/输出库的信息。C语言拥有大量类似于<stdio.h>头(header),每个头都包含一些标准库的内容。这段程序中包含<stdio.h>的原因是:C语言不同于其他的编程语言,它没有内置的“读”和“写”命令。输入/输出功能由标准库中的函数实现。

    所有指令都是以字符#开始的。这个字符可以把C程序中的指令和其他代码区分开来。指令默认只占一行,每条指令的结尾没有分号或其他特殊标记。

    2.2.2 函数

    函数类似于其他编程语言中的“过程”或“子例程”,它们是用来构建程序的构建块。事实上,C程序就是函数的集合。函数分为两大类:一类是程序员编写的函数,另一类则是作为C语言实现的一部分提供的函数。我们把后者成为库函数(library function),因为它们属于一个由编译器提供的函数“库”。

    术语”函数“来源于数学。在数学中,函数是指根据一个或多个给定参数进行数值计算的规则:

    (f(x)=x+1)

    (g(y,z)=y^2-z^2)

    C语言对“函数”这个术语的使用则更加宽松。在C语言中,函数仅仅是一系列组合在一起并且赋予了名字的语句。某些函数计算数值,某些函数不这么做。计算数值的函数用return语句来指定所“返回”的值。例如,对参数进行加1操作的函数可以执行语句

    return x + 1;
    

    而当函数要计算参数的平方差时,则可以执行语句

    return y * y - z * z;
    

    虽然一个C程序可以包含多个函数,但只有main函数是必须有的。main函数是非常特殊的:在执行程序时系统会自动调用main函数。在第9章,我们将学习如何编写其他函数,在此之前的所有程序都只包含一个main函数。

    main函数的名字至关重要的,绝对不能改写为begin或者start,甚至写出MAIN也不行。

    如果main是一个函数,那么它会返回一个值吗?是的。它会在程序终止时向操作系统返回一个状态码。我们再来看看pun.c程序:

    #include <stdio.h>
    
    int main() {
        printf("To C, or not to C: that is the question.
    ");
        return 0;
    }
    

    main前面的int表明该函数将返回一个整数值。圆括号中的void表明main函数没有参数。语句

    return 0;
    

    有两个作用:一是使main函数终止(从而结束程序),二是指出main函数的返回值是0.在后面我们还将详细论述main函数的返回值(9.5节)。但是现在我们始终让main函数的返回值为0,这个值表明程序正常终止。

    如果main函数的末尾没有return语句,程序仍然能终止。但是,许多编译器会产生一条警告信息(因为函数应该返回一个整数却没有这么做)。

    2.2.3 语句

    语句是程序运行时执行的命名。本书后面的几章(主要集中在第5章和第6章)将进一步探讨语句。程序pun.c只用到两种语句。一种是返回(return)语句,另一种则是函数调用(function call)语句。要求某个函数执行分派给它的任务成为调用这个函数。例如,程序pun.c为了在屏幕上显示一条字符串就调用了printf函数:

    printf("To C, or not to C: that is the question.
    ");
    

    C语言规定每条语句都要以分号结尾。(就像任何好的规则一样,这条规则也有一个例外:后面会遇到的复合语句(5.2节)就不以分号结尾。)由于语句可以连续占用多行,有时很难确定它的结束为止,因此用分号来向编译器显示语句的结束位置。但指令通常都只占一行,因此不需要用分号结尾。

    2.2.4 显示字符串

    printf是一个功能强大的函数,第3章将会进一步介绍。到目前为止,我们只是用printf函数显示了一条字符串字面量(string literal)——用一对双引号包围的一系列字符。当用printf函数显示字符串字面量时,最外层的双引号不会出现。

    当显示结束时,printf函数不会自动跳到下一输出行。为了让printf跳转到下一行,必须在要显示的字符串中包含 (换行符)。写换行符就意味着终止当前行,然后把后续的输出转到下一行。为了说明这一点,请思考把语句

    printf("To C, or not to C: that is the question.
    ");
    

    替换成下面两个对printf函数的调用后所产生的效果:

    printf("To C, or not to C: ");
    printf("that is the question.
    ");
    

    第一条printf函数的调用语句显示出To C,or not to C:;而第二条调用语句则显示出that is the question.并且跳转到下一行。最终的效果和前一个版本的printf语句完全一样,用户不会发现什么差异。

    换行符可以在一个字符串字面量中出现多次。为了显示下列信息:

    Brevity is the soul of wit.
     --Shakespeare
    

    可以这样写:

    printf("Brevity is the soul of wit.
     --Shakespeare
    ");
    

    2.3 注释

    我们的pun.c程序仍然缺乏某些重要内容:文档说明。每一个程序都应该包含识别信息,即程序名、编写日期、作者、程序的用途以及其他相关信息。C语言把这类信息放在注释(comment)中。符号/*标记注释的开始,而符号*/则标记注释的结束。例如:

    /* This is a comment */
    

    注释几乎可以出现在程序的任何位置上。它既可以单独占行也可以和其他程序文本出现在同一行中。下面展示的程序pun.c就把注释加在了程序开始的地方:

    /* Name:pun.c */
    /* Purpose:Prints a bad pun. */
    /* Author: K. N. King */
    
    #include <stdio.h>
    
    int main() {
        printf("Brevity is the soul of wit.
     --Shakespeare
    ");
        return 0;
    }
    

    注释还可以占用多行。一旦遇到符号/*,那么编译器读入(并且忽略)随后的内容直到遇到符号*/为止。如果愿意,还可以把一串短注释合并成为一条长注释:

    /* Name:pun.c 
     Purpose:Prints a bad pun. 
     Author: K. N. King */
    

    但是,上面这样的注释可能难于阅读,因为人们阅读程序时可能不易发现注释的结束为止。所以,单独把*/符号放在一行会很有帮助:

    /*	Name:pun.c
    	Purpose:Prints a bad pun.
    	Author: K. N. King 
     */
    

    更好的方法是用一个“盒形”格式把注释单独标记出来:

    /*************************************************
    * Name:pun.c									*
    * Purpose: Prints a bad pun.					 *
    * Author: K. N. King							 *
    **************************************************/
    

    有些程序员通过忽略3条边框的方法来简化盒形注释:

    /**
     * Name:pun.c
     * Purpose:Prints a bad pun.
     * Author: K. N. King 
     */
    

    简短的注释还可以与程序中的其他代码放在同一行:

    int main(void) /*Beginning of main program*/
    

    这类注释有时也称作“翼型注释”。

    C99提供了另一种类型的注释,以//(两个相邻的斜杠)开始:

    // This is a comment
    

    这种风格的注释会在行末自动终止。如果要创建多于一行的注释,既可以使用以前的注释风格(/*...*/),也可以在每一行的前面加上//

    // Name:pun.c
    // Purpose:Prints a bad pun.
    // Author:K. N. King
    

    新的注释风格有两个主要优点:首先,因为注释会在行末自动终止,所以不会出现未终止的注释意外吞噬部分程序的情况;其次,因为每行前面都必须有//,所以多行的注释更加醒目。

    2.4 变量和赋值

    很少有程序会像2.1节中的示例那样简单。大多数程序在产生输出之前往往需要执行一系列的计算,因此需要在程序执行过程中有一种临时存储数据的方法。和大多数编程语言一样,C语言中的这类存储单元被称为变量(variable)

    2.4.1 类型

    每个变量都必须有一个类型(type)。类型用来说明变量所存储的数据的种类。C语言拥有广泛多样的类型。但是现在,我们将只限定在两种类型范围:int类型和float类型。由于类型会影响变量的存储方式以及允许对变量进行的操作,所以选择合适的类型是非常关键的。数值类型变量的类型决定了变量所能存储的最大值和最小值,同时也决定了是否允许在小数点后出现数字。

    int(即integer的简写)型变量可以存储整数,如0、1、392或者-2553。但是,整数的取值范围(7.1节)是受限制的。最大的整数通常是2 147 483 647,但在某些计算机上也可能只有32 767。

    float(即floating-point的简写)型变量可以存储比int型变量大得多的数字。而且,float型变量可以存储带小数位的数,如379.125。但float型变量也有一些缺陷。进行算术运算时float型变量通常比int型变量慢;更重要的是,float型变量所存储的数值往往只是实际数值的一个近似值。如果在一个float型变量中存储0.1,以后可能会发现变量的值为0.099 999 999 999 999 87,这是舍入造成的误差。

    2.4.2 声明

    在使用变量之前必须对其进行声明(为编译器所做的描述)。为了声明变量,首先要指定变量的类型,然后说明变量的名字。(程序员决定变量的名字,命名规则见2.7节。)例如,我们可能这样声明变量height和profit:

    int height;
    float profit;
    

    第一条声明说明height是一个int型变量,这也意味着变量height可以存储一个整数值。第二条声明则表示profit是一个float型变量。

    如果几个变量具有相同的类型,就可以把它们的声明合并:

    int height, length, width, volume;
    float profit, loss;
    

    注意每一条完整的声明语句都要以分号结尾。

    在main函数的第一个模版中并没有包含声明。当main函数包含声明时,必须把声明放置在语句之前:

    int main(void){
        声明
        语句
    }
    

    第9章我们将会看到,函数和程序块(包含嵌入声明的语句,10.3节)一般都有这样的要求。就书写格式而言,建议在声明和语句之间留出一个空行。

    在C99中,声明可以不在语句之前。例如,main函数中可以先有一个声明,后面跟一条语句,然后再跟一个声明。为了与以前的编译器兼容,本书中的程序不会采用这一规则。但是,考虑到C++和Java程序中在使用时才声明变量的情况很常见,估计将来在C99程序中这种做法也会很流行。

    2.4.3 赋值

    变量通过赋值(assignment)的方式获得值。例如,语句

    height = 8;
    length = 12;
    width = 10;
    

    把数值8、12和10分别赋给变量height、length和width,8、12和10称为常量(constant)。

    变量在赋值或以其他方式使用之前必须先声明。也就是说,我们可以这样写:

    int height;
    height = 8;
    

    但下面这样是不行的:

    height = 8; /*** WRONG ***/
    int height;
    

    赋给float型变量的常量通常都带小数点。例如,如果profit是一个float型的变量,可能会这样对其赋值:

    profit = 2150.48;
    

    当我们把一个包含小数点的常量赋值给float型变量时,最好在该常量后面加一个字母f(代表float)。

    profit = 2150.48f;
    

    不加f可能会引发编译器的警告。

    正常情况下,要将int型的值赋给int型的变量,将float型的值赋给float型的变量。混合类型赋值(如把int型的值赋给float型变量或者把float型的值赋给int型变量)是可以的,但不一定安全,见4.2节。

    一旦变量被赋值,就可以用它来辅助计算其他变量的值:

    height = 8;
    length = 12;
    width = 10;
    volume = heigth * length * width;  /* volume is now 960 */
    

    在C语言中,符号*表示乘法运算,因此上述语句把存储在height、length和width这3个变量中的数值相乘,然后把运算结果赋值给变量volume。通常情况下,赋值运算的右侧可以是一个含有常量、变量和运算符的公式(在C语言的术语中称为表达式)。

    2.4.4 显示变量的值

    用printf可以显示出变量的当前值。以

    Height: h
    

    为例,这里的h表示变量height的当前值。我们可以通过如下的printf调用来实现输出上述信息的要求:

    printf("Height: %d
    ", height);
    

    占位符%d用来指明在显示过程中变量height的值的显示位置。注意,由于在%d后面放置了 ,所以printf在显示完height的值后会跳到下一行。

    %d仅用于int型变量。如果要显示float型变量,需要用%f来代替%d。默认情况下,%f会显示出小数点后6位数字。如果要强制%f显示小数点后p位数字,可以把p放置在%和f之间。例如,为了显示信息

    Profit: $2150.48
    

    可以把printf写为如下形式:

    printf("Profit: %.2f
    ", profit);
    

    C语言没有限制调用一次printf可以显示的变量的数量。为了同时显示变量height和变量length的值,可以使用下面的printf调用语句:

    printf("Height: %d Length: %d
    ", height, length);
    

    程序:计算箱子的空间重量

    运输公司特别不喜欢又大又轻的箱子,因为箱子在卡车或飞机上运输时要占据宝贵的空间。事实上,对于这类箱子,公司常常要求按照箱子的体积而不是重量来支付额外的费用。在美国,通常的做法是把体积除以166(这是每磅允许的立方数)。如果除得的商(也就是箱子的“空间”重量或“体积”重量)大于箱子的实际重量,那么运费就按照空间重量来计算。(除数166是针对国际运输的,计算国内运输的空间重量时通常用194代替。)

    假设运输公司雇你来编写一个计算箱子空间重量的程序。因为刚刚开始学习C语言,所以你决定先编写一个计算特定箱子空间重量的程序来试试身手,其中箱子的长、宽、高分别是12英寸、10英寸和8英寸。C语言中除法运算用符号/表示。所以很显然计算箱子空间重量的公式如下:

    weight = volume / 166;
    

    这里的weight和volume都是整型变量,分别用来表示箱子的重量和体积。但是上面这个公式并不是我们所需要的。在C语言中,如果两个整数相除,那么结果会被“截短”:小数点后的所有数字都会丢失。12英寸X10英寸X8英寸的箱子体积是960立方英寸,960除以166的结果是5而不是5.783,这样使得重量向下取整;而运输公司则希望结果向上取整。一个解决方案是在除以166之前把体积数加上165:

    weight = (volume + 165) / 166;
    

    这样,体积为166的箱子重量就为331/166,取整为1;而体积为167的箱子重量则为332/166,取整为2。下面给出了利用这种方法编写的计算空间重量的程序。

    /*Computes the dimensional weight of a 12`` x 10`` x 8`` box*/
    
    #include <stdio.h>
    
    int main(void) {
        int height,length,width,volume,weight;
    
        height=8;
        length=12;
        width=10;
        volume=height*length*width;
        weight=(volume+165)/166;
    
        printf("Dimensions: %dx%dx%d
    ",length,width,height);
        printf("Volume (cubic inches): %d
    ",volume);
        printf("Dimensional weight (pounds): %d
    ",weight);
        
        return 0;
    }
    

    这段程序的输出结果是:

    Dimensions: 12x10x8
    Volume (cubic inches): 960
    Dimensional weight (pounds): 6
    

    2.4.5 初始化

    当程序开始执行时,某些变量会被自动设置为零,而大多数变量则不会(18.5节)。没有默认值并且尚未在程序中被赋值的变量是未初始化的(uninitialized)

    如果试图访问未初始化的变量(例如,用printf显示变量的值,或者在表达式中使用该变量),可能会得到不可预知的结果,如2 568、-30 891或者其他同样没有意义的数值。在某些编译器中,可能会发生更坏的情况(甚至是程序崩溃)。

    我们当然可以总是采用赋值的方法给变量赋初始值,但还有更简单的方法:在变量声明中加入初始值。例如,可以在一步操作中声明变量height并同时对其初始化:

    int height = 8;
    

    按照C语言的术语,数值8是一个初始化式(initializer)

    在同一个声明中可以对任意数量的变量进行初始化;

    int height = 8, length = 12, width = 10;
    

    注意,上述每个变量都有属于自己的初始化式。在接下来的例子中,只有变量width拥有初始化式10,而变量height和变量length都没有(也就是说这两个变量仍然未初始化):

    int height, length, width = 10;
    

    2.4.6 显示表达式的值

    printf的功能不局限于显示变量中存储的数,它可以显示任意数值表达式的值。利用这一特性既可以简化程序,又可以减少变量的数量。例如,语句

    volume = height * length * width;
    printf("%d
    ",volume);
    

    可以用以下形式代替:

    printf("%d
    ",height * length * width);
    

    printf显示表达式的能力说明了C语言的一个通用原则:在任何需要数值的地方,都可以使用具有相同类型的表达式。

    2.5 读入输入

    程序dweight.c并不十分有用,因为它仅可以计算出一个箱子的空间重量。为了改进程序,需要允许用户自行录入尺寸。

    为了获取输入,就要用到scanf函数。它是C函数库中与printf相对应的函数。scanf中的字母f和printf中的字母f含义相同,都是表示“格式化”的意思。scanf函数和printf函数都需要使用**格式串(format string)来指定输入或输出数据的形式。scanf函数需要知道将获得的输入数据的格式,而printf函数需要知道输出数据的显示格式。

    为了读入一个int型值,可以使用下面的scanf函数调用:

    scanf("%d", &i); /* reads an integer; stores into i */
    

    其中,字符串“%d"说明scanf读入的是一个整数,而i是一个int型变量,用来存储scanf读入的输入。&运算符(11.2节)在这里很难解释清楚,因此现在只说明它在使用scanf函数时通常是(但不总是)必需的。

    读入一个float型值时,需要一个形式略有不同的scanf调用:

    scanf("%f",&x); /* reads a float value; stores into */
    

    %f只用于float型变量,因此这里假设x是一个float型变量。字符串“%f"告诉scanf函数去寻找一个float格式的输入值(此数可以含有小数点,但不是必须含有)。

    程序计算箱子的空间重量(改进版)

    下面是计算空间重量的一个改进版。在这个改进的程序中,用户可以录入尺寸。注意,每一个scanf函数调用都紧跟在一个printf函数调用的后面。这样做可以提示用户何时输入,以及输入什么。

    dweight2.c

    /**
     * Computes the dimensional weight of a
     * box from input provided by the user
     */
    #include <stdio.h>
    
    int main() {
        int height, length, width, volume, weight;
    
        printf("Enter height of box:");
        scanf("%d", &height);
        printf("Enter length of box:");
        scanf("%d", &length);
        printf("Enter width of box:");
        scanf("%d", &width);
        volume = height * length * width;
        weight = (volume + 165) / 166;
    
        printf("Volume (cubic inches): %d
    ", volume);
        printf("Dimensional weight (pounds): %d
    ", weight);
    
        return 0;
    }
    

    这段程序的输出显示如下(用户的输入用下划线标注):

    Enter height of box:8
    Enter length of box:12
    Enter width of box:10
    Volume (cubic inches): 960
    Dimensional weight (pounds): 6
    

    提示用户输入的消息(提示符)通常不应该以换行符结束,因为我们希望用户在同一行输入。这样,当用户敲回车键时,光标会自动移动到下一行,因此就不需要程序通过显示换行符来终止当前行了。

    dweight2.c程序还存在一个问题:如果用户输入的不是数值,程序就会出问题。3.2节会更详细地讨论这个问题。

    2.6 定义常量的名字

    当程序含有常量时,建议给这些常量命名。程序dweight.c和程序dweight2.c都用到了常量166。在后期阅读程序时也许有些人会不明白这个常量的含义。所以可以采用称为宏定义(macro definition)的特性给常量命名:

    #define INCHES_PER_POUND 166
    

    这里的#define是预处理指令,类似于前面所讲的#include,因而在此行的结尾也没有分号。

    当对程序进行编译时,预处理器会把每一个宏替换为其表示的值。例如,语句

    weight = (volume + INCHES_PER_POUND - 1) / INCHES_PER_POUND;
    

    将变为

    weight = (volume + 165) / 166;
    

    效果就如同在前一个地方写的是最后一条语句。

    此外,还可以利用宏来定义表达式:

    #define RECIPROCAL_OF_PI (1.0f / 3.14159f)
    

    当宏包含运算符时,必须用括号(14.3节)把表达式括起来。

    注意,宏的名字只用了大写字母。这是大多数C程序员遵循的规范,但并不是C语言本身的要求。(至今,C程序员沿用此规范已经几十年了,希望读者不要打破此规范。)

    程序:华氏温度转换为摄氏温度

    下面的程序提示用户输入一个华氏温度,然后输出一个对应的摄氏温度。此程序的输出格式如下(跟前面的例子一样,用户的输入信息用下划线标注出来):

    Enter Fahrenheit temperature: 212
    Celsius equivalent: 100.0
    

    这段程序允许温度值不是整数,这也是摄氏温度显示为100.0而不是100的原因。首先来阅读一下整个程序,随后再讨论程序是如何构成的。

    celsius.c

    /**
     * Converts a Fahrenheit temperature to Celsius
     */
    #include <stdio.h>
    
    #define FREEZING_PT 32.0f
    #define SCALE_FACTOR (5.0f / 9.0f)
    
    int main() {
        float fahrenheit, celsius;
    
        printf("Enter Fahrenheit temperature:");
        scanf("%f", &fahrenheit);
    
        celsius = (fahrenheit - FREEZING_PT) * SCALE_FACTOR;
    
        printf("Celsius equivalent: %.1f
    ", celsius);
        
        return 0;
    }
    

    把华氏温度转换为相应的摄氏温度。因为FREEZING_PT表示的是常量32.0f,而SCALE_FACTOR表示的是表达式(5.0f / 0.9f),所以编译器会把这条语句看成是

    celsius = (fahrenheit - 32.0f) * (5.0f / 9.0f);
    

    在定义SCALE_FACTOR时,表达式采用(5.0f / 9.0f)的形式而不是(5 / 9)的形式,这一点非常重要,因为如果两个整数想相除,那么C语言会对结果向下取整。表达式(5 / 9)的值将为0,这并不是我们想要的。

    最后的printf函数调用输出相应的摄氏温度:

    printf("Celsius equivalent: %.1f
    ", celsius);
    

    注意,使用%.1f显示celsius的值时,小数点后只显示一位数字。

    2.7 标识符

    在编写程序时,需要对变量、函数、宏和其他实体进行命名。这些名字成为标识符(identifier)。在C语言中,标识符可以含有字母、数字和下划线,但是必须以字母或者下划线开头。(在C99中,标识符还可以使用某些“通用字符名”,25.4节。)

    下面是合法标识符的一些示例:

    times10 get_next_char _done
    

    接下来这些则是不合法的标识符:

    10times get-next-char
    

    不合法的原因是:符号10times是以数字而不是以字母或下划线开头的;符号get-next-char包含了减号,而不是下划线。

    C语言是区分大小写的;也就是说,在标识符中C语言区别大写字母和小写字母。例如,下列标识符全是不同的:

    job joB jOb jOB Job JOb JOb JOB
    

    上述8个标识符可以同时使用,且每一个都有完全不同的意义。(看起来使人困惑!)除非标识符之间存在某种关联,否则明智的程序员会尽量使标识符看起来各不相同。

    因为C语言是区分大小写的,许多程序员都会遵循在标识符中只使用小写字母的规范(宏命名除外)。为了使名字清晰,必要时还会插入下划线:

    symbol_table current_page name_and_address
    

    而另外一些程序员则避免使用下划线,他们的方法是把标识符中的每个单词用大写字母开头:

    symbolTable currentPage nameAndAddress
    

    (第一个字母有时候也用大写。)前一种风格在传统C中很常见,但现在后面的风格更流行一些,这主要归功于它在Java和C#(以及C++)中的广泛使用。当然还存在其他一些合理的规范,只要保证整个程序中对同一标识符按照同一种方式使用大写字母就行。

    C对标识符的最大长度没有限制,所以不用担心使用较长的描述性名字。诸如current_page这样的名字比cp子类的名字更容易理解

    关键字

    表2-1中的所有关键字(keyword)对C编译器而言都有着特殊的含义,因此这些关键字不能作为标识符来使用。注意,其中有5个关键字是C99新增的。

    表2-1 关键字
    auto enum restrict unsigned
    break extern return void
    case float short volatile
    char for signed while
    const goto sizeof _Bool
    continue if static _Complex
    default inline struct _Imaginary
    do int switch
    double long typedef
    else register union

    因为C语言是区分大小写的,所以程序中出现的关键字必须严格按照表2-1所示的格式全部采用小写字母。(C99关键字_Bool_Complex_Imaginary例外。)标准库中函数(如printf)的名字也只能包含小写字母。某些可怜的程序员用大写字母录入了整个程序,结果却发现编译器不能识别关键字和库函数的调用。应该避免这类情况发生。

    请注意有关标识符的其他限制。某些编译器把特定的标识符(如asm)视为附加关键字。属于标准库的标识符也是受限的(21.1节)。误使用这些名字可能会导致在编译或链接时发生错误。以下划线开头的标识符也是受限的。

    2.8 C程序的书写规范

    我们可以把C程序看成是一连串记号(token),即许多在不改变意思的基础上无法再分割的字符组。标识符和关键字都是记号。像+和-这样的运算符、逗号和分号这样的标点符号以及字符串字面量,也都是记号。

    大多数情况下,程序中记号之间的空格数量没有严格要求。除非两个记号合并后会产生第三个记号,否则在一般情况下记号之间根本不需要留有间隔。

  • 相关阅读:
    汇编10:CALL和RET指令
    汇编09:转移指令的原理
    汇编08:数据处理的两个基本问题
    汇编07:定位内存地址的方法
    汇编06:包含多个段的程序
    汇编05:[BX]和loop指令
    汇编04:第一个汇编程序
    汇编03:寄存器
    C#版的mongodb最新的官方驱动2.4.0版本
    如何教你看懂复杂的正则表达式
  • 原文地址:https://www.cnblogs.com/shenhuanjie/p/11416592.html
Copyright © 2011-2022 走看看