今天来讲一下C语言的程序环境和预处理
目录
一、C语言程序环境
C语言从写的代码到运行起来要经过预编译,编译,汇编,和链接四个部分,下面简单讲述一下这四个部分。
1、预编译
预编译部分进行的都是一些文本工作,比如注释掉的代码,#define定义的常量, 还有宏之类的操作。C语言中还有一些预定义符号:
__FILE__ __LINE__ __DATE__ __TIME__ __STDC___
这些符号的意思分别是进行编译的源文件,文件当前所在的行号,文件被编译的日期,文件被编译的时间,编译器是否遵循ANSI C,遵循其值为1,不遵循未定义。
2、编译
编译就是把预编译处理过后的代码翻译成汇编代码的过程
3、汇编
汇编过程就是通过编译生成的汇编代码形成目标文件的过程,比如我们每一个源文件都会生成一个目标文件,目标文件在windows环境下扩展名为.obj。
4、链接
链接就是在所有目标文件之间进行合并段表,符号表的合并以及符号表的重定位。比如如果编译器报错被调用的函数未定义,那就是链接过程出了问题。
二、C语言预处理
1、宏
预处理过程中有一个概念需要详细认识一下,那就是宏。
举例下面代码
#define ADD(x) x*x
int main()
{
int ret = ADD(5+1);
printf("%d",ret);
return 0;
}
按照我们的直觉我们会认为打印出结果是36,但是实际上打印出的结果是11。这是因为宏只是单纯的进行替换,并不会进行任何其他操作,这里ADD(5+1)其实就是 5+1*5+1 这样就是5+5+1,答案自然是11了。
通过这个题我们会发现一个问题,就是运算时候优先级的问题,为了解决这个问题,我们需要在宏里面加括号来明确优先级
#define ADD(x) (x)*(x)
这样就可以打印出36了,但是这样就大功告成了嘛?不不不,假如我换一段代码:
#define DOUBLE(x) (x)+(x)
int main()
{
printf("%d",10*DOUBLE(5));
}
按照我们的直觉,这里总该打印100了吧,但是事实上这里的结果是55,这是因为10*DOUBLE(5)其实是10*5+5结果自然是55了。我们会发现宏很容易造成运算优先级的问题,对于上面的这个题来说我们需要把整体再括起来:
#define DOUBLE(x) ((x)+(x))
为了解决这个问题,我们写宏的时候一定不要吝惜我们的括号。这个时候可能有人会问了,那宏这么容易写错,那我直接写函数不久好了吗,为 什么还要宏呢?
这个问题很好啊,因为宏本质上只是单纯的替换,不存在函数调用那样的堆栈,对于一些简单的操作来说,使用宏相比函数能够极大的减少内存开销。但是也要注意,宏是没法调试的,而且宏不存在类型检查,代码容易造成不严谨的错误。
那么我们正确认识了宏之后在写宏的时候有两个要注意的点,分别是# 和## 。
#会让这个宏参数变成对应的字符串。
##是让两边的符号连接起来形成一个新的符号。
2、条件编译
有些时候我们想要一段代码在不同的情况下有时参与编译,有时不参与编译。或者是有些调试性的代码,删除可惜,保留碍事,那应该怎么做呢?
这时候就要用到条件编译了,条件编译指令一共有4条:
1 #if 判断后面的常量表达式
2 #elif 适用于多个分支的条件编译
3#else 同2
4#endif 条件编译结束标志
条件编译也可以用来判断一个符号是否被定义
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
这就是条件编译的用法了,条件编译通常用来完成跨平台的指令。在不同平台执行不同指令。