预处理和环境

一、翻译环境和执行环境

翻译环境:在这个环境中源代码被转换为可执行的机器指令 执行环境:用于实际执行代码

具体什么意思我们通过画图来解释

image

1.翻译环境


翻译环境分为两步,编译和链接

image

那么编译和链接是什么呢,同样以图来说明

image

那么问题又来了,编译和链接的过程中发生了什么

由于编译和链接内部需要Linux环境深入了解,这里直接上定义

image

2.执行环境

简述一下程序执行的过程

1.程序必须载入内存中. 在有操作系统的环境中:一般由操作系统完成 在独立的环境中:程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存完成 2.程序执行便开始,调用main函数 3.开始执行程序代码.这个时候程序将使用一个运行的堆栈(stack)(堆栈就是栈),存储函数的局部变量和返回地址. 程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值. 4.终止程序.正常终止main函数,也可能是意外终止.

以上


二、预处理详解

1.预定义符号

__FILE__ //进行编译的源文件 %s __LINE__ //文件当前的行号 %d __DATE__ //文件被编译的日期 %s __TIME__ //文件被编译的时间 %s __STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义 %d

三、#define定义宏

#define允许把参数替换到文本中 勤加()

①#

#可以把参数替换到字符串中

引出一下常见情况

void print(int x) { printf("??的结果是%d\n",x); // ?? 里面到底放什么呢 //显然函数并不能做到这个功能 } int main() { int a = 10; //打印出: a的结果是10 print(a); int b = 20; //打印出: b的结果是20 print(b); int c = 30; //打印出: c的结果是30 print(c); }

所以我们使用宏

首先我们要知道

printf("halo"); printf("ha""lo"); //这两个代码的结果是一样的,由此

#define print(X) printf(#X "的结果是%d",X) int main() { int a = 10; //打印出: a的结果是10 print(a); //本质上来讲,替换后 printf(#X "的结果是%d",X); printf("a" "的结果是%d",X); int b = 20; //打印出: b的结果是20 print(b); int c = 30; //打印出: c的结果是30 print(c); }

image

//格式不同的处理 #define print(X,FORMATE) printf(#X "的结果是"FORMATE,X) int main() { int a = 20; //打印出: a的结果是20 print(a, "%d\n"); float b = 5.5; //打印出: b的结果是5.5 print(b, "%f\n"); }

image

注意:#不能直接用在printf中,只能在宏内使用

②##

直接举个例子

#define CAT(X,Y) X##Y##Z int main() { int class1011 = 100; printf("%d\n",CAT(class,10,11)); printf("%d\n",class1011); //两者打印结果相同,可以将##看作是一个连接符号 return 0; }

四、宏和函数的取舍

属性#define定义宏函数
代码长度每次使用时,宏代码都会被插入程序中.除了非常小的宏除外,程序的长度会大幅提升函数代码只出现于一个地方,每次调用这个函数时,都调用那个地方的同一份代码
执行速度更快存在函数的调用和返回的额外开销,所以相对较慢
操作符优先级宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写时多些括号函数参数只在函数调用的时候求值一次,它的结果值传递给函数.表达式的求值结果更容易预测
带有副作用的参数参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果函数传参只在传参的时候求值一次,结果更容易控制
参数类型宏的参数与类型无关,只要对参数的操作时合法的,它就可以使用于任何参数类型函数的参数与类型有关,如果参数的类型不同,就需要不同的函数
调试宏是不方便调试的函数是可以逐语句调试的
递归宏不能递归函数可以递归

以上

五、undef

该指令可以移除一条宏定义 比如说

image

#undef NAME

六、条件编译

先上一个例子

#define ADD int main() { #ifdef ADD //如果定义了ADD宏,那么endif前就可以运行,反之则会在预处理时被略过 printf("666"); #endif #if 1 //非零则进入,否则在预处理时被略过 printf("666"); #endif return 0; }

#ifdef 后跟宏名称 #if 后跟常量表达式 同样都是以#endif作为结尾

同样的还有#else 、#elif 其后面跟的都是常量表达式

int main() { #if 1==1 //常量表达式 printf("666"); #elif 2==2 //常量表达式 printf("999"); #else 3==3 //常量表达式 printf("114514"); #endif //同if else 一样,这三条语句只能进入一个,不能同时进入 return 0; }

也有

#ifndef 作用与 #ifdef相反 #if defined() 括号内加入宏名,作用与 #ifdef相同 也可以#if !defined(),作用与 #ifdef相反