Linux C语言 25-预处理操作

Linux C语言 25-预处理操作

本节关键字:C语言编译过程、预处理、多行宏定义、通过宏判断操作系统、通过宏判断VC版本
相关C库函数:main,printf

什么是预处理?

预处理是C语言源码编译中重要的一步。用C语言编写的代码不能直接被计算机识别并运行,因此需要先将源码转换程计算机能够识别并运行的二进制语言,这个转换的过程叫做编译(翻译),编译过程主要分为四步:

  • 预处理:进行的操作包含头文件展开、消除注释、宏定义替换、条件编译等。
    gcc -E xxx.c -o xxx.i
  • 编译:将C语言代码转换成汇编语言。
    gcc -S xxx.i -o xxx.s (汇编语言编程.asm)
  • 汇编:将汇编语言转化为可重定向目标文件(即可被链接),此时已经是二进制了,但还不是可执行文件。
    as -C xxx.s -o xxx.o (单片机中变成.obj)
  • 链接:将自身程序与相关的库文件进行关联,形成真正的可执行文件。
    gcc xxx.o -o xxx.out (Unix中默认为a.out,Windows中变成xxx.exe)

头文件展开

头文件包含的一般格式有两种:

  • #include <文件名> 预处理程序直接检索C编译系统指定的目录。
  • #include “文件名” 系统首先检索当前文件目录是否有该文件,如果没有,再检索C编译系统中指定的目录。

消除注释

C语言的注释符分为两种:“//”和“/* */”。编译器在预处理阶段,会将注释全部替换为空格。

  • /* */ C风格注释,不推荐使用,因为在嵌套使用时会出现问题
  • // C++风格注释,推荐使用
#include <stdio.h>int main(void)
{// // a/*hello*//*/*******/ */ // this is errorreturn 0;
}/** 
编译时报错:
Linux_C_025.c: 在函数‘main’中:
Linux_C_025.c:14:3: 错误:expected expression before ‘/’ token
*/ // this is error
*/

宏定义替换

宏定义替换是指编译器将宏定义替换为定义时指定的文本内容,宏定义一般指以“#define”定义的语句,分为带参数和不带参数两种形式。

  • #define 定义宏
  • #undef 取消宏定义
#include <stdio.h>#define DEFINE_TESTint main(void)
{#ifdef DEFINE_TESTprintf("#ifdef: DEFINE_TEST\n");
#endif#if defined(DEFINE_TEST)printf("#if defined: DEFINE_TEST\n");
#endif#undef DEFINE_TEST#ifdef DEFINE_TESTprintf("#ifdef: DEFINE_TEST\n");
#endif#if defined(DEFINED_TEST)printf("#if defined: DEFINE_TEST\n");
#endifreturn 0;
}/** 运行结果:
#ifdef: DEFINE_TEST
#if defined: DEFINE_TEST
*/
不带参数宏
#include <stdio.h>#define PI 3.14int main(void)
{    int i;for (i=0; i<5; i++)printf("PI*%d=%.2f\n", i, i*PI);return 0;
}/** 运行结果:
PI*0=0.00
PI*1=3.14
PI*2=6.28
PI*3=9.42
PI*4=12.56
*/
带参数宏

原样替换,不会检测数据类型
#define SUM(a, b) ((a)+(b))

#include <stdio.h>#define SUM_1(a,b)     a+b
#define SUM_2(a,b)    (a)+(b)
#define SUM_3(a,b)    ((a)+(b))int main(void)
{    printf("2*SUM_1(2,3)*2=%d\n", 2*SUM_1(2,3)*2);    // 2*2+3*2printf("2*SUM_2(2,3)*2=%d\n", 2*SUM_2(2,3)*2);    // 2*(2)+(3)*2printf("2*SUM_3(2,3)*2=%d\n", 2*SUM_3(2,3)*2);    // 2*((2)+(3))*2return 0;
}/** 运行结果:
2*SUM_1(2,3)*2=10
2*SUM_2(2,3)*2=10
2*SUM_3(2,3)*2=20
*/
多行宏
#include <stdio.h>#define SUM(a,b)                \do                            \{                            \printf("a=%d, b=%d\n", a, b);    \printf("%d+%d=%d\n", a, b, a+b);        \}                            \while(0)int main(void)
{    SUM(6, 9);return 0;
}
/** 运行结果:
a=6, b=9
6+9=15
*/
常用宏
__DATE__    当前源程序的创建日期
__FILE__    当前源程序的文件名称(包括盘符和路径)
__LINE__    当前被编译代码的行号
__STDC__    返回编译器是否位标准C,若其值为1表示符合标准C,否则不是标准C
__TIME__    当前源程序的创建时间#line 修改下一行的行号

