C语言学习系列:预处理器和内存管理

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() 函数会生成一个错误。

发布了161 篇原创文章 · 获赞 90 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_42415326/article/details/104027506