二.三C语言的组成【C语言的组成】
前言
一个C程序必须有且只有一个main.c主函数,C程序总是从主函数开始执行,主函数可以调用其他函数,而不允许被其他函数调用。
一个复杂的C程序可包含若干个源程序文件(.c文件、.h文件等),而每个源程序文件由预处理命令(宏定义、文件包含、条件编译等)、数据声明(变量声明,函数等声明)以及一个主函数和若干函数组成。
如下所示:
预处理部分【宏定义(#define),文件包含(#include),条件编译(#ifdef,#undef,#if)】
数据声明部分【函数声明和全局变量】
-------------------------------------------------------------------------------------------------------------------------
以上为声明部分,以下为部分均为执行语句
-------------------------------------------------------------------------------------------------------------------------
main()
{
局部变量定义
语句序列(二.一和二.二所提结构)
}
-------------------------------------------------------------------------------------------------------------------------
以上为主函数部分
-------------------------------------------------------------------------------------------------------------------------
全局变量定义(可有可不有,函数外都算全局变量,详细后续章节讲解)
sub1(形式参数表)
{
局部变量定义
语句序列(二.一和二.二所提结构)
}
......
subn(形式参数表)
{
局部变量定义
语句序列(二.一和二.二所提结构)
}
上述为C语言整体结构大体框架。下面详细介绍各部分。
1.预处理
预处理是C语言的一个重要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分做处理,处理完毕自动进入对源程序的编译。预处理命令不是C语句,为了与一般C语句相区别,这些命令均以符号“#”开头,末尾不加“;”。
初学者接触的代码少,结构和模块较为简单,所以暂时会感受不到预处理的作用。
1.1文件包含
可将一些公用的宏定义,定义,等单独组成一个文件,在其他文件的开头用包含命令包含该文件即可使用,这样可以避免在每个文件开头都去书写那些公用量。
#include <文件名>系统到存放C库函数头文件的“包含目录”中查找要包含的文件,这称为标准方式。一般用于库函数。
#include“文件名”系统先在“用户当前的源文件目录”中查找要包含的头文件;若找不到,再到“包含目录”中去查找。 一般用于用户自己编写的文件,若不在当前目录中,则在双撇号内给出文件路径。#include“D:\zhang\file1.c”
<>:从安装路径开始找起,再到该项目目录。
“”:直接从项目目录找起。
1.2宏定义
不带参数的宏定义
#define 宏名 仅说明宏名对应的标识符被定义。
#define 宏名 替换文本 用一个简单且见名知意的“宏名”代表“替换文本”,“替换文本”可以常数、表达式、格式串等。
带参数的宏定义
带参数的宏定义,简单理解就是原封不动的照抄照搬。
#define MU(X,Y) ((X)*(Y))
b=6/MU(a+3,a);→b=6/((a+3)*(a))
先照抄即6/((X)*(Y)),再把对应的X和Y的值带入,即6/((a+3)*(a))
#define MU(X,Y) X*Y
b=6/MU(a+3,a);→b=6/a+3*a
先照抄6/X*Y,再把对应的X和Y的值带入,即6/a+3*a
#define MU(X,Y) (X*Y)
b=6/MU(a+3,a);→b=6/(a+3*a)
先照抄6/(X*Y),再把对应的X和Y的值带入,即6/(a+3*a)
宏替换和函数调用有相似指出,但是在宏替换中,对参数没有类型的要求。
终止宏定义
#define PI 3.14
#undef PI
限定PI的作用域从#define开始到#undef结束
1.3条件编译
#ifdef 标识符
程序段1
#else
程序段2
#endif
若所指定的标识符已被#define命令定义过,则对程序段1进行编译,否则对程序段2进行编译,可以没有程序段2。
#ifndef 标识符
程序段1
#else
程序段2
#endif
与上述相反,定义改为未定义。
#if 常量表达式
程序段1
#else
程序段2
#endif
#if 常量表达式1
程序段1
#elfi 常量表达式2
程序段2
#elif 常量表达式3
程序段3
.
.
.
elif 常量表达式n
程序段n
#endif
该方式与if的选择语句相似,判断#if后的常量表达式是否成立,若成立则执行相应的程序段。
2.数据声明
函数和变量的声明必须在执行语句之前(不同编译器,要求不一样,越接近目前的越没有严格的要求)
3.函数
一个C语言源程序无论包含了多少函数,在正常情况下总是从main函数开始执行,main函数结束。一个C语言程序有且仅有一个main函数。
3.1函数分类
库函数
由C语言编译系统提供,用户不用定义,只要在源文件中包含库函数对应的头文件,即可在程序中直接调用库函数。应当说明,不同的C语言编译系统提供的库函数的数量和功能不尽相同。在程序设计中,可通过网络等手段查询了解库函数,并加以应用。
用户自定义函数
用户根据需要,将实现某个功能的代码编写成相对独立的函数。
3.2函数声明
函数定义的位置要在调用这个函数前面,也可以在最前面先进行函数声明。
函数的声明是把函数的名字、函数类型以及形参的类型、个数和顺序通知编译系统,以便调用函数时系统进行对照检查。函数的声明后面要加分号。
如果该函数在本文件内部,则声明时不用extern修饰,如果声明的函数不在本文件内部,而在另一个文件中,声明时前面必须带extern,指明使用的函数在另一个文件中。(详细内容后续extern章节介绍)
3.3定义函数
【存储分类】<函数类型><函数名称>(<类型名形式参数……>)
{
语句序列;
return(语句表达式);
}
存储分类说明函数存储位置,关乎后续函数是否能被其他文件调用(分为static和extern,可以省略说明,省略时默认为extern)
注意此处提到的extern为定义函数时候;前文所提的是定义函数的时候,前文不可省略。
函数类型说明函数返回值的类型,若省略了函数类说明则默认为int,若函数只是用于完成某些操作,没有函数值返回,则必须把函数定义成void类型。return的返回值要与函数类型一致。
函数名称和形式参数都是由用户命名的标识符。在同一程序中,函数名必须唯一,形式参数名字只要在同一函数中唯一即可,可以与其他函数中的变量同名。
形式参数表用于录入主函数与被调用函数之间进行数据传递的形式参数。
3.4函数调用
函数的一般调用形式为
某变量=函数名(实际参数表);
若函数无形参,调用形式为
函数名();
函数调用的时候,前面不需要任何的存储类别说明(static和extern)以及数据类型说明
一般情况下,可用两种方式调用函数
(1)当调用的函数用于求出某个值的时候,函数的调用可作为值表示出现在允许表达式出现的任何地方。
(2)C语言中的函数可以仅进行某些操作而不返回函数值,这时函数的调用可作为一条独立的语句。
调用函数时的语法要求
(1)调用函数时,函数名必须与所调用的函数名字完全一致。
(2)实际参数的个数必须与形式参数的个数一致。实残酷与式表达式,在类型上应按位置与形参一一对应匹配。如果不匹配,C语言编译程序时会按规则转换,若不能赋值兼容,程序通常依旧能执行且不报错,只是不会得到正常的结果。
(3)C语言规定:函数必须先定义,后调用(函数的返回值类型为int或char时除外)(即顺序上要在调用前面,也可以先在开头进行函数声明)
(4)C语言程序中,函数可以直接或间接地自己调用自己,称为递归调用。
3.5调用函数和被调用函数之间的数据传递
C语言中,调用函数和被调用函数之间的数据可以通过三种方式进行传递:
(1)实际参数和形式参数之间进行数据传递。
(2)通过return语句把函数值返回调用函数。
(3)通过全局变量。但这不是一种好的方式,通常不提倡使用。
在C语言中,数据只能从实参单项传递给形参,称为“按值”传递。也即是说,当简单变量作为实参时,用户不可能在函数中改变对应实参的值。本质是因为形式参数和实际参数都会各自占用一个内存空间。
用按地址进行转递在函数中改变对应实参的值,后续指针介绍。
