十三、预处理和宏

预处理

1. 概述

C 语言程序在编程过程中,完成的操作,可以将代码中需要进行预处理内容,例如 宏、头文件引入、条件编译······在预处理阶段加载完毕

gcc -E 目标.c -o 目标.i // 预处理,将文件中的预处理内容引入到代码中
gcc -S 目标.i -o 目标.s // 编译,预处理的 C 文件转换为编译文件
gcc -c 目标.s -o 目标.o // 汇编,将汇编文件转换为二进制文件
gcc 目标.o -o a.out // 链接,将二进制 .o 组装打包为可执行文件
2. 宏
2.1 无参宏
#include <stdio.h>

// 无参宏,可以理解为带有名称的常量,预处理阶段采用最基本的替换逻辑,
// 将代码中使用的无参宏替换为 宏 对应的数据

/*
宏要求所有英文全部大写,不同的单词之间采用下划线分隔

在预处理过程中,宏之后是什么,就代替什么
*/
#define COUNT 10

int main(int argc, char const *argv[])
{
    /*
    #define COUNT 10
        预处理结果
        printf("count : %d\n", 10);

    错误:不可加分号
    #define COUNT 10;
        预处理结果
        printf("count : %d\n", 10;);
    */
    printf("count : %d\n", COUNT);

    printf("%s:%d\n", __FILE__, __LINE__);

    /*
    #define COUNT 10
        预处理结果
        10 = 200
        无参宏数据一旦在定义中确定,后续无法修改,本身就不是变量了
        仅提供宏之后的代码内容或者数据
    */
   // 不可操作:
   // CLUNT = 200;

    return 0;
}
2.2 带参宏
#include <stdio.h>

/*
带有参数的宏,会将用户提供的所谓的宏参数替换到后续的代码中,
这里需要关注替换之后的结果情况,必要的时候可以提供小括号
保证代码的完整性
*/

#define F(a, b) (a + b)

int main(int argc, char const *argv[])
{
    printf("结果:%d\n", F(5, 7));
    /*
    #define F(a, b) a + b
        printf("结果:%d\n", F(5, 7));
        预处理替换之后
            printf("结果:%d\n", 5 * 5 + 7); // 与预期结果不同
    #define F(a, b) (a + b)
        printf("结果:%d\n", F(5, 7));
        预处理替换之后
            printf("结果:%d\n", 5 * (5 + 7)); 
            // 加入必要的括号后输出正常结果
        
    */
    printf("结果:%d\n", 5 * F(5, 7));

    return 0;
}
2.3 条件编译
2.3.1 # if
#include <stdio.h>

#define ZY 0
#define JL 1

/*
#if 条件编译
    #if 之后需要一定的条件判断,通常是 宏 或者 变量
    必须有 #define 结尾
*/
int main(int argc, char const *argv[])
{
// ZY 为 0,执行 #else 到 #endif 语句里的内容
#if ZY
    printf("今天吃鸡汤方便面!\n");
#else
    printf("今天饿着!\n");
#endif
    
// JL 为 1,执行 #if 到 #else 语句里的内容
#if JL
    printf("今天吃自选!\n");
#else
{
    printf("今天饿着!\n");
}
#endif
    return 0;
}
2.3.2 # ifdef
#include <stdio.h>

/*
定义了一个宏,但是没有提供任何的替换要求和数组
仅声明当前宏存在
*/
#define XL
/*
undef 后面对应的宏名称,表示取消当前宏定义
*/
#undef XL

int main(int argc, char const *argv[])
{
/*
#ifdef 是用于判断当前代码中指定宏是否存在,如果存在
执行 #ifdef 到 #else 之间的语句;如果不存在,执行
#else 到 #dedif 之间的语句,#else 可以不使用
*/
#ifdef XL
    printf("今天吃黄焖鸡米饭!\n");
#else
    printf("今天吃盖饭!\n");
#endif
    return 0;
}
2.3.2 # ifdef
#include <stdio.h>

/*
当前定义了一个宏,但是没有提供任何的替换要求和数组
仅声明当前宏存在
*/
#define XL

#undef XL 