使用示例:

#include <stdio.h>int main(void)
{    printf("date: %s\n", __DATE__);printf("time: %s\n", __TIME__);printf("file: %s\n", __FILE__);printf("line: %d\n", __LINE__);printf("stdc: %d\n", __STDC__);printf("line: %d, #line=1\n", __LINE__);#line 1 "line_test.c"printf("file: %s, line: %d\n", __FILE__, __LINE__);#line 100 "one.c"printf("file: %s, line: %d\n", __FILE__, __LINE__);return 0;
}/** 运行结果:
date: Nov 24 2023
time: 14:44:24
file: Linux_C_025.c
line: 8
stdc: 1
line: 11, #line=1
file: line_test.c, line: 1
file: one.c, line: 100
*/

####“#”和“##”的区别
“#”和“##”是C语言中的预处理指令,它们只能在宏定义中使用。功能如下:

  • #:当在宏定义中出现使用#参数的形式,就是将参数的字面值转换为字符串。如#define STR(s) #s ,这个宏作用就是把s的字面值转为字符串常量。
  • ##:如果出现aa##bb##cc这种使用双井号定义的宏,作用就是形成一个新的符号aabbcc,注意是符号而不是字符串。
#include <stdio.h>#define TO_STR(s)     ("_123_"#s"_321_\n")
#define MERGE(a,b)    a##b
#define POWER(a,b)    a##e##b
#define ARGV(x)       argv##xint main(void)
{    int ARGV(1)=1, ARGV(2)=2, ARGV(3)=3;printf(TO_STR(hello world!));printf("%d\n", MERGE(12, 34));printf("\n");printf("POWER(2, 1)=%e\n", POWER(2, 1));printf("POWER(2, 1)=%d\n", POWER(2, 1));printf("POWER(2, 1)=%lf\n", POWER(2, 1));printf("\n");printf("ARGV(1)=%d, argv1=%d\n", ARGV(1), argv1);printf("ARGV(2)=%d, argv2=%d\n", ARGV(2), argv2);printf("ARGV(2)=%d, argv3=%d\n", ARGV(3), argv3);return 0;
}/** 运行结果:
_123_hello world!321
1234
POWER(2, 1)=2.000000e+01
POWER(2, 1)=2147483623
POWER(2, 1)=20.000000
ARGV(1)=1, argv1=1
ARGV(2)=2, argv2=2
ARGV(2)=3, argv3=3
*/
消除注释和宏替换的优先级

消除注释的优先级高于宏替换,我们可以使用下面的例程进行验证:

