wordpress快站平台,做PPT哪个网站的素材多点,wordpress不能发布,河北省建设机械协会官方网站文章目录
一、程序的翻译环境和执行环境
1.程序编译过程
2.编译内部原理
3.执行环境 二、程序运行前的预处理
1.预定义符号归纳
2.define定义标识符
3.define定义宏
4.define替换规则
5.宏和函数的对比
三、头文件被包含的方式
四、练习#xff1a;写一… 文章目录
一、程序的翻译环境和执行环境
1.程序编译过程
2.编译内部原理
3.执行环境 二、程序运行前的预处理
1.预定义符号归纳
2.define定义标识符
3.define定义宏
4.define替换规则
5.宏和函数的对比
三、头文件被包含的方式
四、练习写一个宏可以将一个整数二进制位的奇偶位交换 大家好我是纪宁。 在ANSI C的任何一种实现中都存在两个不同的环境。第1种是翻译环境在这个环境中源代码被转换为可执行的机器指令。第2种是执行环境它用于实际执行代码。翻译环境中也有很多的内部原理一起来学习吧。
一、程序的翻译环境和执行环境
1.程序编译过程 其中源文件是文件名后缀为 .c 的文件目标文件是文件后缀名为 .obj 的文件。 组成一个程序的每个源文件通过编译过程分别转换成目标代码object code。每个目标文件由链接器linker捆绑在一起形成一个单一而完整的可执行程序。链接器同时也会引入标准C函数库中任何被该程序所用到的函数而且它可以搜索程序员个人的程序库将其需要的函数也链接到程序中
2.编译内部原理 在预处理阶段系统会自动处理掉所有的预编译指令。 包括删去注释将头文件包含#define 定义符号和宏的替换等等 而在编译阶段系统会将C语言代码翻译成汇编指令还有语法分析、词义分析、语义分析、符号汇总等 在汇编阶段。系统将汇编指令翻译成二进制指令并生成.o目标文件形成符号表。 最后在链接阶段合并段表重定位和合成符号表在链接阶段可发现函数是否正常定义
3.执行环境 首先程序必须载入内存中。在有操作系统的环境中一般这个由操作系统完成。在独立的环境中程序的载入必须由手工安排也可能是通过可执行代码置入只读内存来完成。 接下来就是程序的执行开始调用main函数开始执行程序代码。这个时候程序将使用一个运行时堆栈stack存储函数的局部变量和返回地址。程序同时也可以使用静态static内存存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。 最后就是终止程序。正常终止main函数也有可能是意外终止。 二、程序运行前的预处理
1.预定义符号归纳 下面的这些符号都是C语言内置符号
__FILE__ 进行编译的源文件__LINE__ 文件当前的行号__DATE__ 文件被编译的日期__TIME__文件被编译的时间
程序中使用对应的符号在预处理阶段自动转化为对应的含义测试代码如下
#includestdio.h
int main() {printf(%d\n, __LINE__);printf(%s\n, __TIME__);printf(%s\n, __DATE__);printf(%s\n, __FILE__);return 0;
}
运行结果 由于_LINE_在第四行所以转化结果为4时间和日期就是我测试代码的时间、日期_FILE_则对应我源文件的路径。
2.define定义标识符
语法如下
#define name stuff 在预处理阶段编译器会自动将代码中的 name 全部替换为 stuff 。但需注意在写#define定义标识符的时候后面不能加 ;预处理阶段编译器会将 ; 也当成要替换的内容。
3.define定义宏 #define 机制包括了一个规定允许把参数替换到文本中这种实现通常称为宏macro或定义 宏define macro。下面是宏的申明方式
#define name( parament-list ) stuff 其中的 parament-list 是一个由逗号隔开的符号表它们可能出现在stuff中。它的使用方法与函数类似只不过将函数传参改成了直接替换。因为改成了直接替换所以宏的参数尽量加括号修饰。其次参数列表的左括号必须与name紧邻因为如果两者之间有任何空白存在参数列表就会被解释为stuff的一部分。 宏的参数一般会被多次替换所以不能使用那些带 ‘副作用’ 的参数副作用即表达式求值中出现的永久性效果如自加自减这些运算。 例如求一下如下代码的运算结果
#define MAX(a, b) ( (a) (b) ? (a) : (b) )
...
x 5;
y 8;
z MAX(x, y);
printf(x%d y%d z%d\n, x, y, z); 大部分人可能会理所当然的以为传过去的是x和y表达式的值那么结构就应该是z的结果剧应该是8x的结果应该是6y的结果应该是9 可实际上并非如此 这就是参数带有副作用的影响
4.define替换规则
在程序中扩展#define定义符号和宏时需要涉及几个步骤。 1. 在调用宏时首先对参数进行检查看看是否包含任何由#define定义的符号。如果是它们首先被替换。 2. 替换文本随后被插入到程序中原来文本的位置。对于宏参数名被他们的值所替换。 3. 最后再次对结果文件进行扫描看看它是否包含任何由#define定义的符号。如果是就重复上述处理过程。 还有几点需要注意 1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏不能出现递归。 2. 当预处理器搜索#define定义的符号的时候字符串常量的内容并不被搜索 5.宏和函数的对比 在某些方面来讲宏是非常有优势的。比如求两个数的最大值这样一个简单的工作如果用函数来完成的话可能会非常耗费时间因为每次调用函数都要进行函数栈帧创建和销毁而宏却不需要函数传参必须是特定的类型而宏的参数却可以在算数运算范围内传多种类型的参数。总结就是在小型工程中宏比函数在程序的规模和速度方面更胜一筹宏是类型无关的且可以将类型当参数传过过去这点是函数做不到的。 但宏也有以下缺点 1. 每次使用宏的时候一份宏定义的代码将插入到程序中。除非宏比较短否则可能大幅度增加程序的长度。 2. 宏是没法调试的。 3. 宏由于类型无关也就不够严谨。 4. 宏可能会带来运算符优先级的问题导致程容易出现错。 三、头文件被包含的方式 在C语言中包含头文件有两种方法 包含和 包含。 包含是编译器包含本地文件的方法查找策略是先在源文件所在目录下查找如果该头文件未找到编译器就像查找库函数头文件一样在标准位置查找头文件如果找不到就提示编译错误。 而 包含的查找头文件是直接去标准路径下去查找如果找不到就提示编译错误。 看了这种情况有人可能会问那以后能不能包含所有文件都用 来包含答案是可以的但是如果考虑到程序的效率问题就另当别论了因为 去查标准路径的头文件是需要查找两次的而查找标准路径的头文件只需要查找一次效率明显高很多。 为了防止头文件被重复引用头文件中都会有条件编译的内容来防止文件内容的重复
在每个头文件的开头写这几条语句
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif //__TEST_H_
或者
#pragma once
就可以避免头文件的重复引用。
这是一些常用的条件编译指令
#if#elif#endif
四、练习写一个宏可以将一个整数二进制位的奇偶位交换 思路将这个数的二进制位的奇数位取出向左移动一位将这个数的二进制位的偶数位取出向右移动一位最后相加即可。 以8位二进制为例0x55可以得到它的奇数位0xaa可以得到它的偶数位如果上升到32位二进制那么0x55555555可以得到它的奇数位然后左移0xaaaaaaaa可以得到它的偶数位然后右移最后再相加即可将这个数的二进制位的奇偶位交换。 如果看懂图解的话写代码用宏实现就很简单了。
#includestdio.h
#define SWAP(n) (n((n0xaaaaaaaa)1)((n0x55555555)1))
int main()
{int n -120;SWAP(n);//交换奇偶位printf(%d\n, n);SWAP(n);//再交换回来printf(%d\n, n);return 0;
}
用 -120 验证奇偶位交换是否成功
运行结果 时间过得很快眨眼间C语言的博客已经接近完结了后续有时间还会发布C语言的一些经典面试题解大家尽请期待。 下一篇博客就开始学习并更新数据结构与算法的内容感谢各位对纪宁的支持。