宏&条件编译&编译流程
·
宏
在C语言中,宏是通过#define进行声明的
无参数宏
代码案例:
#include<stdio.h>
#define PI 3.14159 //整个项目中 PI就等价于 3.14159
int main(int argc, char const *argv[])
{
printf("PI:%0.1f\n",PI); //PI在此处等价于 3.14159
return 0;
}
有参数宏
宏不说话,只是一味的替换。
代码案例:
#include<stdio.h>
#define ADD(a,b) a+b //所有 ADD(a,b)均会被替换为 a+b
#define ADD2(a,b) (a+b) //所有 ADD(a,b)均会被替换为 (a+b)
int main(int argc, char const *argv[])
{
printf("ADD(3,4)=%d\n",ADD(3,4)); // ADD(3,4)==>被替换为 3+4
printf("ADD(3,4)*3=%d\n",ADD(3,4)*3); // ADD(3,4)*3 ==>3+4*3
printf("ADD(3,4)*ADD(3,4)=%d\n",ADD(3,4)*ADD(3,4)); //==>3+4*3+4
printf("ADD2(3,4)*ADD2(3,4)=%d\n",ADD2(3,4)*ADD2(3,4)); //(3+4)*(3+4)
return 0;
}
条件编译

1.如果存在 ifdef
#include <stdio.h>
#define flag
int main(int argc, char const *argv[])
{
#ifdef flag // flag如果存在,那么执行ifdef后面的,如果flag不存在,那么执行#else后面的
printf("今天周一,需要开会\n");
#else
printf("今天周二,需要开会\n");
#endif
return 0;
}
2.如果不存在 ifndef
#include <stdio.h>
#define flag
int main(int argc, char const *argv[])
{
#ifdef flag // flag如果存在,那么执行ifdef后面的,如果flag不存在,那么执行#else后面的
printf("今天周一,需要开会\n");
#else
printf("今天周二,需要开会\n");
#endif
return 0;
}
3.判断是否成立:if
C语言编译流程
1.预处理

gcc -E main.c -o main.i # 生成预处理后的文件 main.i
2.编译

gcc -S main.i -o main.s # 生成汇编文件 main.s
3.汇编

gcc -c main.s -o main.o # 生成目标文件 main.o
4.链接

