- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章C语言编程之预处理过程与define及条件编译由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
这张图描述了从源文件到可执行文件的整体步骤:
这张图展示了大体上步骤.
从代码到运行环境,编译器提供了翻译环境。在一个程序中,会存在多个文件 ,而每个源文件都会单独经过编译器处理.
预编译: 1,会将#include等头文件所包含的内容,库函数全部拷贝过来 2,代码中注释的删除 3,由#define所定义的符号全部替换进代码中 对预处理指令的操作 。
编译:把C代码翻译成汇编代码 1,语法分析(判断是否存在语言的语法错误而造成无法编译) 2,词法分析 3,语义分析(分析每句代码的意思) 4,符号汇总(会将整个程序中的全局符号进行汇总) 。
汇编 1,形成符号表 。
一个程序,两个文件test.c与add.c,在test.c中,有 。
extern int add(int x,int y);//声明对该函数引用,在其他文件中找该函数。
在汇编时,各个文件都会形参函数符号表,但extern并不会形成地址标记,只是一个0x00.
链接:合并段表 符号表的合并与重定位检查各个函数及其定义声明 。
。
define的预处理指令 。
#define MACRO substitution 预处理指令 宏 替换体 。
宏只是起到替换作用,在替换过程中不产生任何运算 举个例子 。
#define SUM 2+2int num = SUM * SUM;
不了解的人可能会认为是 num=44; 但,实际是替换作用 num=2+22+2; 这就是宏只起到替换作用的意思.
再来介绍一个原理性概念 。
记号 从技术角度来看,可以把宏的替换体看作是记号型字符串。而不是字符型字符串。在C预处理器记号是宏定义的替换体的中单独的“词”,用空白把词分开。例如:
#define FOUR 2*2#define FOURS 2 * 2
这两个对于预处理器要看预处理器把这个替换体看成什么。如果是字符型字符串,这空格也会是字符串的一部分。但如果是记号型字符串,空格就会被认为是分隔符,就和2*2是一个意思。总之要看编译器的规则。 总结一下 。
如果编译器理解替换体是字符型字符串,那么空格就会被认为是字符串的一部分 2 * 2就和2*2不是一个意思。 如果编译器理解为记号型字符串,那么空格就会被认为只是分隔符,并不影响。空格不算替换体的一部分 2 * 2和2乘2则是一个意思.
。
假设把MAX设为30,在文件中又把它重新定义为10.这个过程叫重定义常量但不同的标准有不同的规则。有一些允许重定义,但是会报警。ANSI标准则采用,只有新旧定义完全相同才允许重定义。 完全相同 意味着替换体中必须记号完全相同,顺序也必须相同 .
#define MAX 2 * 3#define MAX 2 * 3
这才允许 。
#define MAX 2 * 3#define MAX 2*3
这不符合那个标准。(虽然我不知道这个标准的重定义有什么用,我比较菜) 注:根据一个大佬的建议,这类代码非常致命,非常不好,最好不使用.
。
在#define中也可以创建外形和作用与函数类似的类函数宏。 带有函数的宏可以达到部分函数的作用.
#define SQUARE(X) X*Xmul=SQUARE(2);
与函数调用有些相似。 同时最好使用足够多的括号去确保运算和结合性的正确.
mul=SQUARE(x++)
则会造成运算不符合要求.
用宏参数创建字符串:#运算符 。
#define PSQRA(X) printf("X is %d\n",((X)*(X));#define PSQRB(X) printf("#X" is %d\n",((X)*(X));
这两个是可以打印出不同的效果 #作为一个预处理运算符,可以把记号转换成字符串,如果X是一个宏形参,那么#X就是“X”的字符串的形参名.
这叫字符串化 。
int y=50;PSQRA(y)X is 2500PSQRB(Y)y is 2500PSQRA(2+4)X is 36PSQRB(2+4)2+4 is 36
这就是区别.
。
#运算符可以作用于宏的替换体 而##运算符也可以作用.
#define NUMBER(n) X##nNUMBER(4)可展开为x4
例如 。
#include <stdio.h>#define XNAME(N) x##N#define PRINT(N) pritnf("x"#N"=%d\n",x##N);int main(void){ int XNAME(1) = 10;//x1=10 int XNAME(2) = 20;//x2=10 int x3 = 0; PRINT(1);//printf("x1=%d",x1); PRINT(2);//printf("x2=%d",x2); PRINT(3);//printf("x3=%d",x3);}
。
一些函数可以接受数量可变的参数(就是没有固定传递的参数的数量,如printf()和scanf())。而宏也可以拥有这样的能力.
#define PR(...) printf(_ _VAG_ARGS_ _)PR("HELLO WORLD");//printf("HELLO WORLD");PR("x1=%d,x2=%d",10,20);//printf(""x1=%d,x2=%d",10,20);
相当于这样的效果。 省略号只能代替最后的宏参数。不能在省略号加其他参数.
#define PR(x,...,y) #x #_ _VAG_ARGS_ _ #y
是不被允许的.
。
有相当一部分的宏可以起到和函数一样的效果,但到底该怎么选呢? 宏和函数可以达到同样效果。宏比函数要简单一些,同时,在编译器的消耗时间也要远小于函数。但是稍有不慎就会产生一些副作用,导致结果不可预测。 宏与函数的比较实际上就是关于时间与空间的比较.
宏在预编译的时候会生成内联代码,也就是会在程序中替换生成语句。如果调用20次,则会在程序中插入20行代码.
但如果调用函数20次,函数也只有一份副本,节省了相当一部分空间。但执行函数时,要调用,再执行,再返回,远比宏插入内联语句消耗的时间要多.
宏较函数也存在缺陷 。
自己按照情况去使用。如果使用宏容易出现副作用,那还是调用函数吧.
但要记住以下几点 。
1,记住宏名中不允许有空格,但在替换字符串中可以有空格。ANSI C允许在参数列表中使用空格.
2,用括号把宏的参数和替换体括起来,正确展开,防止出现副作用.
3,一般用大写字母表示宏常量,一般不全大写表示宏函数 。
4,如果用宏来加快函数的运行速度,要先确定宏和函数之间是否有差距。且,如果只使用一次那对速度加快影响不大。最好在多重嵌套中使用.
。
当编译器碰上#include 指令时,会查看后面文件名并把内容添加到当前文件中.
#include <stdio.h>//查找系统目录文件#include "mtfile.h"//查找当前工作目录#include "/usr/biff/file.h"//查找/usr/biff/目录
不同的系统有不同的规则,但<>与“”的规则是不变的.
通过头文件的引用,我们才能使用各种函数。头文件中有各种函数的声明。再通过库文件去调用函数的原型.
。
可以通过该指令去向之前#define定义的宏 。
#include <stdio.h>#define MAX 10#undef MAXint main(void){ printf("%d", MAX);}
直接报错 #undef可以取消,某个宏的定义,可以用用来防止某个宏被重复定义,造成错误.
。
预处理器在处理标识符时遵循相同规则。当预处理器发现一个标识符时,会将其当作已定义或未定义。而这里的已定义是由预处理器决定。如果该标识符是由define定义的且没有undef取消,那就是已定义。如果是定义的某个全局变量,那就是未定义(对预处理器而言).
#define定义的宏的作用域从文件开头开始,延申至文件结尾或者遇到#undef取消定义,如果跨文件使用,那使用的位置要在#include引用的文件后.
。
就跟条件判断语句有着类似的意思.
#ifdef , #else , #endif 。
举个例子 。
#include <stdio.h>#define MAX 10#undef MAX#ifdef MAX #include <string.h> #define MIN 10#endif // int main(void){ printf("%d", MIN);}
结果就是这个。 但屏蔽#undef 。
#include <stdio.h>#define MAX 10//#undef MAX#ifdef MAX #include <string.h> #define MIN 10#endif // int main(void){ printf("%d", MIN);}
可以运行。 条件编译指令与条件判断语句类似。 只不过,条件判断语句是判读是否执行, 二条件编译指令是判断是否进行预编译。 #endif用来结束该指令的范围 再引入一个指令 。
#ifndef DEBUG 。
如果DEBUG未定义就执行编译,如果已定义就不执行.
还有这几个指令,非常接近条件判断语句 。
#if ,#elif ,#else 。
#if和#elif与if和else if类似。但它们的后面接整形常量表达式。 0为假,非0为真 都是判断是否进行预编译 。
可以通过条件编译指令去防止某些文件被多次调用导致问题出现 。
#ifndef _FILE_H#define _FILE_H文件内容。。#endif
或者直接使用 。
#pragma once//可以保证文件只是使用一次
。
size_t offsetof( structName, memberName );
用于测算结构体成员相对于起始位置的偏移量 。
实现 。
#define OFFSETOF(structName,memberName) (int)&(((struct structName*)0)->memberName)
从0地址处,开始向成员访问再取地址在强转成整形.
以上就是C语言编程之预处理过程与define及条件编译的详细内容,更多关于C语言预处理的资料请关注我其它相关文章! 。
原文链接:https://blog.csdn.net/weixin_52199109/article/details/115048443 。
最后此篇关于C语言编程之预处理过程与define及条件编译的文章就讲到这里了,如果你想了解更多关于C语言编程之预处理过程与define及条件编译的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!