上一节,我们大致总览了一个简单C程序的框架,程序如下:
#include<stdio.h> /*引入头文件*/ int main(void) /*一个简单的C程序*/ { int number; /*定义个名字叫做number的变量*/ number=2014; /*给number赋一个值*/ printf("Hello ! I am dotcpp.com\n"); /*像屏幕打印一句话*/ printf("This year is %d\n",number); return 0; }
或许您已经看懂了前面的叙述,但又觉得不能够通透。诚然如此,麻雀虽小,五脏俱全,即使非常简单的一个C程序,也包含着许多的知识,这一节,我们将从逐条语句出发,去深入探讨程序的背后,如果您能更深的理解这个简单的程序,那么在之后的学习中将更加顺畅自如!
一、头文件
#include<stdio.h>
这个是程序的第一行,作用为在我们的代码中该行所在的位置引入stdio.h这个文件的全部内容,更简单的说,就像一个粘贴的操作,而此刻大家应该想到,#include后尖括号<>里的文件也可以是其他头文件,没错,在今后的学习中,我们会看到更多的#include语句,其中包含了更多的头文件,这也体现了一种设计思想。可以方便的在多个程序间共享公用的信息。
#include语句是C预处理器指令(preprocessor directive)的一个例子,这些是编译器在编译代码之前要做的准备工作,称之为预处理(preprocessing)。
再来说说stdio.h这个文件,不用觉得奇怪,它和我们平常见到的123.txt并无差别,只是后缀名不同而已,这个名字表示:标准输入输出头文件,正如英文:standard input/output header,它包含了与输入输出相关的函数(比如printf)的信息,在C语言的世界中,人们称出现在文件顶部的信息集合为头(header),C实现通常都带有多个头文件。
那么,为什么C语言没有内置的输入输出语句呢?一个答案是并非所有的程序都要用到I/O(输入/输出)包,并且C语言的一个基本的设计原则就是避免不必要的成分。这个经济的使用资源的原则使得C语言在嵌入式编程中非常的流行,例如:为一个控制自动报警系统的芯片编写程序。值得一提的是,#include甚至不是C语言的语句!开头的#,这个符号表明这一行是在编译器接手之前先由C预处理器处理的语句。以后我们将碰到更多的预处理指令。
二、主函数
int main()
接下来的代码声明了一个main函数。的确,main是一个及其普通的名字,但它是唯一的选择。一个C程序(我们不考虑例外的情况)总是从main()函数开始执行。我们可以对自己使用的其他函数任意命名,但是main()必须是开始的函数。每个C程序中必须有它!
那么圆括号的功能呢?它表明main()是一个函数。以后,我们将遇到更多的函数。现在,请记住这个函数就是C程序的基本模块。
int指明main()函数的返回类型。这意味着main()函数返回值的基本类型为整数。返回到哪里呢?返回给操作系统。为什么要有返回值呢?打个比方,就像皇上交代大臣去办事,大臣完成后总要复命,向皇上禀告,告诉皇上是成功还是失败。
函数名后面的圆括号一般包含传递给函数的信息。在我们这个简单的例子中没有传递任何信息,因此圆括号内包含了单词void。以后我们将看到更多传递参数的例子。
另外,您可能在一些教材或老的版本中,看到过这样的写法:
main()
这种形式在C90标准中勉强允许这种形式,在C99标准不允许,即使您的编译器允许。
以及还有这样的写法:
void main()
仅仅有些编译器允许这种形式,但还没有任何编译器考虑接受它,而有的编译器则会报错。因此,我们建议您不要这样做。另一方面,坚持使用标准形式,我们也不必担心程序从一个编译环境移到另一个编译环境上时出错的问题。
三、注释
/*一个简单的C程序*/
包含在/* */之间的部分是程序注释。使用注释的目的是使人们(包括我们自己)更容易的理解我们的程序。比如,有些程序员除了用注释说明我们的代码以外,还会在代码的注释中写这一天的天气如何、心情如何等等。C语言的注释的一个好处就是可以放在任何地方,甚至是和它要解释的语句在同一行。一个较长的注释可以单放一行,或者是多行。在/*和*/之间的所有内容都会被编译器忽略掉,如:
int number; /* 定义一个整形变量number*/
注释也可以分成两行或多行,如:
/* 笔者自述: 时间:18:24 地点:北半球 天气:窗外天气不明 微冷 肚子有点饿 */
除此之外,C99还增加另一种风格的注释,它被普遍用在C++或java里,这种新形式使用//符号,但这种注释被限制在一行里,如:
x=10; //将x赋值为10
四、花括号
{ /*... */ }
在C程序中,通常所有的C函数都使用花括号来表示函数体的开始与结束。它们的存在是必不可少的,因此我们不能丢掉它们。并且仅有花括号{}能起到这种作用,小括号()和中括号[]都不行。花括号还可以用来把函数中的语句聚集到一个单元或代码块中。
五、声明
int num;
程序中这一行叫做声明语句(declaration statement)。该声明语句是C语言中最重要的功能之一。这个特殊的例子声明两件事情。第一,在函数中您有一个名为num的变量,第二,int说明num是一个整数,也就是说这个数没有小数部分(int是C语言的一种数据类型)。编译器使用这个信息为变量num在内存中分配一个合适的存储空间。句末的分号指明这一行是C语言的一个语句或指令,分号是语句的一部分,每个C语言都以一个分号结束。
单词int是C语言的一个关键字,代表了一种C语言的数据类型。关键字是用来表达语言的单词,您不能将他们用于其他目的。比如,不能用int用作一个函数名或者一个变量的名字。
本例中的单词num是一个标识符(identifier),也就是您为一个变量、函数或其他实体所选的名字。这样该声明就把一个特殊的标识符和计算机中的一个特殊位置联系了起来,同时也确定了该位置存储的信息类型(即数据类型),我们为这个变量赋值,也就相当于在计算机中这个特殊的位置(今后我们将清楚,这个位置即所谓的地址)赋值。因为,C语言是人和计算机沟通的纽带。
在C语言中,所有变量都必须在使用之前定义。这就意味着您必须提供程序中要用到的所有变量名的列表,并且指出每个变量的数据类型。声明变量被认为是一种好的编程技术,在C语言当中必须这样做。
传统上,C语言要求必须在一个代码块的开始处声明变量,在这之前不允许任何其他语句。也就是说,main()函数如下所示:
int main() //traditional rules(传统的用法) { int year; int month; int day; year = 2014; month = 2; day = 28; //other statements(其他的语句) }
现在C99遵循C++的惯例,允许把声明放在代码块中的任何位置。然而,在首次使用变量之前仍然必须先声明它。因此,如果你的编译器支持这种功能,你的代码就可以像下面这样:
int main() //C99 rules(C99 用法) { //some statements(一些语句) int doors; doors = 5; //first use of doors(第一次使用到的变量) //more statements(更多的语句) int dogs; dogs = 3; //first use of dogs(第一次使用到的变量) //other statements (其他的语句) }
现在你可能还有三个问题。首先,数据类型是什么?第二,可以选择什么样的名字?第三,为什么必须对变量进行声明?下面来看这些问题的答案。
1. 数据类型
C语言可以处理多个数据种类(或类型),例如整数、字符和浮点数。把一个变量声明为整数类型或字符类型是计算机正确地存储,获取和解释该数据的基本前提。在后面您将学到各种各样的可用类型。
2. 变量名的选择
您应该尽量使用有意义的变量名(例如:如果你的程序需要苹果这个变量,那么使用apple)。如果名字不能表达清楚,可以用注释解释变量所代表的意思。通过这种方式使程序更易读是良好编程的基本技巧之一。
能够使用的字符的数量与C语言的不同实现有关。C99标准允许一个标识符最多可以有 63个字符,除了外部标识符,后者只识别31个字符。与C90分别要求的31个字符和6个字符相比较,这是一个相当可观的进步,而更旧的编译器通常最多只允许8个字符。实际上,你使用的字符数量可以超过规定的最大值,但是编译器不会识别额外的字符。因此,如果一个系统最大字符数为8,那么clangdot和clangdotcc将被看作是一个名字,因为它们的前8个字符相同。
可供使用的字符有小写字母,大写字母,数字和下划线。第一个字符必须是字母或者下划线。C语言的名字是区分大小写的,即把一个大写字母和与之对应的小写字母看作是不同的。因此,clang不同于Clang或者CLANG。
六. 赋值
num = 1; // =(赋值运算符)
这行程序是一个赋值语句(assignment statement)。赋值语句是C语言的基本操作之一。这个特殊的例子的意思是“把值1赋给变量num”。前面的int num;语句在计算机内存中为变量num分配了空间,该赋值语句在那个地方为变量存储了一个值。如果你想的话,以后你还可以给num赋另一个值,这就是把num称为变量的原因。可以把变量理解成是一个容器,用来盛放常量的。另外注意赋值语句赋值的顺序是从右到左。同样,该语句也用分号结束。
七. printf()函数
printf ("Hello!"); printf ("I am dotcpp.com\n"); printf ("My birthday is %d\n", num);
所有这些行都使用了C语言的一个标准函数:printf()。圆括号表明printf是一个函数名。圆括号中包含的内容是从函数main()传递到函数printf()的信息。例如,第一行把Hello!传递给printf()函数。 这样的信息被称为参数(argument),更完整的名称是函数的实际参数(actual argument)。printf()函数如何处理这个参数?程序将识别两个双引号之间的内容并把它们显示在屏幕上。
第一行printf()语句是如何在C语言中调用(call)或请求(invoke)一个函数的例子。只须键入函数的名字,把所需的一个或多个参数放进圆括号中。当程序运行到这一行时,控制权将转给该函数(在这个例子中是 printf())。当函数完成了它所要做的工作,将控制权返回给原来的函数(调用函数),在这个例子中是main()。
那么下一个printf()行呢?引号中有字符\n,但并没有输出它们!发生了什么事情呢?\n字符的意思是开始新的一行。\n组合(依次键入这两个字符)代表一个称为换行符(newline character)的字符,它意味着“在下一行的最左边开始新的一行”。换句话说,打印换行字符的效果和在普通键盘上按下回车键一样。当键入printf()这个参数时,为什么不直接按回车键呢?因为那将看作是直接针对编辑器的命令,而不是作为存在源代码中的指令。也就是说,当你按回车键时,编辑器退出你正在输入的当前行,并开始新的一行。而换行符则影响程序的输出如何显示。
换行符是转义字符(Escape Sequence)的一个例子。转义字符通常用于代表难于表达的或是无法键入的字符。其他的例子比如\t代表Tab键,\b代表退格键盘。每个转义字符都用斜线字符(\)开始。
这样就解释了三个printf()语句只产生两行输出的原因:第一个printf指令中没有换行字符,而第二个和第三个都有。最后一个printf()行中又有一个奇怪的问题:当输出这一行时%d起什么作用?回忆一下,这一行的输出结果是:
My birthday is 2014
喔!当这一行输出时,数字2014被符号组合%d代替了,而1是变量 num的值。%d是一个占位符,其作用是指出输出num值的位置。
事实上,%告诉程序把一个变量在这个位置输出,d告诉程序将输出一个十进制(以10为基数)整数变量。printf()函数允许多种输出变量格式,包括十六进制(以16为基数)整数和带小数点的数。实际上,printf()中的f暗示着这是一种格式化(format)的输出函数。每一种数据都有自己的说明符,往后会有更多的说明。
八. return 语句
return 0;
return语句(返回语句)是程序的最后一个语句。在int main(void)中int表示main()函数的返回值应该是一个整数。C标准要求main()这样做。带有返回值的C语言函数要使用一个return语句,该语句包括关键字return,后面紧跟着要返回的值,然后是一个分号。对于main()函数来说,如果你漏掉了return语句,则大多数编译器将对你的疏忽提出警告,但仍将编译该程序。此时,你可以暂时把main()中的return语句看作是保持逻辑连贯性所需的内容。但对于某些操作系统(包括DOS和UNIX)而言,它有实际的用途。
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程