Oct 12

1.3.5. 字符串

在C语言中,字符串就是一系列放在一对双引号之间的字符:

“就像这样”

由于一个字符串就是一个单独的元素,就像同标志符一样,所以一个字符串只能写在一行上--尽管字符内部可以包含空格或制表符。

“这是一个 有效的 字符串”
“这里有一个换行符
所以是无效的字符串”

有两种方法可以写出一个很长的字符串。在C语言中,无论在什么地方,反斜杠加换行符这样的序列会完全消失不见,所以我们可以利用这一点。

“这样原本是不可以的 \
但对于编译器来说这个换行符不存在”

另一个方法,就是利用字符串连接的功能。也就是说,两个相邻的字符串会被当成一个字符串。

“所有这些” “都会成为”
“一个字符串”

现在回头看看这个例子。这个序列中的 \n 是一个所谓换码序列,在这里,这个序列代表了换行符。printf 函数把这个字符串的内容打印到程序的输出文件中,所以我们看到输出的是 hello,然后再是新的一行。

有的人所使用的编程环境使用了比美国的ASCII字符集更“宽”的字符,比如中国大陆使用的GB2312(译注1.3)。为了对这些程序员提供支持,C语言标准允许在字符串和注释中使用多字节字符。C语言标准规定了C语言所用的96个字符(参见第二章)。如果你的系统可以支持扩展字符集,你只能在字符串、字符常量、注释以及头文件的文件名中使用这些扩展字符。对于扩展字符集如何支持是取决于编译系统的,因此你得去查看你的系统说明文档。

1.3.6. main 函数

在例1.1中实际上有两个函数,即 show_message 和 main 函数。尽管 main 函数比 show_message 函数长那么一点,但很明显它俩长得一样:它们都有名字,然后是小括号(),然后是一个复合语句开头的左花括号“{”。没错,这之后还有好些东西,不过在最后你可以找到一个右花括号“}”和前面的左花括号相对应。

这个函数就实际多了,因为在函数体里有好几个语句,而不是只有一个。你也许已经注意到了,这个函数没有被声明为 void。这当然是有原因的:这个函数返回一个值。现在不用理会这个函数的参数。这些会在第十章中讲到。

关于 main 函数,最重要的一点就是,这是第一个被调用的函数。在主机环境中,当程序开始运行时,你的C语言系统会神奇地安排调用一个叫做 main 的函数(这也就是这个函数叫做 main 的原因)。这个函数结束运行时,整个程序也就结束了。很显然这是一个重要的函数。同样重要的,就是 main 函数下复合语句里面的内容。如前所述,一个复合语句中可以有好几个语句。那么就让我们一一道来。

1.3.7. 声明

第一个语句是这样的:

int count;

它不做任何事,只不过在程序中引入了一个变量。这个语句声明了一样东西,名字叫做 count,其类型为“整数”。在C语言中,用来声明整数的关键词碰巧被缩写成了“int”。C语言对于这些关键词有种特别的处理方式,有些被完整地拼写出来,有些则像 int 一样被缩写了。至少 int 还有或多或少不言自明的含义,到了后面讲 static 的时候好戏才真正登场呢。

由于有了这个声明,编译器就知道了,这里有一件东西要用来存放整数,而且它的名字是 count。在C语言中,所有的变量都必须先声明过后再能使用,而不存在像FORTRAN里那样的隐性声明。在复合语句中,所有的声明必须都放在最前面。这些声明必须在所有“正常”的语句之前,这就使得它们比较特别。

(喜欢钻牛角尖的人:如果你非得要问的话,这里对 count 这样变量的声明同时也是对它的定义。在后面我们才会看到两者的实际区别。)

 

1.3.8 赋值语句

顺着例子往下看,我们可以找到一个赋值语句,与声明很相似。在这里,那个 count 变量第一次被赋值。在这里,被赋与的值是一个常数,其数值为0。在这个赋值语句之前,count 这个变量的值是没有定义、不可预知的。你也许会觉得奇怪,这个赋值符号(准确地说是赋值运算符)是一个等于号=。这在现代编程语言中是不时髦的(译注1.4),不过这只是白玉微瑕。

到现在为止,我们已经声明了一个变量,并把数值0赋给了它。接下来呢?

1.3.9 while语句

接下来是while语句。这是C语言的循环控制语句之一。好好看看它的形式吧。while语句的正式描述是这样的:

    while(expression)
        statement

我们的while语句是这样的吗?正是如此。下面的这个表达式

    count < 10

