C语言的预编译
由“源代码”到“可执行文件”的过程包括四个步骤:预编译、编译、汇编、链接。所以,首先就应该清楚的首要问题就是:预编译只是对程序的文本起作用,换句话说就是,预编译阶段仅仅对源代码的单词进行变换,而不是对程序中的变量、函数等。
预编译指令的基本知识不作详细介绍,只稍作汇总,重点是后面的我能想到的 使用时的注意事项。
1. 基本内容
预编译指令基本分类如下
|
类别 |
指令 |
| 预定义符号 | __FILE__、__LINE__、__DATE__、__TIME__、__STDC__ |
| 宏 | #define |
| 文件包含 | #include |
| 条件编译 | #if、#elif、#else、#ifdef、#ifndef、#endif |
还有一些指令,名称和功能如下表:
| 指令 | 功能 |
| # | 空指令 |
| #undef | 移除一个空定义 |
| #error | 停止编译,并生成错误信息 |
| #line | 修改__LINE__和__FILE__的值 |
| #progma | 允许编译器提供额外功能 |
在定义宏的时候,有两个运算符:
| 运算符 | 功能 |
| # | 将宏参数转换为字符串 |
| ## | 将多个符号连接成一个标识符 |
2. 宏定义
1. 一般在宏定义的结尾不加分号。
我们在使用的时候,要加上分号,像我们平时写语句一样。
2. 注意加括号。
在有参数的空定义中,如果含有数值运算,那么就要在“宏整体”和“宏参数”两端都要加上括号。
如:#define max(a, b) ((a)+(b));
3. 注意空格。
在有参数的宏定义中,注意“宏名称”和“参数列表”之间不能有空格。
如:#define max (a, b) ((a)+(b)); 在"max”和”(a, b)”之间不能有空格。
4. 不要使用有副作用的参数区调用宏。
常见的有副作用的参数有:a++,getchar()等。
如:宏定义为#define max (a, b) ((a)+(b)); 那么使用max(i++, j++)调用该宏,会造成 i 或 j 中的一个值增加2,而不是我们期望的 1。
5. 可以使用编译器选项 添加宏 和 移除宏。
我使用的是gcc,添加宏的指令是”-D”,移除宏的指令是”-U”。
6. 宏参数替换的时候,不会替换字符串中的字符。
即不会替换双引号之间的字符,其他的都会被替换,包括单引号之间的。
7. 可以使用#将 宏参数的值 转化为字符串。
直接使用#,是将宏参数的名称转化为字符串。利用下面的技巧(增加一个过渡宏),可以将“宏参数的值”转化为字符串(当宏参数有值时,这时的宏参数常常也是一个宏)。
- #include <stdio.h>
- #include <stdlib.h>
- #define NUMBER ten /* 宏名称为NUMBER,宏的值为ten */
- #define Str(x) #x
- #define XStr(x) Str(x) /* 增加的一个 过渡宏 */
- int main(){
- printf("Str(NUMBER) == %s /n", Str(NUMBER));
- printf("XStr(NUMBER) == %s /n", XStr(NUMBER));
- system("pause");
- return EXIT_SUCCESS;
- }
输出结果为:
- Str(NUMBER) == NUMBER
- XStr(NUMBER) == ten
8. 使用##运算符来实现标识符连接。
不过,不建议使用操作符##来连接标识符,因为这个容易是程序可读性大大降低。
3. 文件包含
1. 要将头文件的定义在保护条件中。
目的是为了防止重复包含头文件。如果你查看过gcc或者其他编译器的源代码,你一定对这个非常熟悉。
例如,你要编写一个头文件,myheader.h,那么你的头文件的内容形式应该为:(定义一个_MYHEADER宏)
- #ifndef _MYHEADER
- #define _MYHEADER 1
- /* 中间是你的头文件内容 */
- #endif /* _MYHEADER */
2. 注意windows系统和Unix系统的路径符号不同。
可以再#include中指定路径来包含文件,例如 #include “../head.h”。但是注意,windows中使用反斜线”/”作为路径分隔符,而Unix系统使用的是斜线”/”。
3. 可以使用 编译器选项 来设置搜索路径。
我使用的gcc,使用的-Idir选项,例如: -I"D:/Dev-Cpp/include"。
4. 条件编译
1. #ifdef等价于#if defined(),#ifndef等价于#if !defined()。
2. 在#if中可以使用逻辑操作符(&&、||、!)。在#ifdef 中是不可以使用的,这也是#if的优越点。
- #include <stdio.h>
- #include <stdlib.h>
- #define A 1
- #define B 0
- int main(){
- #if defined( A ) && defined( B )
- printf("test logic operation in #if /n"); /* 如果上面的逻辑判断成立,那么将打印出一句话;如果不成立,那么就不会打印这句话 */
- #endif
- system("pause");
- return EXIT_SUCCESS;
- }
运行结果:
- test logic operation in #if
3. sizeof(int)在预编译阶段是不会被求值的。
只要知道“预编译阶段”在真正的“编译阶段”之前,就很容易理解了。预编译阶段只是对组成源代码中的字符进行作用,从某种意义上来说,它有时甚至不知道它的操作对象是什么,它只是按照既定的规则执行替换。
sizeof(int),无论是sizeof的解析,还是类型的解析,都是在“编译阶段”才开始的,编译阶段知道它的操作对象是什么。
下面的代码是错误的
- #if sizeof(int) == 2
- printf("precompile sizeof(int)");
- #endif
5. 额外注意
把一个预处理指令写成多行的形式,要使用符号”/”,并且在该符号后面应紧跟换行符。而非预处理指令的代码行不需要使用该符号,直接换行即可。 原因:编译阶段会自动忽略空白符,而预编译阶段不会。
C语言的预编译的更多相关文章
- 聊聊C语言的预编译指令include
"include"相信大家不会陌生,在我们写代码时,开头总会来一句"include XXX".include是干嘛用的,很多教材都提到了,因此这里不会再详细解释 ...
- javaScript语言的预编译与运行
JS代码执行的过程: 1.预编译 ---- 事先对js代码做一个预处理 2.代码运行---开始执行JS代码. JS编程: 1.加载DOM的最好在/BODY之前 2.与DOM渲染无关的放在Head里面 ...
- C语言的预编译,程序员必须懂的知识!【预编译指令】【预编译过程】
由“源代码”到“可执行文件”的过程包括四个步骤:预编译.编译.汇编.链接.所以,首先就应该清楚的首要问题就是:预编译只是对程序的文本起作用,换句话说就是,预编译阶段仅仅对源代码的单词进行变换,而不是对 ...
- (十八)C语言之预编译命令、宏
- iOS中的预编译指令的初步探究
目录 文件包含 #include #include_next #import 宏定义 #define #undef 条件编译 #if #else #endif #if define #ifdef #i ...
- C语言的傻瓜式随笔(二):全局变量、预编译、goto
函数的作用:可以实现代码的重用. 函数只需要定义1次,那么函数中的代码就可以随意的调用. -某不知出处的基本概念 学而时习之,如有误笔,请指正 一.goto跳转语句 goto在C语言的作用 ...
- 浅谈css的预编译---less语言
正如各位所知道的一样,css是一门标记性语言,语法相对简单,对使用者的要求也比较低 .不过可乐不知道友友们有没有发现,在使用css的时候需要书写大量看似没有逻辑的代码,不方便维护及扩展,不利于复用,尤 ...
- c语言中条件编译相关的预编译指令
一. 内容概述 本文主要介绍c语言中条件编译相关的预编译指令,包括#define.#undef.#ifdef.#ifndef.#if.#elif.#else.#endif.defined. 二.条件编 ...
- Java程序员的现代RPC指南(Windows版预编译好的Protoc支持C++,Java,Python三种最常用的语言,Thrift则支持几乎主流的各种语言)
Java程序员的现代RPC指南 1.前言 1.1 RPC框架简介 最早接触RPC还是初学Java时,直接用Socket API传东西好麻烦.于是发现了JDK直接支持的RMI,然后就用得不亦乐乎,各种大 ...
随机推荐
- url重定向或者重写
有四种方式:1.urlMappings,返回200状态码 <system.web> <urlMappings > <add url="~/others.aspx ...
- .net环境下跨进程、高频率读写数据
一.需求背景 1.最近项目要求高频次地读写数据,数据量也不是很大,多表总共加起来在百万条上下. 单表最大的也在25万左右,历史数据表因为不涉及所以不用考虑, 难点在于这个规模的热点数据,变化非常频繁. ...
- 作业02-Java基本语法与类库
1. 本周学习总结 以几个关键词描述本周的学习内容.并将关键词之间的联系描述或绘制出来. 原则:少而精,自己写.即使不超过5行也可,但请一定不要简单的复制粘贴. 2. 书面作业 1. String-使 ...
- 2016-wing的年度总结
大神们都爱写总结,为了早日成为大神,我也来写一波. 2016 有很多事情发生. 从日常生活来讲,生活水平得到了一定提升,从600一个月的村子搬到了800一个月的村子(/捂脸); 从就业环境来讲,许多人 ...
- 热烈庆祝自已厉精13年开发的 DB查询分析器 7.01(最新版本) 在中关村在线本月获得近6000次的下载量
中国本土程序员马根峰(CSDN专访马根峰:海量数据处理与分析大师的中国本土程序员)推出的个人作品----万能数据库查询分析器,中文版本 DB 查询分析器.英文版本DB Query Analyzer.它 ...
- iOS遍历数组的同时删除元素
我们在遍历可变数组时,最好不要做删除数组中元素的操作. 因为删除操作可能会引起数组容量的变化,导致数组越界等问题. 以前在使用for循环遍历的时候遇到过这个问题. 当时的做法是使用enumerateO ...
- listener.ora--sqlnet.ora--tnsnames.ora的关系以及手工配置举例(转载:http://blog.chinaunix.net/uid-83572-id-5510.ht)
listener.ora--sqlnet.ora--tnsnames.ora的关系以及手工配置举例 ====================最近看到好多人说到tns或者数据库不能登录等问题,就索性总结 ...
- 使用Apache的ab进行压力测试
概述 ab是apache自带的压力测试工具,当安装完apache的时候,就可以在bin下面找到ab然后进行apache 负载压力测试. 后台测试开发中,常用的压力测试服务,php一般选择xampp,下 ...
- React native和原生之间的通信
RN中文网关于原生模块(Android)的介绍可以看到,RN前端与原生模块之 间通信,主要有三种方法: 1)使用回调函数Callback,它提供了一个函数来把返回值传回给JavaScript. 2)使 ...
- Android优化之ViewPager的懒加载
转载本博客请注明出处:点击打开链接 http://blog.csdn.net/qq_32059827/article/details/52487794 出于对用户消耗流量的考虑,有必要对view ...