int main(int argc, char const *argv[])
{
/*
#ifdef 表示如果指定宏没有定义执行 #ifdef 到 #else 或者到 #endif 内容
如果对应宏定义了,执行 #else 到 #endif 之间的内容,
或者没有 #else 语句的话,程序就不执行任何 #ifndef 包括的内容
*/
#ifndef XL
    printf("今天吃黄焖鸡米饭\n");
#else
{
    printf("今天吃焖面\n");
}
#endif 
    return 0;
}
2.3.3 # undef

取消宏定义

#define MY // 定义了一个宏
#undef MY // 取消当前定义宏
#include <stdio.h>

// 定义了宏 VALUE 替换数为 100
#define VALUE 100

int test();

int main(int argc, char const *argv[])
{
    printf("main value : %d\n", VALUE);
    test();

// 取消 VALUE 宏,从取消位置开始,整个带个代码无法再次使用对应的宏
#undef VALUE 

// 重新定义了一个 宏,宏对应的范围是从定义位置开始,到文件末尾
#define VALUE 10000
    return 0;
}

int test()
{
    printf("test value : %d\n", VALUE);
}
2.4 自定义头文件
2.4.1 头文件编译

头文件是 .h 文件,主要是 C/C++ 的声明文件

  • 定义宏
  • 声明数据类型,数据类型别名定义
  • 声明函数

固定的文档格式

#ifndef 头文件名称英文全拼大写
#define 头文件名称英文全拼大写

// 头文件内容

#endif

my.h

#ifndef MY
#define MY

// 头文件内容

#endif
#include "my.h"
#include "my.h"

int mian() {}

以上代码预处理之后,以下是上面代码处理过程

#ifndef MY // 当前 MY 宏没有定义,执行 #ifndef 到 #endif 之间的内容
#define MY // 当前田间编译执行之后,代码已经定义了 MY 宏

// 头文件内容

#endif
#ifndef MY // 再次引入对应的头文件,MY 已经存在,#ifdef 到 #endif 之间内容不参与编译
		   // 不会出现重复引入头文件、也不会出现类型和函数的多次声明
#define MY

// 头文件内容

#endif
int main() {}
2.4.2 头文件案例

在这里插入图片描述

student.h

#ifndef STUDENT
#define STUDENT
/*
头文件中的内容
    1. 声明的数据类型
    2. 声明的函数
    3. 引入其他头文件
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct student
{
    int id;
    char name[32];
    int age;
} Student;

/*
根据用户提供的数据内容,创建一个 Student 数据,数据所在内存空间为【堆区】

@param id   学生 ID
@param name 学生姓名
@param age  学生年龄
@return 创建成功返回学生在内存堆区的地址,否则返回 NULL
*/
Student *create_student(int id, char *name, int age);

/*
展示学生相关数据

@param stu 学生数据在内存中的首地址
*/
void show(Student *stu);

/*
释放学生占用的内存空间

@param stu 学生数据所在内存【堆区】空间首地址
*/
void free_student(Student *stu);

#endif

student.c

/*
<> 方式引入头文件,是直接在系统指定库中搜索目标头文件
"" 方式引入头文件,是在当前文件夹中搜索目标头文件,如果没有找到
到系统指定库文件夹搜索

一般情况下,"" 引入头文件都是自定义头文件
*/
#include "student.h"

Student *create_student(int id, char *name, int age)
{
    Student *stu = (Student *)malloc(sizeof(Student));

    if (NULL == stu)
    {
        return NULL;
    }

    memset(stu, 0, sizeof(Student));

    stu->id = id;
    strcpy(stu->name, name);
    stu->age = age;

    return stu;
}

void free_student(Student *stu)
{
    free(stu);
}

void show(Student *stu)
{
    printf("ID: %d, Name: %s, Age: %d\n",
           stu->id, stu->name, stu->age);
}

main.c

/*
虽然在 student.h 已经存在对应的头文件,但是建议系统头文件
如果在不同的文件中,需要再次引入,避免丢失必要的头文件内容
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "student.h"

int main(int argc, char const *argv[])
{
    Student *stu = create_student(1, "JH", 56);

    show(stu);
    free_student(stu);

    return 0;
}

编译

gcc main.c student.c
# 得到 a.out 可执行文件
  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值