c语言收尾 预处理相关知识

简介: c语言收尾 预处理相关知识

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


1 翻译环境 在环境中源代码被转换为可执行的机器指令


2 执行环境 它用于实际执行代码


运行的结果大概是这样


ed3fff6d6875484ba66df640a10b2313.png

1.1 翻译环境


22d6f3f137314d0997dbac0bff535e06.png


编译器是一个集成开发环境 它会生成目标文件


092b30bb778b4370b656ccd604692633.png

编译之后通过连接器生成可执行目标文件

ab624dc9ae1b4dd9b7094bf69828f2b4.png


这里我们详细介绍下编译的过程


1.1.1 预处理


我们要观察预处理的过程就要首先执行这一条指令


gcc test.c -E -o test.i
• 1


这样子我们前面一条指令的结果就放到test.i 里面去了

0e7783da1b1c48ada0096aa4d32ea524.png

0c6cddac662d477ba21cf276a9f9b89c.png


打开之后我们发现竟然有八百多行的数据


这里我们可以发现 其实


头文件的包含是在预处理这一步完成的(#include)

宏的替换是在预处理这一步完成的

注释的消除也是在预处理这一步完成的


1.1.2 编译


想要在编译这一步停止 我们要使用这样的linux命令


gcc test.i test.s


50358121161f4e428802185955eee7e5.png


这里让我们生成了一个test.s文件


我们打开来看看


1823ed41d5af4209afa31cf3846ba83f.png


我们可以发现 这里面其实就是汇编代码


所以说在汇编这一步


将所有的代码 变成了汇编代码 并且进行

1 语法分析

2 词法分析

3 语义分析

4 符号汇总


1.1.3 汇编


同样的 我们执行下面的命令 让代码进行汇编


gcc test.s -C
• 1


我们可以发现能得到一个test.o文件


687d7a49ec824b3a8e66717d70cc92f4.png


我们打开这个文件看看

863e886b273b424aa570c162dfc5c5e6.png


我们会发现 一堆乱码


这是因为


在汇编阶段

所有程序变成了二进制语言


在liunx环境下


test.o 就是一个可执行文件


我们可以使用readelf来读这个文件


readelf test.o -s


aa7cdf86d7a74e56a17a0ce558e08eae.png


我们可以发现会出现上面的一些符号 这些都是符号表


1.1.4 连接


连接之后会生成一个可执行程序


它做的操作实际上是


1 合并段表

2 符号表的合并和重定位


1.2 运行环境


程序执行的过程


1 程序必须载入内存中 在有操作系统的环境中 一般由操作系统来完成

2 程序执行便开始 接着调用main函数

3 开始执行 使用运行时堆栈 存储函数的局部变量和返回地址 程序同时也可以使用静态内存 储存静态内存中变量在程序整个执行过程中一直保留它们的值

4 终止程序 正常终止main函数 也可能意外终止


二. 预处理详解


2.1 预定义符号


FILE //进行编译的源文件

LINE //文件当前的行号

DATE //文件被编译的日期

TIME //文件被编译的时间

FUNCTION //文件当前所在的函数

STDC //如果编译器遵循ANSI C标准,其值为1,否则未定义


这些预定义符号都是语言内置的


我们来使用下上面的语句


int main()
{
  int i = 0;
  for ( i = 0; i < 10; i++)
  {
    printf("File: %s Linr: %d date:%s time:%s i=%d\n", __FILE__, __LINE__, __DATE__, __TIME__,i);
  }
  return 0;
}


这样我们就可以知道 函数在什么时间 什么地点 发生了什么


f66e2a31e2014250be09f100528ce92a.png


2.2 #define 定义标识符号


define可以定义符号


例如


#define NUM 100+200
#define STR "abcdef"
int main()
{
  int num = NUM;
  char* str = STR;
  return 0;
}


在预处理其实NUM就变成了 100+200


STR就变成了“abcdef”


这里注意的是define是不能加分号的 否则替换到源文件里面会出现各种问题


2.3 #define 定义宏


我们举个例子


#define Max(x,y) (x>y?x:y)
int main()
{
  int a = 10;
  int b = 20;
  int c =Max(a, b);
  printf("%d", c);
  return 0;
}


这里 它就是一个宏 它允许把参数替换到文本中去 这种实现通常称为宏


ab5336180ee346c99367aa82c7cbee12.png


这里我们要计算11的平方 计算出来的结果确是21 这是怎么回事呢?


原来啊 在宏定义的时候 数据替换并不会经过计算 实际上我们的表达式是这样子的


10 + 1 * 10 + 1;
• 1


当然最后的结果就是21啦


这件事告诉我们在定义宏的时候一定要注意括号


所以说 正确的定义方式应该是这样子


#define S(x) ((x)*(x))


d7e911b6e7ce43fea10fb9d8126f988b.png


这里还有要注意的一点 注释中的宏是不会被替换的


2.4 #define 的副作用


我们写出以下代码


#define Max(x,y)((x)>(y))?(x):(y)
int main()
{
  int a = 5;
  int b = 8;
  int c= Max(a++, b++);
  printf("%d\n", a);
  printf("%d\n", b);
  printf("%d\n", c);
  return 0;
}


这里输出结果如下

d3c1f26ab8984e5c97a9a15f11ea4b24.png


我们可以发现 其实b是++了两次的


这里就是宏的一个小缺陷


2.5 宏和函数对比


宏通常用于比较简单的运算


例如求两个数中的较大值


为什么这样子呢?


因为

1 宏在规模和速度方面更胜一筹

2 函数必须要声明类型 而宏不用


当然宏也有缺点


每次使用宏的时候一份宏的代码将会被插入到程序中 除非宏比较短 可能大幅度增加程序的长度

2 宏无法调试

3 宏无类型定义 不够眼睛

4 宏可能会带来操作符优先级的问题 导致程序出错

以上就是本篇博客的全部内容啦 由于博主才疏学浅 所以难免会出现纰漏 希望大佬们看到错误之后能够不吝赐教 在评论区或者私信指正 博主一定及时修正


那么大家下期再见咯


相关文章
|
1月前
|
编译器 C语言
C语言的预处理指令
C语言的预处理指令
|
1月前
|
编译器 Linux PHP
C语言从入门到实战——预处理详解
C语言预处理是C语言编译过程的一个阶段,它在编译之前对源代码进行一系列的处理操作,包括宏替换、文件包含、条件编译等,最终生成经过预处理的代码,然后再进行编译。
46 0
|
4月前
|
存储 自然语言处理 编译器
『C语言进阶』程序环境和预处理
『C语言进阶』程序环境和预处理
|
5月前
|
Linux 编译器 C语言
C语言进阶-程序环境和预处理(2)
C语言进阶-程序环境和预处理
53 1
|
5月前
|
存储 自然语言处理 编译器
C语言进阶-程序环境和预处理(1)
C语言进阶-程序环境和预处理
43 0
|
1月前
|
存储 编译器 程序员
【程序环境和预处理】C语言
【程序环境和预处理】C语言
|
1月前
|
存储 机器学习/深度学习 自然语言处理
【进阶C语言】编译与链接、预处理符号详解
【进阶C语言】编译与链接、预处理符号详解
25 0
|
1月前
|
存储 自然语言处理 编译器
【C语言进阶】程序环境和预处理
【C语言进阶】程序环境和预处理
|
1月前
|
编译器 Linux C语言
程序环境和预处理(含C语言程序的编译+链接)--2
程序环境和预处理(含C语言程序的编译+链接)--2
30 5
|
1月前
|
存储 编译器 程序员
程序环境和预处理(含C语言程序的编译+链接)--1
程序环境和预处理(含C语言程序的编译+链接)--1
25 0