程序的环境和预处理
程序的环境
1.程序的两种环境
在ANSI C中的实现分为编译环境和执行环境:
编译环境:在这个环境中会将源代码转换成可以执行的机器指令
执行环境:运用于执行编译环境所转换的代码
注: 编译环境本身有不同的阶段:
- 预处理阶段:这个阶段会将预处理指令所指向的对象直接替换
- 编译阶段:这个阶段会将代码的语法,语义,词法进行分析,并会对代码中的符号进行汇总。
- 汇编阶段:这个阶段会将代码中的符号制成符号表
- 链接阶段:
- 将符号表进行合并并对符号表进行重定义
- 对段表进行合并
2.预处理
2.1预处理指令
__ FILE __ //进行编译的源文件
__ LINE __//文件当前的行号
__ DATE __ //文件被编译的日期
__ TIME __//文件被编译的时间
__ STDC __//如果编译器遵循ANSI C,它的值就为1,否则就是未定义
这写预处理指令都是语言自带的。
下面举个例子:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
printf("date:%s,line:%d\n", __DATE__, __LINE__);
return 0;
}
上述代码的执行结果为:
2.2 #define
2.2.1 #define 定义标识符
举个例子:
#define _CRT_SECURE_NO_WARNINGS 1
#define MIN 0 //这里定义最小值,可以防止后面的重复定义,它会将文件中所以的MIN转换成 0
#include <stdio.h>
int main()
{
printf("date:%s,line:%d\n", __DATE__, __LINE__);
return 0;
}
同理:
可以用更简洁的符号来表示关键字,无符号数之类的。
也可以用更形象的符号来实现替换
如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都要加一个反斜杠(续行符)。
注意:
在定义标识符的时候最好不要在后面加上;因为这样可能会导致后面出现一定的问题。
例如:
if(selection)
min = MIN;
else
min = 0;
上述会把MIN直接替换成 0; 所以上述代码会直接变成
if(selection)
min = 0;;
else
min = 0;
这样就会出现问题了。
2.2.2 #define 定义宏
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义
宏(define macro)。
宏的申明方式:
#define name(parament-list) stuff
其中parament-list是一个由逗号隔开的符号表,它们可能出现在stuff中
注意:
- 参数列表的左括号必须与name紧邻,如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。
- 在定义宏的时候要注意,因为#define定义的符号是会直接替换而不是直接传参,所以最好给每个参数加上(),避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。
例:
#define square(x) x * x
如果传的是4+2,我们原本想要得到的结果是36,但它这边得到的结果会是14,因为#define 会直接将x替换成4+2,所以会变成下面这样:
#define square(x) 4+2*4+2
3.宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
4.当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
5.在定义宏的时候要注意带副作用的宏参数,副作用就是表达式求值的时候出现的永久性效果
例:
#include <stdio.h>
#define SUM(x,y) ((x)+(y))
int main()
{
int n = 5;
int m = 6;
int z = SUM(n++, ++m);
printf("n = %d m = %d z = %d\n",n,m,z);
return 0;
}
上述代码的执行结果为:
注:
这样传过去的参数会发生一些不可逆转的变化,可能导致后续的运用中出现一定的问题。
2.2.3 #和##
- # 的作用是将宏定义的参数转化成相应的字符串
例:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#define PRINT(VALUE,SUM)\
printf("The sum of " #SUM " is " VALUE "\n",SUM)
int main()
{
int i = 10;
PRINT("%d", i+6);
return 0;
}
上述代码的执行结果为:
因为#VALUE会在预处理完后转换成“VALUE"
- ##的作用是把位于它两边的符号合成一个符号。
它允许宏定义从分离的文本片段创建标识符。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#define SUM(a,b)\
sum##a += b;
int main()
{
SUM(6,8);//作用是将sum6的值增加8
return 0;
}
注:
这样的连接必须产生一个合法的标识符。否则其结果就是未定义的
注
因为函数和宏比较类似,但使用的方式和位置并不一样,所以
一般把宏名全大写
函数的名部分大写
以用于区别宏和函数
2.4 #undef
#undef 指令 用于移除已经定义的宏
#undef MIN
//如果要重新定义一个现存的符号,需要移除现有的定义,即取缔其旧名
2.5 条件编译
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。
为了方便后续的调试,我们可以用到条件编译,即进行选择性的编译。
#if 常量表达式
//…
#endif
//常量表达式由预处理器求值。
如:
#define __ DEBUG __ 1
#if __ DEBUG __
//…
#endif
2.多个分支的条件编译
#if 常量表达式
//…
#elif 常量表达式
//…
#else
//…
#endif
类似于if判断语句,条件代码本身也可以嵌套使用,所以可以运用的方法有很多。
#pragma once
防止头文件的重复引入。