北京撒网站设计git网站开发
news/
2025/10/6 22:39:15/
文章来源:
北京撒网站设计,git网站开发,设计师个人作品集模板,一个简单的html网页文章目录 目录1. 程序的翻译环境和执行环境2. 详解编译链接2.1 翻译环境2.2 编译本身也分为几个阶段2.3 运行环境 3. 预处理详解3.1 预定义符号3.2 #define3.2.1 #define 定义标识符3.2.2 #define 定义宏3.2.3 #define 替换规则3.2.4 #和##3.2.5 带副作用的宏参数3.2.6 宏和函数… 文章目录 目录1. 程序的翻译环境和执行环境2. 详解编译链接2.1 翻译环境2.2 编译本身也分为几个阶段2.3 运行环境 3. 预处理详解3.1 预定义符号3.2 #define3.2.1 #define 定义标识符3.2.2 #define 定义宏3.2.3 #define 替换规则3.2.4 #和##3.2.5 带副作用的宏参数3.2.6 宏和函数对比 目录
程序的翻译环境程序的执行环境详解C语言程序的编译链接预定义符号介绍预处理指令 #define宏和函数的对比预处理操作符#和##的介绍命令定义预处理指令 #include预处理指令 #undef条件编译
1. 程序的翻译环境和执行环境
在ANSI C的任何一种实现中存在两个不同的环境。 第1种是翻译环境在这个环境中源代码被转换为可执行的机器指令。 第2种是执行环境它用于实际执行代码。 计算机是能够执行二进制指令的但是我们写出的C语言代码是文本信息计算机不能直接理解。
翻译环境C语言代码 — 二进制的指令放在可执行程序中
执行环境执行二进制的代码
2. 详解编译链接
2.1 翻译环境 组成一个程序的每个源文件通过编译过程分别转换成目标代码object code。每个目标文件由链接器linker捆绑在一起形成一个单一而完整的可执行程序。链接器同时也会引入标准C函数库中任何被该程序所用到的函数而且它可以搜索程序员个人的程序库将其需要的函数也链接到程序中。 我们也可以通过代码来观察
//add.cint Add(int x, int y)
{return x y;
}//test.c#include stdio.hextern int Add(int, int);int main()
{int a 10;int b 20;int c Add(a, b);printf(%d\n, c);return 0;
}2.2 编译本身也分为几个阶段 预处理阶段 编译阶段 汇编阶段 解释一下符号汇总、符号表 在这个目标文件里确实能看到一些符号都是全局的 接下来我们用两个文件来举例子
这有什么用呢
如果没有定义Add函数那么在链接的时候就定位不到这个函数就会发生链接错误生成不了可执行程序。
2.3 运行环境
程序执行的过程
程序必须载入内存中。在有操作系统的环境中一般这个由操作系统完成在独立的环境中程序的载入必须由手工安排也可能是通过可执行代码置入只读内存来完成。程序的执行便开始接着便调用main函数。开始执行程序代码。这个时候程序将使用一个运行时堆栈stack函数栈帧存储函数的局部变量和返回地址。程序同时也可以使用静态static内存存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。终止程序。正常终止main函数也有可能是意外终止。
3. 预处理详解
3.1 预定义符号 __FILE__ -- 进行编译的源文件 __LINE__ -- 文件当前的行号 __DATE__ -- 文件被编译的日期 __TIME__ -- 文件被编译的时间 __STDC__ -- 如果编译器遵循ANSI C其值为1否则未定义 这些预定义符号都是语言内置的。
举个例子
#include stdio.hint main()
{printf(%s\n, __FILE__);printf(%d\n, __LINE__);printf(%s\n, __DATE__);printf(%s\n, __TIME__);//printf(%d\n, __STDC__);//当前VS是不支持ANSI Creturn 0;
}对代码进行预处理之后
3.2 #define
3.2.1 #define 定义标识符 语法 #define name stuff #include stdio.h#define M 100
#define STR abc
#define FOR for(;;)
#define reg register//为 register这个关键字创建一个简短的名字int main()
{printf(%d\n, M);printf(%s\n, STR);FOR;//死循环return 0;
}预处理之后 int main()
{int d 0;switch (d){case 1:break;case 2:break;case 3:break;}return 0;
}以上代码还可以这样写
#define CASE break;caseint main()
{int d 0;switch (d){case 1:CASE 2:CASE 3:}return 0;
}#include stdio.h//如果定义的 stuff 过长可以分成几行写除了最后一行外每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf(file:%s\tline:%d\tdate:%s\t\time:%s\n , __FILE__, __LINE__,\__DATE__, __TIME__)int main()
{DEBUG_PRINT;return 0;
}提问 在define定义标识符的时候要不要在最后加上 ; 比如
#define M 100;int main()
{int a M;return 0;
}这样的代码看上去是没有问题的但是这样写是非常容易出错的
#define M 100;int main()
{int a 0;int b 0;if (a 5)b M;elseb -1;return 0;
}if 语句后面默认只能跟一条语句这里再加上一个 ; 就变成了两条语句这就意味着下面的 else 不知道和谁匹配了。 因此建议不要加上 ; ,这样容易导致问题。 3.2.2 #define 定义宏 #define 机制包括了一个规定允许把参数替换到文本中这种实现通常称为宏macro或定义宏define macro。 下面是宏的申明方式 #define name( parament-list ) stuff 其中的 parament-list 是一个由逗号隔开的符号表它们可能出现在stuff中。 注意 参数列表的左括号必须与name紧邻如果两者之间有任何空白存在参数列表就会被解释为stuff的一部分。
#include stdio.h#define MAX(x,y) ((x)(y)?(x):(y))
int main()
{int a 2;int b -2;int c MAX(a, b);printf(c%d\n, c);return 0;
}预处理之后 提示 所有用于对数值表达式进行求值的宏定义都应该用这种方式加上括号避免在使用宏时由于参数中的操作符或邻近操作符之间操作符的优先级而导致不可预料的相互作用。 应该这样写
#includestdio.h#define SQUARE(x) ((x) * (x))int main()
{int a 3;int r SQUARE(a 2);printf(r%d\n, r);return 0;
}应该这样写
#includestdio.h#define DOUBLE(x) ((x) (x))int main()
{int a 3;int r 10 * DOUBLE(a);printf(r%d\n, r);return 0;
}3.2.3 #define 替换规则
在程序中扩展#define定义符号和宏时需要涉及几个步骤。
在调用宏时首先对参数进行检查看看是否包含任何由#define定义的符号。如果是它们首先被替换。替换文本随后被插入到程序中原来文本的位置。对于宏参数名被他们的值所替换。最后再次对结果文件进行扫描看看它是否包含任何由#define定义的符号。如果是就重复上述处理过程。 注意
宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏不能出现递归。当预处理器搜索#define定义的符号的时候字符串常量的内容并不被搜索。
#includestdio.h#define M 3int main()
{printf(M%d\n, M);//M3return 0;
}3.2.4 #和## 如何把参数插入到字符串中 首先我们看看这样的代码
#include stdio.hint main()
{printf(hello world\n);//hello worldprintf(hello world\n);//hello worldreturn 0;
}我们发现字符串是有自动连接的特点的。 我们想实现这样一个功能
#include stdio.hint main()
{int a 20;printf(the value of a is %d\n, a);int b 15;printf(the value of b is %d\n, b);float f 4.5f;printf(the value of f is %f\n, f);return 0;
}我们可以用宏来实现函数做不到
#include stdio.h#define PRINT(n, format) printf(the value of #n is format\n, n)int main()
{int a 20;//printf(the value of a is %d\n, a);PRINT(a, %d);int b 15;//printf(the value of b is %d\n, b);PRINT(b,%d);float f 4.5f;//printf(the value of f is %f\n, f);PRINT(f, %f);return 0;
}代码中的 #n 会预处理为 “n” ##的作用 ##可以把位于它两边的符号合成一个符号它允许宏定义从分离的文本片段创建标识符。 #include stdio.h#define CAT(x,y) x##yint main()
{int Class110 2024;printf(%d\n, CAT(Class, 110));//2024printf(%d\n, Class110);//2024return 0;
}3.2.5 带副作用的宏参数
当宏参数在宏的定义中出现超过一次的时候如果参数带有副作用那么你在使用这个宏的时候就可能出现危险导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。
int main()
{int a 10;//int b a 1;//b11, a10int b a;//b11, a11return 0;
}x1;//不带副作用 x;//带有副作用 MAX宏可以证明具有副作用的参数所引起的问题
#include stdio.h#define MAX(x, y) ((x)(y)?(x):(y))int main()
{int a 5;int b 6;int c MAX(a, b);//int c ((a) (b) ? (a) : (b));// 5 6 7//c7//b8//a6printf(c %d\n, c);printf(a %d\n, a);printf(b %d\n, b);return 0;
}3.2.6 宏和函数对比
#include stdio.h//1
#define MAX(x, y) ((x)(y)?(x):(y))//2
int Max(int x, int y)
{return (x y ? x : y);
}int main()
{int a 5;int b 6;int c MAX(a, b);//int c Max(a, b);printf(c %d\n, c);printf(a %d\n, a);printf(b %d\n, b);return 0;
}宏通常被应用于执行简单的运算比如在两个数中找出较大的一个。
那为什么不用函数来完成这个任务
原因有二 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多所以宏比函数在程序的规模和速度方面更胜一筹。 更为重要的是函数的参数必须声明为特定的类型所以函数只能在类型合适的表达式上使用反之这个宏则可以适用于整形、长整型、浮点型等可以用 来比较的类型。宏是类型无关的。
宏的缺点 当然和函数相比宏也有劣势的地方
每次使用宏的时候一份宏定义的代码将插入到程序中。除非宏比较短否则可能大幅度增加程序的长度。(代码如果特别长那么编译的压力就会很大因为在编译的时候会对代码做各种各样的处理如语法分析、词法分析等等)宏是没法调试的。宏由于类型无关也就不够严谨。宏可能会带来运算符优先级的问题导致程容易出现错。
宏有时候可以做函数做不到的事情。比如宏的参数可以出现类型但是函数做不到。
#define MALLOC(num, type) (type*)malloc(num*sizeof(type))int main()
{int* p (int*)malloc(126 * sizeof(int));//malloc(126, int);//errint* p MALLOC(126, int);//int* p (int*)malloc(126 * sizeof(int));return 0;
}宏和函数的一个对比
对于第三点的一个例子
//1
#define MAX(x, y) ((x)(y)?(x):(y))//2
int Max(int x, int y)
{ //5 6return (x y ? x : y);
}int main()
{int c MAX(2 3, 6);//int c ((23)(6)?(23):(6))c Max(2 3, 6);return 0;
}补充 宏有自己的优势当然也有劣势
函数也有自己的优势也有劣势
能不能有一个函数既具有函数的好也具有宏的好呢
inline — 内联
内联函数
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/929778.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!