学习C – C预处理
源代码的预处理发生在编译为机器指令之前。
预处理阶段可以执行由#
符号启动的预处理指令指定的操作范围。
预处理阶段在编译之前处理C源代码。
在预处理阶段后,将分析和执行所有指令,并且所有预处理指令将不再出现在源代码中。
#include
和 #define
是两个流行的指令。
用#define语句定义常量。
#include <stdio.h>
#define PI 3.14159
int main(void)
{
float area, circum, radius;
printf("What is the radius of your pizza?\n");
scanf("%f", &radius);
area = PI * radius * radius;
circum = 2.0 * PI *radius;
printf("Your basic pizza parameters are as follows:\n");
printf("circumference = %1.2f, area = %1.2f\n", circum,
area);
return 0;
}
上面的代码生成以下结果。
包括头文件
你完全熟悉这样的语句:
#include <stdio.h>
上面的代码将标准库头文件的内容带入您的程序。
#include指令引入到程序中的文件也可能包含#include指令。
定义头文件
您可以定义自己的头文件,通常使用扩展名为.h。
头文件不应包括实现。
您创建头文件以包含声明。
所有的函数定义和全局变量都放在扩展名为.c的源文件中。
您可以将函数原型,结构类型定义,符号定义,extern语句和typedefs放在头文件中。
一个典型的例子可能是:
#include "myfile.h"
外部变量
要使用另一个文件的全局变量,请使用extern关键字将该变量声明为当前源文件的外部变量。
例如,假设您的语句在另一个文件中定义了全局变量:
int number = 0; double in_to_mm = 2.54;
在要访问这些文件的源文件中,指定这些变量名称是外部的:
extern int number; extern double in_to_mm;
这些语句不会创建这些变量。
他们通知编译器这些名称被定义在哪里。
静态函数
您可以通过将其声明为静态来确保函数仅在您定义它的源文件中可见。
例如:
static double average(double x, double y) { return (x + y) / 2.0; }
程序源代码中的替换
define预处理器指令的一般形式如下:
#define identifier sequence_of_characters
这里,标识符符合C中标识符的通常定义。
在#define语句中定义字符串。
#include <stdio.h>
#define PRAISE "You are an extraordinary being."
int main(void)
{
char name[40];
printf("What"s your name? ");
scanf("%s", name);
printf("Hello, %s. %s\n", name, PRAISE);
return 0;
}
上面的代码生成以下结果。
宏
宏是预处理器,允许可以称为多个参数化替换。
这涉及到用于令牌标识符的字符序列的替换。
一个例子将使这更容易理解:
#define Print(My_var) printf_s("%d", My_var)
My_var
是一个参数名称,可以指定一个字符串。
该指令提供两个替代级别。
代码中的Print(My_var)的出现将被紧随其后的字符串以及您为My_var指定的任何参数所代替。
例如,你可以写下列内容:
Print(ival);
这将在预处理期间转换为此语句:
printf_s("%d", ival);
宏函数
刚才讨论的替代方式的一般形式如下:
#define macro_name( list_of_identifiers ) substitution_string
以下代码显示了如何使用以下指令定义最多生成两个值的宏:
#define max(x, y) x>y ? x : y
然后,您可以将该语句放在程序中:
int result = max(myval, 99);
这将在预处理期间扩展,以生成以下代码:
int result = myval>99 ? myval : 99;
条件编译
由于在先前的#define指令中已创建标识符,因此我们可以测试是否存在标识符。
它采取以下形式:
#if defined identifier // Statements... #endif
如果指定的标识符在此之前由#define指令定义,则#if之后的语句将包含在程序代码中,直到指令#endif为止。
如果未定义标识符,则#if和#endif之间的语句将被跳过。
您可以测试缺少标识符。
该指令的一般形式是:
#if !defined identifier // Statements... #endif
这里,如果以前没有定义标识符,那么#if下面的#endif之后的语句将被包含。
这可以避免重复的功能或其他代码和指令。
这个机制只是把你想避免重复的代码块的顶部和尾部如下所示:
#if !defined block1 #define block1 /* Statements you do not */ /* want to occur more than once. */ #endif
测试多个条件
您可以使用逻辑运算符来测试是否定义了多个标识符。例如:
#if defined block1 && defined block2 // Statements... #endif
如果先前已经定义了block1和block2,则此值将为true。
您可以使用||和!运算符结合&&。
未定义标识符
我们可以取消定义您之前定义的标识符。
这是使用指令实现的,例如:
#undef block1
如果先前定义了block1,则该指令不再被定义。
测试标识符的特定值
您可以使用#if指令的形式来测试常量表达式的值。
如果常量表达式的值不为零,则下列语句直到下一个#endif包含在程序代码中。
如果常量表达式计算为零,则会跳过下一个#endif的下一个语句。
#if指令的一般形式是:
#if constant_expression
您可能会有以下语句序列,例如:
#if CPU == Intel printf("Performance should be good.\n" ); #endif
多重选择
你有#else指令。
如果#if条件失败,它会标识要执行的一组指令或要包括的语句,例如:
#if CPU == Intel printf_s("Performance should be good.\n" ); #else printf_s("Performance may not be so good.\n" ); #endif
#elif指令具有一般形式:
#elif constant_expression
标准预处理宏
__DATE__宏会以Mmm dd yyyy格式生成日期的字符串表示形式。
这里Mmm是个月,如1月,2月等等。
一对字符dd是一对数字1到31的形式,其中单位数字前面有一个空格。
最后,例如,yyyy是2012年的四位数字。
__TIME__提供一个包含时间值的字符串,格式为hh:mm:ss。
时间是编译器执行时,而不是当程序运行时。
当您的程序上次使用此语句进行编译时,可以使用此宏来记录输出:
printf_s("Program last compiled at %s on %s\n", __TIME__, __DATE__ );
执行此语句将产生类似于以下内容的输出:
Program last compiled at 15:27:01 on Nov 24 2015
__FILE__宏将当前源文件的名称表示为字符串文字。
__LINE__宏包含与当前行号相对应的整数常量。
您可以结合使用__FILE__宏来识别源代码中哪个特定事件或错误已被检测到。
例如:
fprintf(stderr, "Failed to open file in %s line %d\n", __FILE__, __LINE__);