文章目录
预处理
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 可执行文件