目录
一.程序的翻译环境和执行环境
二.详解翻译环境
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预定义符号