#include <stdio.h>
#define AAA //int main(void)
{AAA printf("Hello World\n");return 0;
}
/** 运行结果:
Hello World

上面的程序输出了“Hello World”,这就说明注释符没有生效“//”,那也就是说消除注释优先于宏替换。
整个过程就是,先将“//”替换成了空格,然后才进行宏替换,这样“AAA”就变成了空格,所以正常输出了“Hello World”。

条件编译

什么是条件编译?

一般情况下,源程序中所有的非注释行都需要参加编译。但是有时希望对其中一部分内容只在满足一定条件下才进行编译,即对一部分内容指定编译条件,这就是“条件编译”。使用条件编译后,在预处理阶段,如果使用条件编译的代码块满足条件,则保留;如果不满足条件,则预处理器就会将代码裁剪,使其不会在后续阶段被编译。
C语言中常见的条件编译指令有:

  • #ifdef 如果定义了,相当于“if define”
  • #ifndef 如果没有定义,相当于“if not define”
  • #if 判断是否定义,同if
  • #else 同else
  • #elif 同else if
  • #if defined() 同#ifdef
  • #endif 结束#if、#ifdef、#ifndef、#if defined()
条件编译的作用

1.我们可以使用条件编译来裁剪代码,用于快速实现某种目的,如版本维护(free,收费),功能裁剪以及代码的跨平台性。这样写出的软件一般只需维护一份代码,当制作者想要发行不同版本时,只需定义特定的宏就可以实现功能的改变。

2.一个C工程可能有多个.c文件,而.c文件可能又包含多个.h文件,难免会出现头文件重复包含的现象。这将会导致大量重复的代码被拷贝至我们的文件中,后期编译时会大大降低效率。因此我们可以使用条件编译来防止头文件重复包含,如下:

//头文件fun.h
#ifndef _FUN_H_
#define _FUN_H_// 需要定义的内容#endif

当没有包含过fun.h时,#ifndef _FUN_H_成立,定义_FUN_H_宏并保留后续内容。当未来有文件想再次包含时,由于_FUN_H_宏被定义,#ifndef _FUN_H_不成立,预处理器就将代码裁剪掉,不会进行编译,实现了预防头文件重复包含的作用。

此外,还可以使用 #pragma once 预处理指令防止头文件重复包含。

操作系统的识别

// The operating system, must be one of: (Q_OS_x)
DARWIN   - Darwin OS (synonym for Q_OS_MAC)
SYMBIAN  - Symbian
MSDOS    - MS-DOS and Windows
OS2      - OS/2
OS2EMX   - XFree86 on OS/2 (not PM)
WIN32    - Win32 (Windows 2000/XP/Vista/7 and Windows Server 2003/2008)
WINCE    - WinCE (Windows CE 5.0)
CYGWIN   - Cygwin
SOLARIS  - Sun Solaris
HPUX     - HP-UX
ULTRIX   - DEC Ultrix
LINUX    - Linux
FREEBSD  - FreeBSD
NETBSD   - NetBSD
OPENBSD  - OpenBSD
BSDI     - BSD/OS
IRIX     - SGI Irix
OSF      - HP Tru64 UNIX
SCO      - SCO OpenServer 5
UNIXWARE - UnixWare 7, Open UNIX 8
AIX      - AIX
HURD     - GNU Hurd
DGUX     - DG/UX
RELIANT  - Reliant UNIX
DYNIX    - DYNIX/ptx
QNX      - QNX
LYNX     - LynxOS
BSD4     - Any BSD 4.4 system
UNIX     - Any UNIX BSD/SYSV system#if defined(_Win32)    // 提示:WIN32有时不生效,_WIN32 或 _Win32才生效
#if defined(_linux)
#if defined(_unix)#ifdef __unix
#	if (defined(__sun) || defined(__hpux) || defined(_AIX))
#	endif
#endif#ifdef __linux
#endif#ifdef _WIN32
#endif

通过宏判断VC版本

#if _MSC_VER==1200// VC6
#else if _MSC_VER>1200// 更高的VC版本
#endif// _MSC_VER是MSVC编译器的内置宏,定义了编译器的版本。下面是一些编译器版本的_MSC_VER值
MS VC++ 10.0 _MSC_VER = 1600
MS VC++ 9.0 _MSC_VER  = 1500
MS VC++ 8.0 _MSC_VER  = 1400
MS VC++ 7.1 _MSC_VER  = 1310
MS VC++ 7.0 _MSC_VER  = 1300
MS VC++ 6.0 _MSC_VER  = 1200
MS VC++ 5.0 _MSC_VER  = 1100

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/172540.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

案例027:基于微信小程序的校园二手平台的设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

基于springboot实现智慧党建系统项目【项目源码】

基于springboot实现智慧党建系统演示 Java技术 Java是由Sun公司推出的一门跨平台的面向对象的程序设计语言。因为Java 技术具有卓越的通用性、高效性、健壮的安全性和平台移植性的特点&#xff0c;而且Java是开源的&#xff0c;拥有全世界最大的开发者专业社群&#xff0c;所以…

银行测试--------转账

转账 付款账号测试 付款账号是借记卡&#xff0c;也可以是活期存折信用卡&#xff0c;定期存折不能转出。一般在账号选择的时候进行屏蔽转出账户在销户&#xff0c;冻结&#xff0c;挂失等异常状态&#xff0c;不能进行转账付款账号金额不够 转账金额测试 0.01&#xff5e;…

C++ libcxxabi中dynamic_cast 实现

摘要&#xff1a;最近在看一个崩溃的过程中详细看了一遍cxxabi的定义&#xff0c;就想着看一些llvm中cxxabi的一些实现。本文描述了cxxabi中dynamic_cast的实现以及原理。   关键字&#xff1a;cxxabi,dynamic_cast 1 简介 C中&#xff0c;dynamic_cast用于有虚函数的继承链…

【go入门】表单

4.1 处理表单的输入 先来看一个表单递交的例子&#xff0c;我们有如下的表单内容&#xff0c;命名成文件login.gtpl(放入当前新建项目的目录里面) <html> <head> <title></title> </head> <body> <form action"/login" meth…

【Java】循环语句练习

文章目录 1. 计算5的阶乘2. 计算 1! 2! 3! 4! 5!3. 数字9 出现的次数4. 判定素数5. 求1-100之间的素数6. 求2个整数的最大公约数7. 计算分数的值8. 模拟登陆9. 输出乘法口诀表10. 求出0&#xff5e;999之间的所有“水仙花数”并输出11. 猜数字游戏&#x1f648; 1. 计算5的…

Linux系统编程 day05 进程控制

Linux系统编程 day05 进程控制 1. 进程相关概念2. 创建进程3. exec函数族4. 进程回收 1. 进程相关概念 程序就是编译好的二进制文件&#xff0c;在磁盘上&#xff0c;占用磁盘空间。程序是一个静态的概念。进程即使启动了的程序&#xff0c;进程会占用系统资源&#xff0c;如内…

FO-like Transformation

参考文献&#xff1a; [RS91] Rackoff C, Simon D R. Non-interactive zero-knowledge proof of knowledge and chosen ciphertext attack[C]//Annual international cryptology conference. Berlin, Heidelberg: Springer Berlin Heidelberg, 1991: 433-444.[BR93] Bellare M…

造一个float类型二维矩阵,并将二维矩阵存快速储到一个float*中(memcpy)

// 创建并初始化一个二维数组 std::vector<std::vector<float>> createAndInitializeArray(int rows, int cols) {std::vector<std::vector<float>> array(rows, std::vector<float>(cols));float value 0.0f;for (int i 0; i < rows; i) {…

大数据-之LibrA数据库系统告警处理(ALM-37007 Datanode进程异常)

告警解释 当出现如下情况时&#xff0c;产生该告警&#xff1a; DN实例数据目录中的postgresql.conf配置文件不存在或者其中某个配置参数不对。DN实例线程无法监听IP&#xff0c;或者无法绑定监听端口。DN实例进程没有其数据目录读写权限。 告警属性 告警ID 告警级别 可自…

4-Python与设计模式--抽象工厂模式

4-Python与设计模式–抽象工厂模式 一、快餐点餐系统 想必大家一定见过类似于麦当劳自助点餐台一类的点餐系统吧。在一个大的触摸显示屏上&#xff0c; 有三类可以选择的上餐品&#xff1a; 汉堡等主餐、小食、饮料。当我们选择好自己需要的食物&#xff0c;支付完成后&#…

软件介绍01- koodo Reader支持所有电脑平台!

1 软件简介 Koodo Reader软件是一款阅读器&#xff0c;可以阅读各种格式的文档。用来代替kindle。界面简洁&#xff0c;好看&#xff0c;阅读功能强大&#xff0c;而且可以多设备同步。 因为开源&#xff0c;所以免费。而且支持所有电脑平台&#xff01; 支持格式&#xff1a…

Android修行手册-ViewPager定制页面切换以及实现原理剖析

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分…

kafka的设计原理

文章目录 1 Kafka简介2 Kafka的架构2.1 Kafka 一些重要概念2.2 工作流程2.3 副本原理2.4 分区和主题的关系2.5 生产者2.5.1 分区可以水平扩展2.5.2 分区策略 2.6 消费者2.6.1 消费方式2.6.2 分区分配策略 2.7 数据可靠性保证2.7.1 副本数据同步策略2.7.2 ACK 应答机制2.7.3 可靠…

Java抽象类和接口(1)

&#x1f435;本篇文章将对抽象类和接口相关知识进行讲解 一、抽象类 先来看下面的代码&#xff1a; class Shape {public void draw() {System.out.println("画");} } class Cycle extends Shape {public void draw() {System.out.println("圆形");} } …

开发知识点-ArkTS-鸿蒙开发-Typescript

Typescript IED IED https://developer.harmonyos.com/cn/develop/deveco-studio/#download

打开CMD的六种方法,CMD快捷键,CMD命令大全及详解

目录 前言1. winR快捷键2、通过文本文档创建&#xff1b;3、通过C盘中的cmd.exe文件打开&#xff1b;4、创建快捷方式&#xff1b;5、通过PowerShell打开&#xff1b;6、通过文件夹导航栏打开&#xff1b; 前言 自己的电脑win键失灵了&#xff0c;想通过winR来调出cmd&#xff…

【Linux基础】Linux常见指令总结及周边小知识

前言 Linux系统编程的学习我们将要开始了&#xff0c;学习它我们不得不谈谈它的版本发布是怎样的&#xff0c;谈它的版本发布就不得不说说unix。下面是unix发展史是我在百度百科了解的 Unix发展史 UNIX系统是一个分时系统。最早的UNIX系统于1970年问世。此前&#xff0c;只有…

初始React

<!DOCTYPE html> <html> <head> <meta charset"UTF-8" /> <title>React</title> </head> <body> 了解React <!-- React是一个用于构建web和原生态交互界面的库 相对于传统DOM开发优势&#xff1a;组件化开发…

https 是否真的安全,https攻击该如何防护,https可以被抓包吗?如何防止呢?

首先解释一下什么是HTPPS 简单来说&#xff0c; https 是 http ssl&#xff0c;对 http 通信内容进行加密&#xff0c;是HTTP的安全版&#xff0c;是使用TLS/SSL加密的HTTP协议 Https的作用&#xff1a; 内容加密 建立一个信息安全通道&#xff0c;来保证数据传输的安全&am…