08-预处理器
1. 预处理指令
1.1 预处理指令
C 预处理程序在编译之前使用 #
指令在程序源代码中进行替换。
例如,在编译程序之前,行 #include <stdio.h>
被 stdio.h
头文件的内容替换。
预处理程序指令及其用法:
#include
包括头文件。#define
,#undef
定义和取消定义宏。#ifdef
,#ifndef
,#if
,#else
,#elif
,#endif
条件编译。#pragma
用于指示编译器完成一些特定的动作#error
,#warning
输出错误或警告消息错误停止编译。
不要在
#
指令的末尾放一个分号;
字符。
以下哪个预处理指令允许在源代码中包含头文件?
A. #including
B. #define
C. #ifdef
D. #include
✅
1.2 #include 指令
#include
指令是用来在程序中包含头文件的。一个头文件声明了一个库函数和宏的集合.
一些有用的 C 语言库:
- stdio: 输入/输出函数,包括 printf 和文件操作。
- stdlib: 内存管理和其他实用工具
- string: 处理字符串的函数
- errno: errno 全局变量和错误代码宏
- math: 常用的数学函数
- time: 时间/日期实用程序
对应的库的头文件按惯例以 .h
结尾。如果头文件要在编译器的包含路径中搜索,#include
指令希望头文件名周围有括号 <>
。
一个用户定义的头文件也被赋予 .h
的扩展名,但要用引号 "
来表示,如 "myutil.h"
。当使用引号时,该文件将在源代码目录中被搜索到。
例如:
#include <stdio.h>
#include “myutil.h”
一些开发者使用 .hpp
扩展名来表示头文件。
填空,包括 stdio.h
头文件。
___ <stdio.h>
#include
1.3 #define 指令
C 语言中,可以用 #define
定义一个标识符来表示一个常量。
其特点是:定义的标识符不占内存,只是一个临时的符号,预编译后这个符号就不存在了。
预编译又叫预处理。预编译不是编译,而是编译前的处理。这个操作是在正式编译之前由系统自动完成的。
用 #define
定义标识符的一般形式为:
#define 标识符 常量 //注意, 最后没有分号
#define
又称宏定义,标识符为所定义的宏名,简称宏。标识符的命名规则与前面讲的变量的命名规则是一样的。#define
的功能是将标识符定义为其后的常量。一经定义,程序中就可以直接用标识符来表示这个常量。
例如:
#include <stdio.h>
#define PI 3.14
#define AREA(r) (PI*r*r)
int main() {
float radius = 2;
printf("%3.2f\n", PI);
printf("Area is %5.2f\n", AREA(radius));
printf("Area with radius + 1: %5.2f\n", AREA(radius+1));
return 0;
}
#include <stdio.h>
#define PI 3.14
#define AREA(r) (PI*r*r)
int main() {
float radius = 2;
printf("%3.2f\n", PI);
printf("Area is %5.2f\n", AREA(radius));
printf("Area with radius + 1: %5.2f\n", AREA(radius+1));
return 0;
}
在编译之前,预处理程序会对每个宏标识符进行扩展。在这种情况下,PI 的每一次出现都被替换为 3.14,AREA(arg) 被替换为PI*arg*arg
的表达。发送给编译器的最终代码将已经有了常量值。
这不是我们可能期望的! 然而,如果你考虑到 #define
严格通过替换文本来工作,你会看到 AREA(radius+1)
变成 PI*radius+1*radius+1
,也就是 3.14*2+1*2+1
。
解决这个问题的办法是将每个参数用圆括号 ()
括起来,以获得正确的运算顺序。
例如:
#define AREA(r) (PI*(r)*(r))
#include <stdio.h>
#define PI 3.14
#define AREA(r) (PI*(r)*(r))
int main() {
float radius = 2;
printf("%3.2f\n", PI);
printf("Area is %5.2f\n", AREA(radius));
printf("Area with radius + 1: %5.2f\n", AREA(radius+1));
return 0;
}
该代码产生的输出结果: "Area with radius + 1: 28.26"
输入代码,定义一个用于计算参数平方的预处理程序宏:
___ SQR(___) ((x)*(x))
#define
x
1.4 格式化预处理指令
使用预处理程序指令时,#
必须是一行中的第一个字符。但是,在 #
之前以及 #
与指令之间可以有任意数量的空格。
如果 #
指令很长,则可以使用 \
字符将定义扩展到多行。
例如:
#include <stdio.h>
#define VERY_LONG_CONSTANT \
23.678901
#define MAX 100
#define MIN 0
# define SQUARE(x) \
x*x
int main() {
printf("%f\n", VERY_LONG_CONSTANT * SQUARE(2));
return 0;
}
#include <stdio.h>
#define VERY_LONG_CONSTANT \
23.678901
#define MAX 100
#define MIN 0
# define SQUARE(x) \
x*x
int main() {
printf("%f\n", VERY_LONG_CONSTANT * SQUARE(2));
return 0;
}
在
#
之前和#
与指令之间可以有任意数量的空格。
输入字符,将宏的定义扩展到多行:
#define A_LONG_MACRO ___
42.4256789
\
1.5 预定义的宏定义
除了定义你自己的宏之外,还有几个标准的预定义宏,它们在 C 程序中总是可用的,不需要 #define
指令,如:
__DATE__
当前日期是一个字符串,格式为Mm dd yyyy
__TIME__
当前的时间是一个字符串,格式为hh:mm:ss
__FILE__
当前的文件名,字符串。__LINE__
当前的行号,是一个int值。__STDC__
1
例如:
char curr_time[10];
char curr_date[12];
int std_c;
strcpy(curr_time, __TIME__);
strcpy(curr_date, __DATE__);
printf("%s %s\n", curr_time, curr_date);
printf("This is line %d\n", __LINE__);
std_c = __STDC__;
printf("STDC is %d", std_c);
#include <stdio.h>
#include <string.h>
int main() {
char curr_time[10];
char curr_date[12];
int std_c;
strcpy(curr_time, __TIME__);
strcpy(curr_date, __DATE__);
printf("%s %s\n", curr_time, curr_date);
printf("This is line %d\n", __LINE__);
std_c = __STDC__;
printf("STDC is %d", std_c);
return 0;
}
以下哪项是代表当前日期的预定义宏?
A. __FILE__
B. __TIME__
C. __DATE__
✅
2. 条件编译指令
2.1 #ifdef,#ifndef 和 #undef 指令
#ifdef
,#ifndef
和 #undef
指令对使用 #define
创建的宏起作用。
例如,如果两次定义相同的宏,将存在编译问题,因此你可以使用 #ifdef
指令进行检查。
或者,如果你想重新定义宏,则可以先使用 #undef
。
下面的程序演示了这些指令:
#include <stdio.h>
#define RATE 0.08
#ifndef TERM
#define TERM 24
#endif
int main() {
#ifdef RATE /* this branch will be compiled */
#undef RATE
printf("Redefining RATE\n");
#define RATE 0.068
#else /* this branch will not be compiled */
#define RATE 0.068
#endif
printf("%f %d\n", RATE, TERM);
return 0;
}
#include <stdio.h>
#define RATE 0.08
#ifndef TERM
#define TERM 24
#endif
int main() {
#ifdef RATE /* this branch will be compiled */
#undef RATE
printf("Redefining RATE\n");
#define RATE 0.068
#else /* this branch will not be compiled */
#define RATE 0.068
#endif
printf("%f %d\n", RATE, TERM);
return 0;
}
因为 RATE 是在顶部定义的,所以只有 #ifdef
子句会被编译。
在预处理过程中,当 #ifdef RATE
为假时,可选的 #else
分支会被编译。需要一个 #endif
来关闭该代码块。
一个
#elif
指令就像一个else if
,可以用来在#else
之后提供额外的选择。
填空,定义TERM宏(如果未定义)。
#___TERM
#define TERM 24
#___
ifndef
endif
2.2 条件编译指令
代码段的条件编译由一组指令控制:#if
,#else
,#elif
和 #endif
。
例如:
#define LEVEL 4
int main() {
#if LEVEL > 6
/* do something */
#elif LEVEL > 5
/* else if branch */
#elif LEVEL > 4
/* another else if */
#else
/* last option here */
#endif
return 0;
}
#include <stdio.h>
#define LEVEL 4
int main() {
#if LEVEL > 6
/* do something */
#elif LEVEL > 5
/* else if branch */
#elif LEVEL > 4
/* another else if */
#else
/* last option here */
#endif
return 0;
}
在有些情况下,这种条件性编译是有用的,但这种类型的代码应该少用。
defined()
预处理器操作符可以和 #if
一起使用,如:
#if !defined(LEVEL)
/* statements */
#endif
#if !defined(LEVEL)
/* statements */
#endif
#if
和 if
语句是不能互换的。#if
使用预处理器可用的数据进行评估,然后只发送真正的分支代码进行编译。
一个
if
语句使用在运行时提供数据,并有可能分支到任何else
子句。
如果没有定义 LEVEL 宏,请填入空白处以包括 printf 语句:
#if ___(LEVEL)
printf("hello");
#___
!defined
endif
3. 预处理预算符
3.1 预处理运算符
C 预处理器提供以下运算符。
# 运算符
# 宏运算符
称为字符串化或字符串化运算符,它告诉预处理器将参数转换为字符串常量。
参数两侧的空白将被忽略,转义序列将被识别。
例如:
#define TO_STR(x) #x
printf("%s\n", TO_STR( 123\\12 ));
#include <stdio.h>
#define TO_STR(x) #x
int main() {
printf("%s\n", TO_STR( 123\\12 ));
return 0;
}
下面代码输出是?
#define STR(x) #x
#define STRLEN(x) strlen(x)
printf("%d", STRLEN(STR(12345)));
5
3.2 ## 运算符
与 #运算符
类似,##运算符
可用于类函数宏(带参宏)的替换部分。##运算符
可以把两个记号组合成一个记号。
例如:
#define VAR(name, num) name##num
int x1 = 125;
int x2 = 250;
int x3 = 500;
printf("%d\n", VAR(x, 3));
#include <stdio.h>
#define VAR(name, num) name##num
int main() {
int x1 = 125;
int x2 = 250;
int x3 = 500;
printf("%d\n", VAR(x, 3));
return 0;
}
下面代码输出是?
#define CONCAT(x, y) x##y
int x = 4;
int y = 5;
int CONCAT(x,y) = x + y;
printf("%d", xy);
9
4. 小测验
4.1 练习-1
定义一个将其参数增加三倍的宏。然后使用 num
变量作为其参数并输出结果。
___ TRIPLE(x) (x) * 3
int num = 42;
printf("%d", TRIPLE(___));
#define
num
4.2 练习-2
如果定义了 TRIPLE 宏,则定义宏 SQR,否则定义 TRIPLE。
#___ TRIPLE
#define SQR(x) (x) * (x)
#___#define TRIPLE(x) (x) * 3
#___
ifdef
else
endif
4.3 练习-3
预处理器的哪个阶段起作用?
A. 编译前✅
B. 编译后
C. 执行时
4.4 练习-4
下面代码的输出是哪一项?
#include <stdio.h>
#define T 42
int main()
{
int T = 8;
printf("%d ", T);
return 0;
}
A. 8
B. 编译错误 Compile Error✅
C. 42
D. 0
4.5 练习-5
下面代码的输出是哪一项?
#define sqr(x) x*x
int x = 16/sqr(4);
printf("%d", x);
A. 16
B. 0
C. 1✅
D. 4
公众号:AI悦创【二维码】
AI悦创·编程一对一
AI悦创·推出辅导班啦,包括「Python 语言辅导班、C++ 辅导班、java 辅导班、算法/数据结构辅导班、少儿编程、pygame 游戏开发、Linux、Web、Sql」,全部都是一对一教学:一对一辅导 + 一对一答疑 + 布置作业 + 项目实践等。当然,还有线下线上摄影课程、Photoshop、Premiere 一对一教学、QQ、微信在线,随时响应!微信:Jiabcdefh
C++ 信息奥赛题解,长期更新!长期招收一对一中小学信息奥赛集训,莆田、厦门地区有机会线下上门,其他地区线上。微信:Jiabcdefh
方法一:QQ
方法二:微信:Jiabcdefh
- 0
- 0
- 0
- 0
- 0
- 0