irpas技术客

<大厂面试高频考点>程序环境和预处理_.峰峰

大大的周 6781

目录

一.程序的翻译环境和执行环境

二.详解翻译环境

1.编译

(1)预编译(文本操作)

(2)编译(把C语言代码翻译成汇编代码)

(3)汇编(汇编指令翻译成二进制指令)

(4)链接(把多个经过编译的源文件+链接库 链接)

三.概述运行环境

程序执行的过程

四.预处理详解

? ? ? ? ?1.预定义符号

2.#define

(1)#define定义标识符

(2)#define定义的宏

3.#和##

(1)# 将参数插入到字符串中

(2)## 可以把它两边的符号合成一个符号

?

4.带有副作用的宏参数

5.宏和函数的对比

6.命令行定义(在编译的过程中定义符号)

7.条件编译

(1)#if

(2)多个分支的条件编译

(3) 判断是否被定义

(4)嵌套指令

8.文件包含

(1)头文件被包含的方式

(2)防止重复包含


一.程序的翻译环境和执行环境

概念介绍:

在ANSI C的任何一种实现中,都会存在两个环境。

1.翻译环境:将源代码转换成可执行的机器指令。(编译器)

2.执行环境: 实际执行代码。(操作系统)

二.详解翻译环境

即:一个源程序文件怎么转化成一个可执行程序的。

流程:多个源文件单独经过编译器处理,再经过链接器链接,最后形成一个可执行程序。

1.编译 (1)预编译(文本操作)

文本操作:文件的替换调用

a.头文件的包含

#include — 预处理指令

b.#define定义符号的替换

#define —预处理指令

c.删除注释

(2)编译(把C语言代码翻译成汇编代码)

a.语法分析

b.词法分析

c.语义分析

d.符号汇总:只汇总全局符号 例如:Add main(链接时会用到)

(3)汇编(汇编指令翻译成二进制指令)

a.形成符号表:每个源文件都是单独编译,都形成自己的符号表(包括引用的外部符号),并带有符号地址,如果是外部符号就赋一个无意义的地址

(4)链接(把多个经过编译的源文件+链接库 链接)

a.合并段表:把不同源文件的相同段进行合并

b.符号表的合并和重定位:把不同源文件形成的符号表合并,使得外部声明的符号得到其真正的地址(所以如果未定义,会在链接的时候报错:无法解析的外部符号)

三.概述运行环境 程序执行的过程

1.程序必须载入内存中。在有操作系统的环境中,载入由操作系统完成。在独立的环境中,载入到内存必须由手工安排。

2.程序的执行开始。调用main函数。

3.开始执行代码。这个时候程序将使用一个运行时堆栈,存储函数的局部变量和返回地址。同时也就可以使用静态内存。

4.终止程序。正常终止main,也可能是异常终止。

四.预处理详解 1.预定义符号

a.__FILE__? ? ? ? ?进行编译的源文件

b.__LINE__? ? ? ? ?文件当前的行号

c.__DATE__? ? ? ? 文件被编译的日期

d.__TIME__? ? ? ? ?文件被编译的时间

e.__STDC__? ? ? ? 如果编译器遵循ANSI C ,其值为1,否则未定义

? 2.#define (1)#define定义标识符

非常灵活,在末尾不要加;会出问题

(2)#define定义的宏

因为宏定义是直接替换的,所以由于运算符优先级的不同,会导致一些问题。

所以我们在进行宏定义的时候,要把变量都加上括号、把宏的整体也加上括号。

注意:宏参数和#define定义中可以出现其他#define定义的符号。但是宏不能递归。

? ? ? ? ? ?字符串中出现宏定义符号 不会被替换

? ? ? ? ? ?#undef NAME? 这条指令用于移除一个宏定义

3.#和## (1)# 将参数插入到字符串中

铺垫:由于C语言对于字符串的打印有一个会合并的特点。(两个打印出来效果一样)

?所以

我们用宏可以实现如下效果:

演示1:

#include<stdio.h> #define PRINT(N) printf("the value of "#N" is %d\n",N) //无分号! int main() { int a = 10; PRINT(a); int b = 20; PRINT(b); }

?演示2:

#include<stdio.h> #define PRINT(N,format) printf("the value of "#N" is "#format"\n",N) //无分号! int main() { int a = 10; PRINT(a,%d); double b = 3.14; PRINT(b,%lf); }

?(2)## 可以把它两边的符号合成一个符号

其允许从分离的片段中创建标识符

演示:

#include<stdio.h> #define CAT(name,num) name##num int main() { int class105 = 105; printf("%d\n", CAT(class, 105)); }

? 4.带有副作用的宏参数

b=a+1; 不带副作用?

b=a++; 带有副作用 改变了a的值 宏替换是自左向右一步一步替换的,此时a的值已经发生改变

演示:

#include<stdio.h> #define MAX(x,y) ((x)>(y)?(x):(y)) int main() { int a = 5; int b = 8; int c = MAX(a++, b++); printf("%d %d %d", a, b, c); }

?5.宏和函数的对比

?

6.命令行定义(在编译的过程中定义符号)

当我们根据同一个源文件编译出不同版本可用命令行定义,例如:可以用来调整数组大小。

7.条件编译 (1)#if

#if 常量表达式? 如果为真 进入编译 ......

#endif? 结束编译

(2)多个分支的条件编译

#if 常量表达式

...

#elif 常量表达式

...

#endif

(3) 判断是否被定义

a.如果定义了 就为真 参与编译

#if defined(NAME)

...

#endif

#ifdef NAME

...

#endif

?

?b.如果没定义? 参与编译

#if !defined(NAME)

...

#endif

#ifndef NAME

...

#endif

(4)嵌套指令

与if语句嵌套相同,不过每组条件编译语句都要用#endif来结束

8.文件包含 (1)头文件被包含的方式

a.本地文件包含

#include "filename"

查找策略:两步走:先在源文件所在目录下查找,如果未找到,就像查找库函数头文件一样在标准位置查找头文件。

b.库函数包含

#include<filename.h>

查找策略:直接去标准路径下查找

(2)防止重复包含

a.

#ifndef __NAME_H__? ?(名字随便写 它只是判断这条语句之前有没有走过)

#define __NAME_H__

.......

#endif?

b.

#pragma once


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #1预定义符号