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

目录

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

二.详解翻译环境

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,否则未定义

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6L695a6B5p2O5piT5bOwLg==,size_20,color_FFFFFF,t_70,g_se,x_16

 

2.#define

(1)#define定义标识符

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

(2)#define定义的宏

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

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

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

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

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

3.#和##

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

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

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6L695a6B5p2O5piT5bOwLg==,size_10,color_FFFFFF,t_70,g_se,x_16

 所以

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

演示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);
}

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6L695a6B5p2O5piT5bOwLg==,size_18,color_FFFFFF,t_70,g_se,x_16

 演示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);
}

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6L695a6B5p2O5piT5bOwLg==,size_20,color_FFFFFF,t_70,g_se,x_16

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

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

演示:

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

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6L695a6B5p2O5piT5bOwLg==,size_12,color_FFFFFF,t_70,g_se,x_16

 

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);
}

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6L695a6B5p2O5piT5bOwLg==,size_11,color_FFFFFF,t_70,g_se,x_16

 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

  • 53
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 97
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 97
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sessy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值