一、程序的翻译环境和执行环境
- 第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。
- 第2种是执行环境,它用于实际执行代码。
二、详解编译与链接
三、预处理详解
3.1 #define定义宏
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。
下面是宏的申明方式:
#define name( parament-list ) stuff
其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中。
#include <stdio.h>
#define POW(x) x*x
int main()
{
int n = 5;
int sum = POW(5 + 1);
printf("%d", sum);//11 注意 是把5+1替换到x里面去了
return 0;
}
3.2 #和##
一个#,把一个宏参数变成相应的字符串
#include <stdio.h>
#define PRINT(FORMAT,VALUE) printf("the value of " #VALUE " is "FORMAT "\n", VALUE)
int main()
{
int i = 10;
PRINT("%d", i + 3);
return 0;
}
//the value of i + 3 is 13
两个##,把两边符号合成一个符号
#include <stdio.h>
#define ADD_TO_SUM(num,value) sum##num += value;
int main()
{
int sum5 = 10;
int sum = ADD_TO_SUM(5, 10);
printf("%d ", sum);//20
return 0;
}
3.3 带副作用的宏参数
当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。
例子
#include <stdio.h>
#define MAX(a,b) ((a)>(b)?(a):(b))
int main()
{
int x = 5;
int y = 4;
int num = MAX(x++, y++);
printf("num=%d a=%d b=%d", num, x, y);//num=6 x=7 y=5
return 0;
}
四、宏和函数的对比
属性 | #define定义宏 | 函数 |
---|---|---|
代码长度 | 每次使用时,宏代码都会被插入到程序中。除了非常小的宏之外,程序的长度会大幅度增长 | 函数代码只出现于一个地方;每次使用这个函数时,都调用那个地方的同一份代码 |
执行速度 | 更快 | 存在函数的调用和返回的额外开销,所以相对慢一些 |
操作符的优先级 | 宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写时候多些括号。 | 函数参数只在函数调用的时候求值一次,它的结果值传递给函数。表达式的求值结果更容易预测 |
带有副作用的参数 | 参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果。 | 函数参数只在传参的时候求值一次,结果更容易控制。 |
参数类型 | 宏的参数与类型无关,只要对参数的操作是合法的,它就可以使用于任何参数类型 | 函数的参数是与类型有关的,如果参数的类型不同,就需要不同的函数,即使他们执行的任务是不同的 |
调试 | 宏是不方便调试的 | 函数是可以逐语句调试的 |
递归 | 宏是不能递归的 | 函数是可以递归的 |
五、条件编译
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。
常见的条件编译指令:
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
或者
#pragma once