gcc main.o -o program # 将文件链接为可执行程序
五、关键字和修饰符分析 (Keywords and Modifiers)
- extern:
- 这是一个链接规范,表明该函数是在其他地方定义的(通常是在 C 标准库中,如 string.h 中声明)。当前文件(包含这个声明的源文件)只是声明要使用这个函数,而不是定义它。在头文件中的函数声明前加 extern 是常见做法,但即使不加,默认也是 extern 的。
- __restrict:
- 这是一个C99标准引入的限定符(编译器扩展,标准中为 restrict)。
- 它向编译器承诺:指针 __dest 和 __src 所指向的内存区域不会重叠。
- 为什么重要? 这个承诺允许编译器进行更大程度的优化。如果没有 restrict,编译器必须假设 src 和 dest 可能指向同一块内存(例如 strcpy(s, s+5)),因此必须生成能处理这种重叠情况的、更保守的代码。使用 restrict 后,编译器可以生成更高效(例如使用块内存复制指令)但无法处理重叠的代码。
- 程序员的责任:调用者必须确保源区和目标区确实不重叠,否则行为是未定义的。
- __THROW:
- 这通常是一个宏,用于指定函数的异常规范。
- 在 GNU C 库(glibc)中,__THROW 通常被定义为 throw() 或 __attribute__((__nothrow__))。
- 它的意思是:保证这个函数不会抛出任何异常。这对于C++代码调用C函数非常重要,因为C++有异常处理机制,而C没有。它告诉C++编译器,调用这个函数是安全的,不需要准备处理异常。
- __nonnull ((1, 2)):
- 这是另一个编译器属性/宏(通常是 GCC 的特性 __attribute__((nonnull(...))))。
- 它表示:第1个和第2个参数绝对不能是空指针(NULL)。
- 作用:
-
- 编译时检查:如果编译器能够确定在调用时传入了NULL(例如 strcpy(NULL, "hello")),它可能会产生一个警告。
- 优化提示:编译器可以假设这些指针是有效的,从而进行一些优化。
- 如果调用者违反了此规则,传入了NULL,那么函数的行为是未定义的,几乎必然导致程序崩溃(段错误)。
1.strcpy与strncpy
strcpy 函数
函数原型
char *strcpy(char *dest, const char *src);
参数解释
- dest: 目标字符串缓冲区指针,用于存放复制后的字符串
- src: 源字符串指针,要复制的字符串内容
返回值解释
- 返回指向目标字符串 dest 的指针
- 便于链式调用,如 printf("%s", strcpy(dest, src))
特点与风险
- 不安全:不检查目标缓冲区大小,容易导致缓冲区溢出
- 自动添加终止符:总是会在复制完成后添加 \0
- 要求内存不重叠:源和目标内存区域不能重叠
strncpy 函数
函数原型
char *strncpy(char *dest, const char *src, size_t n);
参数解释
- dest: 目标字符串缓冲区指针
- src: 源字符串指针
- n: 最多复制的字符数(包括空间给终止符)
返回值解释
- 返回指向目标字符串 dest 的指针
- 便于链式调用
特点与注意事项
- 相对安全:可以限制复制的最大字符数
- 不会自动添加终止符:如果复制的字符数达到 n,不会添加 \0
- 可能填充零:如果源字符串长度小于 n,会用 \0 填充剩余空间
- 必须手动确保终止:使用后需要手动添加 \0
2.strcat与strncat
strcat - 字符串拼接
函数原型:
c
char *strcat(char *dest, const char *src);
功能特点:
- 将 src 字符串完整拼接到 dest 字符串末尾
- 自动覆盖 dest 原来的终止符 '\0'
- 在新的字符串末尾自动添加 '\0'
- 返回 dest 的指针(便于链式调用)
strncat - 安全字符串拼接
函数原型:
c
char *strncat(char *dest, const char *src, size_t n);
功能特点:
- 将 src 字符串的前 n 个字符拼接到 dest 末尾
- 自动添加终止符 '\0'
- 比 strcat 更安全,可防止缓冲区溢出
- 返回 dest 的指针
3.strcmp与strncmp
strcmp - 字符串比较
函数原型:
int strcmp(const char *str1, const char *str2);
功能特点:
- 比较两个字符串的完整内容
- 按字典顺序逐个字符比较
- 返回比较结果:
- 0:两个字符串相等
- < 0:str1 小于 str2
- > 0:str1 大于 str2
strncmp - 安全字符串比较
函数原型:
int strncmp(const char *str1, const char *str2, size_t n);
功能特点:
- 比较两个字符串的前 n 个字符
- 只比较指定长度的内容
- 返回比较结果(同strcmp)
- 更安全,可防止越界访问
4.strstr和strchr
strstr - 查找子字符串
函数原型:
char *strstr(const char *haystack, const char *needle);
功能特点:
- 在 haystack(干草堆)中查找 needle(针)子字符串
- 返回第一次出现位置的指针
- 如果没找到,返回 NULL
- 查找完整的子字符串
strchr - 查找字符
函数原型:
char *strchr(const char *str, int c);
功能特点:
- 在字符串中查找特定字符 c
- 返回第一次出现位置的指针
- 如果没找到,返回 NULL
- 查找单个字符
5.memchr
memchr
函数原型:
c
void *memchr(const void *str, int c, size_t n);
功能特点:
- 在内存块的前 n 个字节中查找字符 c
- 与 strchr 类似,但可以处理任意内存数据(不限于字符串)
- 不会在遇到 '\0' 时停止,会检查所有 n 个字节
- 返回找到位置的指针,没找到返回 NULL
参数说明:
- str:要搜索的内存块的起始地址
- c:要查找的字符(作为 int 传递,但转换为 unsigned char)
- n:要搜索的字节数
6.memcmp
memcmp
1. 函数原型:
int memcmp(const void *ptr1, const void *ptr2, size_t n);
2. 功能特点:
- 比较两个内存块的前 n 个字节
- 按字节比较(二进制比较)
- 返回比较结果:
- 0:两个内存块完全相等
- < 0:ptr1 小于 ptr2(按字节值)
- > 0:ptr1 大于 ptr2(按字节值)
3. 与字符串比较函数的区别:
|
特性
|
memcmp
|
strcmp
|
strncmp
|
|
比较内容
|
任意内存数据
|
以'\0'结尾的字符串
|
字符串前n字符
|
|
停止条件
|
比较完n个字节
|
遇到'\0'或不同字符
|
遇到'\0'或不同字符或比较完n字符
|
|
二进制安全
|
✅ 是
|
❌ 否
|
❌ 否
|
|
参数
|
两个void指针 + 长度
|
两个char指针
|
两个char指针 + 长度
|
7.memcpy
memcpy
1. 函数原型:
void *memcpy(void *dest, const void *src, size_t n);
2. 功能特点:
- 从源内存地址 src 复制 n 个字节到目标内存地址 dest
- 不检查重叠(如果内存重叠,使用 memmove)
- 不检查终止符(二进制安全)
- 返回目标内存地址 dest
8.memmove
memmove
memmove 是 C 语言标准库中的一个内存操作函数,用于在内存区域之间安全地复制数据,即使源和目标内存区域有重叠也能正确处理。
函数原型
void *memmove(void *dest, const void *src, size_t n);
- dest: 目标内存地址
- src: 源内存地址
- n: 要复制的字节数
9.memset
memset 函数详解
memset 是 C 语言标准库中的一个内存操作函数,用于将一段内存区域填充为指定的值。
函数原型
c
void *memset(void *ptr, int value, size_t num);
- ptr: 指向要填充的内存区域的指针
- value: 要设置的值(以 int 形式传递,但实际使用时会被转换为 unsigned char)
- num: 要设置的字节数
更多推荐



所有评论(0)