C语言从入门到精通:C程序的组成(c程序的基本组成)
一个C程序可以由若干个源程序文件组成,每一个源文件可以由若干个函数和预处理命令以及全局变量声明部分组成,每一个函数由函数首部和函数体组成。c程序的结构如图所示。
作为一名程序开发人员,不可能每次编写都从最底层开发。比如在上例中,要输入一串字符到输出设备上,我们需要做的仅是调用printf()函数,至于"Hello C! "是怎样显示的,我们并不关心。我们认识printf()函数,在编写程序时调用它,更需要让程序认识它,这样才能使用printf()函数提供的功能,这就需要使用#include <stdio.h>,包含标准输入输出头文件,这样程序就能够认识printf()函数,并执行其功能。
C提供有丰富的函数集,我们称之为标准函数库。标准函数库包括15个头文件,借助这些函数可以完成不同的功能。
例如, 【范例2-1】中有#include<math.h>时,就可以使用该数学函数库头文件提供的如开平方函数sqrt(),求出半径radius为2时的开平方值1.414;又如,当程序包含头文件"malloc.h"时,就可以完成对内存申请和释放等功能。
2.2.2函数声明
标准C语言引入了新的更好的函数声明方法,即用函数原型指定函数更多的信息,通过函数原型可以将函数的名字和函数类型以及形式参数的个数、类型、顺序通知编译系统,以便在调用函数时,系统可以对照检查。
函数声明由函数返回类型、函数名和形参列表组成。形参列表必须包括形参类型,但是不必对形参命名。这3个元素被称为函数原型,函数原型描述了函数的接口。定义函数的程序员提供函数原型,使用函数的程序员就只需要对函数原型编辑即可。
函数声明的一般形式为:
函数返回类型函数名(参数类型1,参数类型2,……);
函数声明包括函数的返回类型和函数名,来看下面这个例子。
01 int fun (int a, int b);02 void display();03 float fn (float x);
其中, int, void和float都是函数返回类型,也属于数据类型。fun, display和fn是所调用的函数名。也就是说, fun()函数返回的数据类型为整型int; display()函数返回的类型为void型,指函数无返回值; fn()返回的数据类型为浮点型float。
fun (int a, int b)数内部的a和b为形参,其参数类型都为int型。fn (float x )函数内部参数x的类型为float型。函数声明中的形参名往往被忽略,如果声明中提供了形参的名字,也只是用作辅助文档。另外要注意函数声明是一个语句,后面不可漏分号!
进一步对函数做解释,需要注意的有以下几点。
(1)函数名称后面必须有小括号,不能省略,这是函数的特征。
(2)函数结束必须有分号,不能省略。
(3)字符串结尾有这样的内容"n" ,它叫做转义符,表示的合义是把光标移动到下一行的行首,也就是回车换行,因为我们无法直接通过键盘输入换行的指令,所以需要使用转义符;又比如输出内容后希望返回该行的行首,重新输出内容,键盘上也没有对应的功能键,我们就可以使用回车符转义符"r"来代替。当然,转义还包含其他内容,后续拿节中会详细讲达。
2.2.3变量声明
在大多数语言中,在使用一个变量之前,都要对这个变量进行声明, C语言同样如此。那么,什么是变量的声明呢?有什么作用呢?变量的声明其实就是在程序运行前,告诉编译器程序使用的变量以及与这些变量相关的属性,包括变量的名称、类型和长度等。这样,在程序运行前,编译器就可以知道怎样给变量分配内存空间,可以优化程序。
变量的声明语句的形式如下:
变量类型名变量名
变量的声明包括变量类型名和变量名两个部分。来看下面的例子:
01 int num02 double area03 char ppt
其中, int、double和char是变量类型名, num、area和ppt是变量名。其实,变量类型名也是数据类型的一种,就是说变量num是int类型, area是double类型, ppt是char类型。
变量类型名是C语言自带的数据类型和用户自定义的数据类型。C语言自带的数据类型包括整型、字符型、浮点型、枚举型和指针类型等。
变量名其实就是一个标识符,当然,标识符的命名规则在此处同样适用。除此之外,变量命名的时候还需要注意以下几点。
(1) 变量名区分大小写,变量Num和num是两个不同的变量。
(2) 变量的命名最好与实际应用有关联,例如: num一般表示数量, area表示面积等。
(3)变量的命名必须在变量使用之前。
提示
如果变量没有经过声明而直接使用,则会出现编译器报错的现象。
下面用一个例子来验证声明必须在变量使用的前面。
【范例2-2】验证未声明的标识符不可用。
(1) 在Visual C 6.0中,新建名为"undeclaredvar.c"的【Text File】文件。
(2)在编辑窗口中输入以下代码(代码2-2.txt) 。
01 #include<stdio.h>02 int main(void)03{04 print ("output undeclaredvar num-%dn",num);05 return 0:06}
【运行结果】
编译后显示出错,信息如下:
undeclaredvar.c(4): error C2065: ‘num': undeclared identifier
【范例分析】在此例子中,没有对标识符num进行声明就直接引用,编译器不知道num是什么,所以调试时编译器就会报错。
【拓展训练】
在第3句和第4句之间插入语句:
04 int num=10;
或者:
04 int num;05 num=10;
检验一下程序能否运行,是否还报错。
2.3主函数
每个C程序必须有而且只有一个主函数,也就是main()函数,它是程序的入口。main()函数有时也作为-种驱动,按次序控制调用其他函数, C程序是由函数构成的,这使得程序容易实现模块化; main()函数后面的"()"不可省略,表示函数的参数列表; "{"和"}"是函数开始和结束的标志,不可省略。
下图是对主函数调用其他函数的说明。
主函数main()在程序中可以放在任何位置,但是编译器都会首先找到它,并从它开始运行。它就像汽车的引擎,控制程序中各部分的执行次序。下图是对主函数各部分名称的说明。
在前面的两个范例中,主函数main()的部首都是int类型, int是整数integer单词的缩写,表示返回给系统的数据类型是整型数据,返回值是0,在return句中体现了出来。
2.4函数定义区
C语言编译系统是由上往下编译的。一般被调函数放在主调函数后面时,前面就该有声明,不然C语言由上往下的编译系统将无法识别。正如变量必须先声明后使用一样,函数也必须在被调用之前先声明,否则无法调用!函数的声明可以与定义分离,要注意的是一个函数只能被定义一次,但可以声明多次。
函数定义:
返回类型函数名(参数类型1参数名1,…,参数类型n参数名n)
{函数体...}例如int fun(int a,int b){int c;c=a b;return c;}
在程序中,如果main()函数在前,必须在mian()中写函数声明;如果函数在main()前面,可以不在main()中写函数声明!
下面看一个有关函数声明和函数定义的综合例子,比较一下它们有何不同。
#include<stdio.h>int add(int x,int y);//函数声明语句Main(){int a,b,c;c=add(a,b);printf("%d",c);}Int add(int x,int y)//函数定义{int z;z=x y;return z;}
2.5注程
读者可能已经注意到,很多语句后面都跟有"/*"和"*/"符号,它们表示什么含义呢?
在前文已经说过,我们在编辑代码的过程中,希望加上一些说明的文字,来表示代码的含义,这是很有必要的。
费了很大精力,绞尽脑汁编写的代码,如果没有写注释或者注释得不够清楚,一年后又要使用这段代码时,当年的思路全部记不得了,无奈之中,只得重分析、重理解。试问,因为当初一时的懒散造成了今日的结局,值得吗?又比如,一个小组共同开发程序,别人需要在该小组写的代码上进行二次开发,如果代码很复杂、没有注释,恐怕只能用4个字形容组员此时的心情:欲哭无泪。所以,编写代码时最好书写注释,这样做有百利而无一弊。
注释的要求如下。
(1)使用"/*"和"*/"表示注释的起止,注释内容写在这两个符号之间,注释表示对某语句的说明,不属于程序代码的范畴,比如【范例1-1】和【范例2-1】代码中"/*"和"*/"之间的内容。
(2) "/"和"*"之间没有空格。
(3)注释可以注释单行,也可以注释多行,而且注释不允许嵌套,嵌套会产生错误,比如:
/*这样的注释/*特别*/有用*/
这段注释放在程序中不但起不到说明的作用,反而会使程序产生错觉,原因是“这样”前面
的"/*"与“特别”后面的"*/"匹配,注释结束,而“有用*/"就被编译器认为是违反语法规则的代码。
2.6代码的规矩
从书写代码清晰,便于阅读、理解、维护的角度出发,在书写程序时应遵循以下规则。
(1)一个说明或一个语句占一行。我们把空格符、制表符、换行符等统称为空白符。除了字符串、函数名和关键字, C忽略所有的空白符,在其他地方出现时,只起间隔作用,编译程序对它们忽略不计。因此在程序中使用空白符与否,对程序的编译不产生影响,但在程序中适当的地方使用空白符,可以增加程序的清晰性和可读性。
例如下面的代码:
intmain(){printf( "Hello C!n”);}/*这样的写法也能运行,但是太乱,很不妥*/
(2)用“{"和"}"括起来的部分,通常表示程序某一层次的结构。"{"和“}"一般与该结构语句的第1个字母对齐,并单独占一行。
例如下面的代码:
int main(){printf( "Hello C!n");return 0;}/*这样的写法也能运行,但是阅读起来比较费事*/
(3)低一层次的语句通常比高一层次的语句留有一个缩进后再书写。一般来说,缩进指的是存在两个空格或者一个制表符的空白位置。
例如下面的代码:
int main(){print( "Hello C!n" );{printf( "Hello C!n" );}return 0;}
(3) 在程序中书写注释,用于说明程序做了什么,同样可以增加程序的清晰性和可读性。
(4) 以上介绍的4点规则,大家在编程时应力求遵循,以养成良好的编程习惯。
2.7高手点拨
文件中声明函数,就像变量可以在头文件中声明,而在源文件中定义一样,函数也可以在头文件中声明,在源文件中定义。把函数声明直接放在每个使用该函数的源文件中是大多数新手习惯并喜爱的方式,这是合法的。但是这种方式古板且易出错。解决方法就是把函数的声明放在头文件中,这样可以确保指定函数的所有声明保持一致。如果函数接口发生变化,则只需修改其唯一的声明即可。
将提供函数声明的头文件包含在定义该函数的源文件中,可使编译器能检查该函数的定义和声明是否一致。特别地,如果函数定义和函数声明的形参列表一致,但返回类型不一致,编译器会发出警告或出错信息来指出差异。
那么学过了函数声明与函数定义,它们到底有什么不同呢?我们知道函数的定义是一个完整的函数单元,它包含函数类型、函数名、形参及形参类型、函数体等,并且在程序中,函数的定义只能有一次,函数首部与花括号间也不加分号。而函数声明只是对定义函数的返回值类型进行说明,以通知系统在本函数中所调用的函数是什么类型。它不包含函数体,并且调用几次该函数就应在各个主调函数中作相应声明,函数声明是一个说明话句,必须以分号结束!
学习了这一堂的内容,读者是不是对C语言有更好的理解呢?下面了解一下我们在使用函数时,需要注意的一些问题。
1、函数声明可以省略形参名,但是函数定义的首部必须写出所有形参名并给出其对应的数据类型。
2、函数原型的主要目的是为了声明函数返回值类型以及函数期望接受的参数的个数、参数类型和参数顺序。
3、如果程序中没有某个函数的函数原型(没有说明) ,编译系统就会用第一次出现的这个函数(函数定义或函数调用)构造函数原型。
4、在默认下,编译系统默认函数返回值为int。
在编写函数时的常见错误:当调用的函数与函数原型不相匹配时,程序会提示语法错误,并且当函数原型和函数定义不一致时,也会产生错误。
本文节选自《C语言从入门到精通(第2版)》
本书面向C语言的零基础读者,介绍基础入门知识和实际操作技巧。全书紧贴软件开发的实际需求,首先向读者展示语言的背景知识和应用范围,之后通过实例和自测,系统讲解相关知识点,同时兼顾实际开发项目经验。为了增强实用性,光盘中还特别赠送了精选的考试认证、求职面试等题库,供读者研究学习。最后,为帮助初入职场的从业者顺利进入角色,还在光盘中赠送了职业规划建议及相关开发文档。