头文件是扩展名为 .h 的文件,包含了 C 函数声明和宏定义,被多个源文件中引用共享。有两种类型的头文件:程序员编写的头文件和编译器自带的头文件。html
在程序中要使用头文件,须要使用 C 预处理指令 #include 来引用它。前面咱们已经看过 stdio.h 头文件,它是编译器自带的头文件。程序员
引用头文件至关于复制头文件的内容,可是咱们不会直接在源文件中复制头文件的内容,由于这么作很容易出错,特别在程序是由多个源文件组成的时候。express
A simple practice in C 或 C++ 程序中,建议把全部的常量、宏、系统全局变量和函数原型写在头文件中,在须要的时候随时引用这些头文件。编程
使用预处理指令 #include 能够引用用户和系统头文件。它的形式有如下两种:编程语言
#include <file>
这种形式用于引用系统头文件。它在系统目录的标准列表中搜索名为 file 的文件。在编译源代码时,您能够经过 -I 选项把目录前置在该列表前。ide
#include "file"
这种形式用于引用用户头文件。它在包含当前文件的目录中搜索名为 file 的文件。在编译源代码时,您能够经过 -I 选项把目录前置在该列表前。函数
#include 指令会指示 C 预处理器浏览指定的文件做为输入。预处理器的输出包含了已经生成的输出,被引用文件生成的输出以及 #include 指令以后的文本输出。例如,若是您有一个头文件 header.h,以下:操作系统
char *test (void);
和一个使用了头文件的主程序 program.c,以下:指针
int x; #include "header.h" int main (void) { puts (test ()); }
编译器会看到以下的代码信息:code
int x; char *test (void); int main (void) { puts (test ()); }
若是一个头文件被引用两次,编译器会处理两次头文件的内容,这将产生错误。为了防止这种状况,标准的作法是把文件的整个内容放在条件编译语句中,以下:
// 若是HEADER_FILE不存在 #ifndef HEADER_FILE #define HEADER_FILE the entire header file file #endif
这种结构就是一般所说的包装器 #ifndef。当再次引用头文件时,条件为假,由于 HEADER_FILE 已定义。此时,预处理器会跳过文件的整个内容,编译器会忽略它。
有时须要从多个不一样的头文件中选择一个引用到程序中。例如,须要指定在不一样的操做系统上使用的配置参数。您能够经过一系列条件来实现这点,以下:
#if SYSTEM_1 # include "system_1.h" #elif SYSTEM_2 # include "system_2.h" #elif SYSTEM_3 ... #endif
可是若是头文件比较多的时候,这么作是很不稳当的,预处理器使用宏来定义头文件的名称。这就是所谓的有条件引用。它不是用头文件的名称做为 #include 的直接参数,您只须要使用宏名称代替便可:
#define SYSTEM_H "system_1.h" ... #include SYSTEM_H
SYSTEM_H 会扩展,预处理器会查找 system_1.h,就像 #include 最初编写的那样。SYSTEM_H 可经过 -D 选项被您的 Makefile 定义。
强制类型转换是把变量从一种类型转换为另外一种数据类型。例如,若是您想存储一个 long 类型的值到一个简单的整型中,您须要把 long 类型强制转换为 int 类型。您可使用强制类型转换运算符来把值显式地从一种类型转换为另外一种类型,以下所示:
(type_name) expression
请看下面的实例,使用强制类型转换运算符把一个整数变量除以另外一个整数变量,获得一个浮点数:
#include <stdio.h> int main() { int sum = 17, count = 5; double mean; mean = (double) sum / count; printf("Value of mean : %f\n", mean ); }
当上面的代码被编译和执行时,它会产生下列结果:
Value of mean : 3.400000
这里要注意的是强制类型转换运算符的优先级大于除法,所以 sum 的值首先被转换为 double 型,而后除以 count,获得一个类型为 double 的值。
类型转换能够是隐式的,由编译器自动执行,也能够是显式的,经过使用强制类型转换运算符来指定。在编程时,有须要类型转换的时候都用上强制类型转换运算符,是一种良好的编程习惯。
整数提高是指把小于 int 或 unsigned int 的整数类型转换为 int 或 unsigned int 的过程。请看下面的实例,在 int 中添加一个字符:
#include <stdio.h> int main() { int i = 17; char c = 'c'; /* ascii 值是 99 */ int sum; sum = i + c; printf("Value of sum : %d\n", sum ); }
当上面的代码被编译和执行时,它会产生下列结果:
Value of sum : 116
在这里,sum 的值为 116,由于编译器进行了整数提高,在执行实际加法运算时,把 'c' 的值转换为对应的 ascii 值。
经常使用的算术转换是隐式地把值强制转换为相同的类型。编译器首先执行整数提高,若是操做数类型不一样,则它们会被转换为下列层次中出现的最高层次的类型:
经常使用的算术转换不适用于赋值运算符、逻辑运算符 && 和 ||。让咱们看看下面的实例来理解这个概念:
#include <stdio.h> int main() { int i = 17; char c = 'c'; /* ascii 值是 99 */ float sum; sum = i + c; printf("Value of sum : %f\n", sum ); }
当上面的代码被编译和执行时,它会产生下列结果:
Value of sum : 116.000000
在这里,c 首先被转换为整数,可是因为最后的值是 float 型的,因此会应用经常使用的算术转换,编译器会把 i 和 c 转换为浮点型,并把它们相加获得一个浮点数。
C 语言不提供对错误处理的直接支持,可是做为一种系统编程语言,它以返回值的形式容许您访问底层数据。在发生错误时,大多数的 C 或 UNIX 函数调用返回 1 或 NULL,同时会设置一个错误代码 errno,该错误代码是全局变量,表示在函数调用期间发生了错误。您能够在 errno.h 头文件中找到各类各样的错误代码。
因此,C 程序员能够经过检查返回值,而后根据返回值决定采起哪一种适当的动做。开发人员应该在程序初始化时,把 errno 设置为 0,这是一种良好的编程习惯。0 值表示程序中没有错误。
C 语言提供了 perror() 和 strerror() 函数来显示与 errno 相关的文本消息。
让咱们来模拟一种错误状况,尝试打开一个不存在的文件。您可使用多种方式来输出错误消息,在这里咱们使用函数来演示用法。另外有一点须要注意,您应该使用 stderr 文件流来输出全部的错误。
#include <stdio.h> #include <errno.h> #include <string.h> extern int errno ; int main () { FILE * pf; int errnum; pf = fopen ("unexist.txt", "rb"); if (pf == NULL) { errnum = errno; fprintf(stderr, "错误号: %d\n", errno); perror("经过 perror 输出错误"); fprintf(stderr, "打开文件错误: %s\n", strerror( errnum )); } else { fclose (pf); } return 0; }
当上面的代码被编译和执行时,它会产生下列结果:
错误号: 2
经过 perror 输出错误: No such file or directory
打开文件错误: No such file or directory
在进行除法运算时,若是不检查除数是否为零,则会致使一个运行时错误。
为了不这种状况发生,下面的代码在进行除法运算前会先检查除数是否为零:
#include <stdio.h> #include <stdlib.h> int main() { int dividend = 20; int divisor = 0; int quotient; if( divisor == 0){ fprintf(stderr, "除数为 0 退出运行...\n"); exit(-1); } quotient = dividend / divisor; fprintf(stderr, "quotient 变量的值为 : %d\n", quotient ); exit(0); }
当上面的代码被编译和执行时,它会产生下列结果:
除数为 0 退出运行...
一般状况下,程序成功执行完一个操做正常退出的时候会带有值 EXIT_SUCCESS。在这里,EXIT_SUCCESS 是宏,它被定义为 0。
若是程序中存在一种错误状况,当您退出程序时,会带有状态值 EXIT_FAILURE,被定义为 -1。因此,上面的程序能够写成:
#include <stdio.h> #include <stdlib.h> int main() { int dividend = 20; int divisor = 5; int quotient; if( divisor == 0){ fprintf(stderr, "除数为 0 退出运行...\n"); exit(EXIT_FAILURE); } quotient = dividend / divisor; fprintf(stderr, "quotient 变量的值为: %d\n", quotient ); exit(EXIT_SUCCESS); }
当上面的代码被编译和执行时,它会产生下列结果:
quotient 变量的值为 : 4
递归指的是在函数的定义中使用函数自身的方法。
语法格式以下:
void recursion() { statements; ... ... ... recursion(); /* 函数调用自身 */ ... ... ... } int main() { recursion(); }
流程图:
C 语言支持递归,即一个函数能够调用其自身。但在使用递归时,程序员须要注意定义一个从函数退出的条件,不然会进入死循环。
递归函数在解决许多数学问题上起了相当重要的做用,好比计算一个数的阶乘、生成斐波那契数列,等等。
下面的实例使用递归函数计算一个给定的数的阶乘:
#include <stdio.h> double factorial(unsigned int i) { if(i <= 1) { return 1; } return i * factorial(i - 1); } int main() { int i = 15; printf("%d 的阶乘为 %f\n", i, factorial(i)); return 0; }
当上面的代码被编译和执行时,它会产生下列结果:
15 的阶乘为 1307674368000.000000
下面的实例使用递归函数生成一个给定的数的斐波那契数列:
#include <stdio.h> int fibonaci(int i) { if(i == 0) { return 0; } if(i == 1) { return 1; } return fibonaci(i-1) + fibonaci(i-2); } int main() { int i; for (i = 0; i < 10; i++) { printf("%d\t\n", fibonaci(i)); } return 0; }
当上面的代码被编译和执行时,它会产生下列结果:
0
1
1
2
3
5
8
13
21
34