C程序的翻译环境与执行环境。
翻译环境指的是将源文件转换成可执行的机器指令的环境,执行环境用于实际执行代码。
在翻译环境中,每一个源文件通过编译被转换为目标代码(obj),目标文件通过链接器捆绑在一起
,形成一个单一的可执行程序,链接器同时也将函数库中的函数链接到程序中。
编译分为 预编译(预处理),编译,汇编三个阶段。
在预编译阶段,把包含的头文件代码放入源文件中,进行define定义的符号的替换,以及注释的删除。
在编译阶段,通过语法分析,语义分析,词法分析,符号汇总把C语言代码翻译为汇编语言代码。
在汇编阶段,把汇编语言代码翻译为机器语言。
预处理指令
//这些预定义符号都是语言内置的。
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
#define
使用define定义表示符
#define name stuff
#define定义标识符时,最好不要加;
使用#define允许把参数替换到文本中,这种定义被称为宏定义。
例如
#define SQUARE( x ) x * x
但是用于对数值表达式进行求值的宏定义都应该加上适当的括号,避免在使用宏时由于参数中的操
作符或邻近操作符之间不可预料的相互作用。
#define的替换规则
1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首
先被替换。
2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
注意:
1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
宏的优劣:
1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。
所以宏比函数在程序的规模和速度方面更胜一筹。
2. 函数的参数必须声明为特定的类型。 所以函数只能在类型合适的表达式上使用。反之宏怎
可以适用于整形、长整型、浮点型等可以,用于来比较的类型。 宏是类型无关的。
宏的缺点:
1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
2. 宏是没法调试的。
3. 宏由于类型无关,也就不够严谨。
4. 宏可能会带来运算符优先级的问题,导致程容易出现错。
条件编译
1.
#if 常量表达式
//
#endif
//常量表达式由预处理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__
//..
#endif
2.多个分支的条件编译
#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif
3.判断是否被定义
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
4.嵌套指令
#if defined(OS_UNIX)
#ifdef OPTION1
unix_version_option1();
#endif
#ifdef OPTION2
unix_version_option2();
#endif
#elif defined(OS_MSDOS)
#ifdef OPTION2
msdos_version_option2();
#endif
#endif
头文件的包含
本地文件包含:
#include "filename"
查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样
在标准位置查找头文件。 如果找不到就提示编译错误。
库文件包含:
#include <filename.h>
查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。
如何使用 条件编译 避免嵌套包含头文件的重复引用,提高程序执行效率。
每个头文件开头写
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif //__TEST_H__
或者
#pragma once