C 预处理器是编译过程中的一个文本替换工具,它们会指示编译器在实际编译之前完成所需的预处理。它不是编译器的组成部分,简写为 CPP。
所有的预处理器命令都是以井号(#)开头。所有的预处理器指令如下:
预处理器指令 | 描述 |
#define | 定义宏 |
#include | 引用一个头文件 |
#undef | 取消已定义的宏 |
#ifdef | 如果宏已经定义,则返回真 |
#ifndef | 如果宏没有定义,则返回真 |
#if | 如果给定条件为真,则编译下面的代码 |
#else | #if 的替代方案 |
#elif | 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面的代码 |
#endif | 结束一个 #if……#else 条件编译块 |
#error | 当遇到标准错误时,输出错误消息 |
#pragma | 使用标准化方法,向编译器发布特殊的命令到编译器中 |
预处理器实例
#ifndef MESSAGE
#define MESSAGE "You wish!"
#endif
这个指令告诉 CPP 只有当 MESSAGE 未定义时,才定义 MESSAGE。
#ifdef DEBUG
/* 调试语句 */
#endif
这个指令告诉 CPP 如果定义了 DEBUG,则执行处理语句。在编译时,若向 gcc 编译器传递了 -DDEBUG 开关量,这个指令就非常有用。它定义了 DEBUG(调试),可以在编译期间随时开启或关闭调试。
预定义宏
对于下列ASNI C 预定义的宏,可以直接使用,不必用 #define 定义,但是不能修改:
宏 | 描述 |
__DATE__ | 当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量。 |
__TIME__ | 当前时间,一个以 "HH:MM:SS" 格式表示的字符常量。 |
__FILE__ | 这会包含当前文件名,一个字符串常量。 |
__LINE__ | 这会包含当前行号,一个十进制常量。 |
__STDC__ | 当编译器以 ANSI 标准编译时,则定义为 1。 |
预处理器运算符
宏延续运算符(\),该运算符允许一个宏定义时换行:
#define max(x,y) \
(x)>(y)? (x) : (y)
字符串常量化运算符(#), 该运算符可以把宏的参数转换为字符串常量。
#define message_for(a, b) printf(#a " and " #b ": We love you!\n")
标记粘贴运算符(##),该运算符允许在宏定义中两个独立的标记被合并为一个标记(标记就是宏定义中的标识符):
#include <stdio.h>
#define tokenpaster(n) printf ("token" #n " = %d", token##n)
int main(void)
{
int token34 = 40;
tokenpaster(34);
return 0;
}
根据宏定义,tokenpaster(34) 即 printf("token34 = %d", token34);
defined() 运算符
预处理器 defined 运算符是用在常量表达式中的,用来确定一个标识符是否已经使用 #define 定义过。如果指定的标识符已定义,则值为真(非零)。如果指定的标识符未定义,则值为假(零)。
参数化的宏
通过参数化的宏可以进行函数的模拟:
#include <stdio.h>
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void)
{
printf("Max between 20 and 10 is %d\n", MAX(10, 20));
return 0;
}
使用带有参数的宏之前,除预定义的宏之外必须使用 #define 指令定义,参数列表是括在圆括号内,且必须紧跟在宏名称的后边。宏名称和左圆括号之间不允许有空格
C 中的动态内存管理
内存的分配和管理函数 (在头文件stdlib.h中):
函数原型 | 描述 |
void *calloc(int num,int size); | 在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了 num*size 个字节长度的内存空间,并且每个字节的值都是0。 |
void free(void*address); | 该函数释放 address 所指向的内存块,释放的是动态分配的内存空间。 |
void *malloc(int num); | 在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。 |
void *realloc(void *address,int newsize); | 该函数重新分配内存,把内存扩展到 newsize。 |
注意:void * 类型表示未确定类型的指针。C、C++ 规定 void * 类型可以通过类型转换强制转换为任何其它类型的指针。
动态分配内存
当事先不知道需要存储的长度时,就需要定义一个指针,该指针指向未定义所需内存大小的字符,那么后续可以再根据需求来重新调整分配内存:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *p;
/* 动态分配内存 */
p = (char *)malloc( 200 * sizeof(char) );
//p = (char *)calloc(200, sizeof(char));
if(p == NULL)fprintf(stderr, "错误-无法分配所需的内存\n");
else strcpy(p, "Zara ali a student in class 10th");
printf("Description: %s\n", p);
}
重新调整内存的大小
当程序退出时,操作系统会自动释放所有分配给程序的内存,但是,建议在不需要内存时,都应该调用函数 free() 来释放内存。或者通过调用函数 realloc() 来增加或减少已分配的内存块的大小:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *p;
/* 动态分配内存 */
p = (char *)malloc( 30 * sizeof(char) );
if( p == NULL )fprintf(stderr, "错误-无法分配所需的内存\n");
else strcpy( p, "Zara ali a student.");
/* 假设想要存储更大的描述信息,使用realloc函数 */
p = (char *) realloc( p, 100 * sizeof(char) );
if(p == NULL )fprintf(stderr, "错误-无法分配所需的内存\n");
else strcat( p, "She is in class 10th");
printf("Description: %s\n", p);
/* 使用 free() 函数释放内存 */
free(p);
}
如果不重新分配更多内存给p,那么由于内存不足,strcat() 函数会生成一个错误。