延安网站设计公司,医疗器械股票龙头股票,在国外网站做中国旅游推广,百度官方版下载Linux C语言 25-预处理操作
本节关键字#xff1a;C语言编译过程、预处理、多行宏定义、通过宏判断操作系统、通过宏判断VC版本 相关C库函数#xff1a;main#xff0c;printf
什么是预处理#xff1f;
预处理是C语言源码编译中重要的一步。用C语言编写的代码不能直接被…Linux C语言 25-预处理操作
本节关键字C语言编译过程、预处理、多行宏定义、通过宏判断操作系统、通过宏判断VC版本 相关C库函数mainprintf
什么是预处理
预处理是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.outWindows中变成xxx.exe
头文件展开
头文件包含的一般格式有两种
#include 文件名 预处理程序直接检索C编译系统指定的目录。#include “文件名” 系统首先检索当前文件目录是否有该文件如果没有再检索C编译系统中指定的目录。
消除注释
C语言的注释符分为两种“//”和“/* */”。编译器在预处理阶段会将注释全部替换为空格。
/* */ C风格注释不推荐使用因为在嵌套使用时会出现问题// C风格注释推荐使用
#include stdio.hint 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 (i0; i5; i)printf(PI*%d%.2f\n, i, i*PI);return 0;
}/** 运行结果
PI*00.00
PI*13.14
PI*26.28
PI*39.42
PI*412.56
*/带参数宏
原样替换不会检测数据类型 #define SUM(a, b) ((a)(b))
#include stdio.h#define SUM_1(a,b) ab
#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*23*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)*210
2*SUM_2(2,3)*210
2*SUM_3(2,3)*220
*/多行宏
#include stdio.h#define SUM(a,b) \do \{ \printf(a%d, b%d\n, a, b); \printf(%d%d%d\n, a, b, ab); \} \while(0)int main(void)
{ SUM(6, 9);return 0;
}
/** 运行结果
a6, b9
6915
*/常用宏
__DATE__ 当前源程序的创建日期
__FILE__ 当前源程序的文件名称(包括盘符和路径)
__LINE__ 当前被编译代码的行号
__STDC__ 返回编译器是否位标准C,若其值为1表示符合标准C否则不是标准C
__TIME__ 当前源程序的创建时间#line 修改下一行的行号使用示例
#include stdio.hint 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, #line1\n, __LINE__);#line 1 line_test.cprintf(file: %s, line: %d\n, __FILE__, __LINE__);#line 100 one.cprintf(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, #line1
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.000000e01
POWER(2, 1)2147483623
POWER(2, 1)20.000000
ARGV(1)1, argv11
ARGV(2)2, argv22
ARGV(2)3, argv33
*/消除注释和宏替换的优先级
消除注释的优先级高于宏替换我们可以使用下面的例程进行验证
#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_VER1200// VC6
#else if _MSC_VER1200// 更高的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