函数定义形式:
返回值类型函数明(参数声明表)
{
声明和语句
}
函数定义中的各构成部分都可以省略。
不执行任何操作的函数有时很有用,它可以在程序开发期间用以保留位置(留待以后填充代码)。如果函数定义中省略了返回值类型,则默认为int类型。
程序可以看成是变量定义和函数定义的集台。函数之间的通信可以通过参数、函数返回值以及外部变量进行。
被调用函数通过return语句向调用者返回值,return语句的后面可以跟任何表达式:
return 表达式;
如果函数带有参数,则要声明它们;如果没有参数,则使用void进行声明。函数声明和定义的参数类型一定要对应。
C语言程序可以看成由一系列的外部对象构成,这些外部对象可能是变量或函数。
默认情况下,外部变量与函数具有下列性质:通过同一个名字对外部变量的所有引用(即使这种引用来自于单独编译的不同函数)实际上都是引用同一个对象(标准中把这一性质称为外部链接)。
因为外部变量可以在全局范围内访问,这就为函数之间的数据交换提供了一种可以代替函数参数与返回值的方式。任何函数都可以通过名字访问一个外部变量,当然这个名字需要通过某种方式进行声明。
如果函数之间需要其享大量的变量,使用外部变量要比使用一个很长的参数表更方便、有效。
int sp;
double val[MAXVAL];
extern int sp;
extern double val[];
在一个源程序的所有源文件中,一个外部变量只能在某个文件中定义一次,而其它文件可以通extern声明来访问它(定义外部变量的源文件中也可以包含对该外部变量的extern 声明)。外部变量的定义中必须指定数组的长度,extern声明则不一定要指定数组的长度。
外部变量的初始化只能出现在其定义中。
对于某些中等规模的程序,最好只用一个头文件存放程序中各部分共享的对象。较大的程序需要使用更多的头文件,我们需要精心地组织它们。
与extern关键字是冲突的。
static声明限定外部变量与函数,可以将其后声明的对象的作用域限定为被编译源文件的剩余部分。通static限定外部对象,可以达到隐藏外部对象的目的。
register声明告诉编译器,它所声明的变量在程序中使用频率较高。其思想是,将register变量放在机器的寄存器中,这样可以使程序更小、执行速度更快。但编译器可以忽略此选项。
registe 声明只适用于自动变量以及函数的形式参数。声明形式:
register int x;
register char c;
f(register unsigned m, register long n)
{
register int i;
}
另外,无论寄存器变量实际上是不是存放在寄存器中,它的地址都是不能访问的。
自动变量(包括形式参数)也可以隐藏同名的外部变量与函数。如
int x;
int y;
f(double x)
{
double y;
}
在一个好的程序设计风格中,应该避免出现变量名隐藏外部作用域中相同名字的情况,否则,很可能引起混乱和错误。
int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
如果初始化表达式的个数比数组元索数少,则对外部变量、静态变量和自动变量来说,没有初始化表达式的元素将被初始化为0,如果初始化表达式的个数比数组元素数多,则是错误的。
char pattern[] = "ould ";
char pattern[] = { 'o', 'u', 'l', 'd'}; //这种情况下,数组的长度个字符加上一个字符串结束符'\0'
语言中的函数可以递归调用,即函数可以直接或间接调用自身。
递归并不节省存储器的开销,因为递归调用过程中必须在某个地方维护一个存储处理值的栈。递归的执行速度并不快,但递归代码比较紧凑,并且比相应的非递归代码更易于编写与理解。在描述树等递归定义的数据结构时使用递归尤其方便。
每个递归函数都有两个部分:基线条件和递归条件。递归条件指的是函数调用自身,而基线条件则指的是函数不再调用自身,从而避免无限循环。
预处理器是编译过程中单独执行的第一个步骤。
#include "文件名"
#include <文件名>
有上述文件包含的行都将被替换为由文件名指定的文件的内容。如果文件名用引号引起来,则在源文件所在位置查找该文件;如果在该位置没有找到文件,或者如果文件名用尖括号括起来,则将根据相应的规则查找该文件。
源文件的开始处通常都会有多个#include指令,它们用以包含常见的#define语句和extern声明,或从头文件中访问库函数的函数原型声明,比如
在大的程序中,#include指令是将所有声明捆绑在一起的较好的方法。它保证所有的源文件都具有相同的定义与变量声明,这样可以避免出现一些不必要的错误。很自然,如果某个包含文件的内容发生了变化,那么所有依赖于该包含文件的源文件都必须重新编译。
#define 名字 替换文本
后续所有出现名字记号的地方都将被替换为替换文本。
如果在替换文本中,参数名以#作为前缀则结果将被扩展为由实际参数替换该参数的带引号的字符串。
#define dprint(expr) printf(#expr " = %g\n", expr)
预处理器运算符##为宏扩展提供了一种连接实际参数的手段。如果替换文本中的参数与##相邻,则该参数将被实际参数替换,##与前后的空白符将被删除,并对替换后的结果重新扫描。
#define paste(front, back) front ## back
#if
#endif
#elif
#else
#ifndef
#define
还可以使用条件语句对预处理本身进行控制,这种条件语句的值是在预处理执行的过程中进行计算。这种方式为在编译过程中根据计算所得的条件值选择性地包含不同代码提供了一种手段。
如果多个头文件能够一致地使用这种方式,那么,每个头文件都可以将它所依赖的任何头文件包含进来,用户不必考虑和处理头文件之间的各种依赖关系。
常用为了保证文件的内容只被包含过一次,将文件的内容包含在以下形式的条件语句中。
#ifndef HDR
#define EDR
/* hdr.文件的内容放在这*/
#endif