在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)
    • 作用
    1. 编译时检查:如果编译器能够确定在调用时传入了NULL(例如 strcpy(NULL, "hello")),它可能会产生一个警告。
    2. 优化提示:编译器可以假设这些指针是有效的,从而进行一些优化。
  • 如果调用者违反了此规则,传入了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: 要设置的字节数
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