是一个关系表达式。这是一个有效的表达式。而这个表达式之后跟着一个复合语句,这就形成了一个有效的语句。这样就满足了构成while语句的条件。

这个语句做的事情对于任何写过程序的人来说都是很明显的。只要 count < 10 这个条件成立,循环体就会被执行,然后又再进行比较。如果想让这个程序能够停下来,那么这个循环体就必需能够最终让这个比较表达式成为“假”。无疑它能够做到这点。

循环体里只有两个语句。第一个是函数调用语句,调用了show_message这个函数。之所以这是一个函数调用,是因为首先出现了函数名称,然后跟是一对括号(),其包含了函数的参数列表。如果函数不带函数,那么你就不给它参数就行了。如果函数带参数,这些参数就得像下面这样放在括号里:

    /* 调用带参数的函数 */
    function_name(first_arg, second_arg, third_arg);

调用printf则是另一种形式。这在第四章中有更详细的讲解。

循环体的最后一个语句也是赋值语句。它的作用是把 count 变量加一,最终就能满足程序停止的条件。

1.3.10 返回(return)语句

最后我们要讨论的一个语句就是这个返回(return)语句。它看上去就像是一个函数调用语句,但其实它的书写规则是这样的:

    return expression;

这里的 expression(表达式) 不是必须的。这个例子采用了一种通常的美观写法,把这个表达式放在了括号里。这其实对程序没有任何影响。

这个返回语句使得当前函数把一个数值返回给调用这个函数的地方。如果这里没有写表达式,那么返回的就可能是任何数值--这几乎肯定是错的,除非函数本身返回类型是void(空)。和 show_message 函数不同的是,main 函数没有声明为任何类型。那么 main 函数返回什么类型的值呢?答案是 int (整型)。在C语言中,有很多地方可以有默认声明:函数的默认返回类型是 int,所以常常可以看到 main 函数没有返回类型。在这种情况下就相当于把 main 函数声明为:

    int main(){

最后的结果是一样的(译注1.5)。

对于变量,则不能用这种方式得到一个默认类型,因为变量类型都必须是明确指定的。

那么,main 函数返回数值是什么意思呢?返回的数值去了哪里呢?在旧版C里,这个返回值回到了操作系统,或是任何其它开始运行这个程序的地方。在类似UNIX的环境下,数值0通常表示某种意义上的“成功”,其它所有数值(通常是-1)表示“失败”。标准C把惯例变成了规定,明确指出0代表程序的正确结束。这并不意味着返回到主机系统就是数值0,而是说返回的数值应该是在该系统中代表的“成功”的数值。由于在这点上通常有一些模糊,你也许会比较喜欢用在 <stdlib.h> 头文件中预定的 EXIT_SUCCESS(成功退出)和 EXIT_FAILURE(失败退出)这两个值来代替。从 main 函数中返回,实际上和调用 exit 库函数并用返回值作参数是一样的。区别在于,exit 库函数可以程序的任何地方调用,而程序就会在那个地方做一些整理工作后停下来。如果你想用 exit 库函数,就必须包含 <stdlib.h> 这个头文件。从现在起,我们将使用 exit,而不是从 main 函数中返回。

小结

main 函数返回的是一个 int 值。

从 main 函数返回和调用 exit 函数是一样的,只不过 exit 可以在程序的任何地方调用。

返回 0 或者 EXIT_SUCCESS 代表成功,而其它任何值都代表失败。

 

1.3.11 目前的进展

这里的范例程序尽管短小,还是让我们了解到了几个重要的程序特性,包括:

  • 程序结构
  • 注释文件包含
  • 函数定义
  • 复合语句
  • 函数调用
  • 变量声明
  • 算术运算
  • 循环

当然,所有这些都不是很严谨的讲解。

 


 

译注:

1.1、原文为 implementation defined。这里implementation的意思是具体的C语言编译器、连接程序、装入程序等等。这里译为“编译系统”。

1.2、在C标准中,严格意义上来讲预处理指令和声明都不是语句(statement),因此这里的“语句”一词加上了引号,表明是使用了“语句”一词的非正式用法。下文中的“语句”一词也都是这种用法。

1.3、原文用了日语编码shift-JIS为例。在这里考虑到读者可能更加熟悉中文编码,特换为GB2312。

1.4、由于C语言的流行,事实上现在非常多“时髦”的编程语言都沿用了C当中这种用等于号做赋值符号的做法。

1.5、事实上,写程序时最好把main的类型声明明确地写出来。在最新的C标准(C99)要求编译器在 main 返回类型省略时给出警告。