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. 可以使用#将 宏参数的值 转化为字符串。
直接使用#,是将宏参数的名称转化为字符串。利用下面的技巧(增加一个过渡宏),可以将“宏参数的值”转化为字符串(当宏参数有值时,这时的宏参数常常也是一个宏)。
[cpp] view plain copy
#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;}
输出结果为:
[cpp] view plain copy
Str(NUMBER) == NUMBERXStr(NUMBER) == ten
8. 使用##运算符来实现标识符连接。
不过,不建议使用操作符##来连接标识符,因为这个容易是程序可读性大大降低。
3. 文件包含
1. 要将头文件的定义在保护条件中。
目的是为了防止重复包含头文件。如果你查看过gcc或者其他编译器的源代码,你一定对这个非常熟悉。
例如,你要编写一个头文件,myheader.h,那么你的头文件的内容形式应该为:(定义一个_MYHEADER宏)
[cpp] view plain copy
#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的优越点。
[cpp] view plain copy
#include <stdio.h>#include <stdlib.h>#define A 1#define B 0int main(){#if defined( A ) && defined( B )printf("test logic operation in #if /n"); /* 如果上面的逻辑判断成立,那么将打印出一句话;如果不成立,那么 就不会打印这句话 */#endifsystem("pause");return EXIT_SUCCESS;}
运行结果:
[cpp] view plain copy
test logic operation in #if
3. sizeof(int)在预编译阶段是不会被求值的。
只要知道“预编译阶段”在真正的“编译阶段”之前,就很容易理解了。预编译阶段只是对组成源代码中的字符进行作用,从某种意义上来说,它有时甚至不知道它的操作对象是什么,它只是按照既定的规则执行替换。
sizeof(int),无论是sizeof的解析,还是类型的解析,都是在“编译阶段”才开始的,编译阶段知道它的操作对象是什么。
下面的代码是错误的
[cpp] view plain copy
#if sizeof(int) == 2printf("precompile sizeof(int)");#endif
5. 额外注意
把一个预处理指令写成多行的形式,要使用符号”/”,并且在该符号后面应紧跟换行符。而非预处理指令的代码行不需要使用该符号,直接换行即可。 原因:编译阶段会自动忽略空白符,而预编译阶段不会。
C语言的预编译,程序员必须懂的知识!【预编译指令】【预编译过程】的更多相关文章
- 谈谈Java程序员进阶的那些知识和方向
谈谈Java程序员进阶的那些知识和方向 记得前段时间看过一篇文章谈到一种程序员叫野生程序员,战斗力极强,可以搞定一切问题,但是通常看问题抓不到本质,或者说是google/baidu/stackover ...
- PHP笔记——java程序员看懂PHP程序
PHP笔记——java程序员看懂PHP程序 php是一种服务器端脚本语言,类型松散的语言. <?php ?> xml风格 <script language=”ph ...
- Sharepoint程序员应该了解的知识
做为一个Sharepoint程序员应该了解的知识:注意,我说的是程序员.因为我一直把自己看一个普普通通的程序员. 前提: 要知道网络基础(包括DHCP.IP.掩码.DNS.网关.广播),会装操作系统( ...
- 震惊!90%的程序员不知道的Java知识!
震惊!90%的程序员不知道的Java知识! 初学Java的时候都会接触的代码 public static void main(String[] args){ ... } 当时就像背公式一样把这行代码给 ...
- Java程序员的现代RPC指南(Windows版预编译好的Protoc支持C++,Java,Python三种最常用的语言,Thrift则支持几乎主流的各种语言)
Java程序员的现代RPC指南 1.前言 1.1 RPC框架简介 最早接触RPC还是初学Java时,直接用Socket API传东西好麻烦.于是发现了JDK直接支持的RMI,然后就用得不亦乐乎,各种大 ...
- Java程序员必须掌握的知识
1.语法:Java程序员必须比较熟悉语法,在写代码的时候IDE的编辑器对某一行报错应该能够根据报错信息 知道是什么样的语法错误并且知道任何修正. 2.命令:必须熟悉JDK带的一些常用命令及其常用选项, ...
- JAVA程序员必须要学习的知识
Java是热门的语言之一,TIOBE编程语排名Java排名第二,仅在C语言之后.Java可以用来开发web应用和桌面应用,更重要的是Java具有跨平台性:write once, run everywh ...
- Java程序员必了解的JVM原理以及虚拟机的运行过程
JVM概念 虚拟机:指以软件的方式模拟具有完整硬件,VM概念 虚拟机:指以软件的方式模拟具有完整硬件系统功能.运行在一个完全隔离环境中的完整计算机系统 ,是物理机的软件实现.常用的虚拟机有VMWare ...
- 深圳尚学堂:Web程序员应该会的知识
互联网的行业里涌入了很多的程序员, 都在为互联网的发展添砖加瓦.程序员可以分为很多种,像Unix程序员.Windows程序员,或是C++程序员.Delphi程序员,等等.今天我们谈谈Web程序员,一名 ...
随机推荐
- O035、Nova Suspend / Rescue 操作详解
参考https://www.cnblogs.com/CloudMan6/p/5503501.html Suspend / Resume 有时候需要长时间暂停 instance , 可以通过 S ...
- 浅谈RPC框架
RPC(Remote Promote Call) RPC(Remote Promote Call):一种进程间通信方式.允许像调用本地服务一样调用远程服务. RPC框架的主要目标就是让远程服务调用更简 ...
- JavaJDBC【七、JDBC升级版简介】
Commons-dbutils Apache提供的一个开源JDBC工具类库,对传统操作数据库的类进行二次封装,可以把结果集转换成List. 特点: 1.杜绝资源泄漏 2.简化代码 3.以Bean实例形 ...
- Delphi 画布对象
樊伟胜
- 第八章·Logstash深入-通过TCP/UDP收集日志
1.收集TCP/UDP日志 通过logstash的tcp/udp插件收集日志,通常用于在向elasticsearch日志补录丢失的部分日志,可以将丢失的日志通过一个TCP端口直接写入到elastics ...
- sourceforge.net
https://sourceforge.net/ SourceForge.net,又称SF.net,是开源软件开发者进行开发管理的集中式场所. SourceForge.net由VA Software提 ...
- Python 3 中字符串和 bytes 的区别
在Python中字符串和unicode真是傻傻分不清楚,在没搞懂两个区别时,你会发现程序报的错怎么改都是再报错,让你烦躁. 学习Python的时候,又重温了这部分内容,写了这个学习笔记. Python ...
- zencart简易页面ezpage后台编辑位置
zencart简易页面ezpage后台编辑位置: 后台-Tools(工具)-EZ-Pages(简易页面管理) 若编辑之后无法保存,说明includes\languages\语言包\html_inclu ...
- 在ASP.NET MVC项目中使用极验验证(geetest)
时间 2016-03-02 18:22:37 smallerpig 原文 http://www.smallerpig.com/979.html 主题 ASP.NET MVC geetest开发体 ...
- JavaScript设计模式与开发实践(二)——apply&&call
call和apply的用途 改变this指向 先看个例子: var obj1 = { name: 'sven' }; var obj2 = { name: 'anne' }; window.name ...