目录

一、内存操作函数(mem 系列)

1. void *memchr(const void *s, int c, size_t n)

2. int memcmp(const void *s1, const void *s2, size_t n)

3. void *memcpy(void *__restrict dest, const void *__restrict src, size_t n)

4. void *memmove(void *dest, const void *src, size_t n)

5. void *memset(void *s, int c, size_t n)

二、字符串操作函数(str 系列)

1. char *strcat(char *__restrict dest, const char *__restrict src)

2. char *strchr(const char *s, int c)

3. int strcmp(const char *s1, const char *s2)

4. int strcoll(const char *s1, const char *s2)

5. char *strcpy(char *__restrict dest, const char *__restrict src)

6. size_t strcspn(const char *s, const char *reject)

7. char *strerror(int errnum)

8. size_t strlen(const char *s)

9. char *strncat(char *__restrict dest, const char *__restrict src, size_t n)

10. int strncmp(const char *s1, const char *s2, size_t n)

11. char *strncpy(char *__restrict dest, const char *__restrict src, size_t n)

12. char *strpbrk(const char *s, const char *accept)

13. char *strrchr(const char *s, int c)

14. size_t strspn(const char *s, const char *accept)

15. char *strstr(const char *haystack, const char *needle)

16. char *strtok(char *__restrict str, const char *__restrict delim)

三、关键区别与使用建议


strings.h 是 C 语言标准库中用于内存操作字符串处理的核心头文件,包含的函数可分为两类:内存操作函数mem 开头,按字节处理任意数据)和字符串操作函数str 开头,处理以 '\0' 结尾的字符串)。以下是对这些高频函数的详细解析:

一、内存操作函数(mem 系列)

内存操作函数不依赖数据类型,直接按字节处理内存块,适用于任何数据(字符、整数、结构体等),需显式指定操作的字节长度。

1. void *memchr(const void *s, int c, size_t n)
  • 功能:在内存块 s 的前 n 个字节中,查找第一个值为 c(取低 8 位,即 c & 0xFF)的字节。
  • 参数
    • s:待查找的内存块起始地址(const 表示不修改原内存)。
    • c:要查找的字符(以 int 传入,实际用低 8 位)。
    • n:最大查找字节数。
  • 返回值:找到时返回该字节的地址;未找到或 n=0 时返回 NULL
  • 示例
    char buf[] = "abc123";
    char *p = memchr(buf, '1', 6); // p 指向 buf[3]('1' 的位置)
    
2. int memcmp(const void *s1, const void *s2, size_t n)
  • 功能:比较内存块 s1 和 s2 的前 n 个字节(按字节的 ASCII 码值比较)。
  • 参数
    • s1s2:两个待比较的内存块。
    • n:比较的字节数。
  • 返回值
    • 0:前 n 字节完全相同;
    • 正数:s1 中第一个不同字节 > s2 中对应字节;
    • 负数:s1 中第一个不同字节 < s2 中对应字节。
  • 注意:与 strcmp 不同,memcmp 不关心 '\0',严格比较 n 字节(即使包含 '\0')。
  • 示例
    char a[] = "abc", b[] = "abd";
    memcmp(a, b, 3); // 返回 -1('c' < 'd')
    
3. void *memcpy(void *__restrict dest, const void *__restrict src, size_t n)
  • 功能:从源内存块 src 复制 n 字节到目标内存块 dest
  • 参数
    • dest:目标内存块(需有足够空间)。
    • src:源内存块(const 表示不修改)。
    • n:复制的字节数。
    • __restrict:C99 关键字,表明 dest 和 src 无内存重叠(编译器可优化)。
  • 返回值:目标内存块 dest 的起始地址。
  • 风险:若 dest 和 src 内存重叠(如同一数组内复制),可能导致数据覆盖错误(需用 memmove 替代)。
  • 示例
    int src[3] = {1,2,3}, dest[3];
    memcpy(dest, src, 3*sizeof(int)); // dest 被复制为 {1,2,3}
    
4. void *memmove(void *dest, const void *src, size_t n)
  • 功能:与 memcpy 相同(复制 n 字节),但支持 dest 和 src 内存重叠
  • 实现逻辑:若内存重叠,会先将 src 数据临时缓存(或从尾部向头部复制),避免覆盖,因此更安全,但效率略低于 memcpy
  • 使用场景:当不确定 dest 和 src 是否重叠时(如同一数组内移动数据),优先用 memmove
  • 示例
    char buf[] = "abcdef";
    memmove(buf+2, buf, 4); // 从 buf[0] 复制 4 字节到 buf[2],结果为 "ababcf"(安全)
    
5. void *memset(void *s, int c, size_t n)
  • 功能:将内存块 s 的前 n 字节全部设置为 c(取低 8 位)。
  • 参数
    • s:目标内存块。
    • c:要设置的字符(如 0 表示清零)。
    • n:设置的字节数。
  • 返回值:内存块 s 的起始地址。
  • 常见用途:初始化内存(如数组清零、缓冲区填充)。
  • 注意:不能直接用于初始化非字符类型(如 floatint),因为按字节设置(如 memset(arr, 1, 4) 对 int 来说是 0x01010101,而非 1)。
  • 示例
    char buf[10];
    memset(buf, 0, 10); // 数组全部清零
    

二、字符串操作函数(str 系列)

字符串操作函数仅处理以 '\0' 结尾的字符串(遇到 '\0' 自动停止),无需显式指定长度,但需确保字符串正确终止(否则可能越界)。

1. char *strcat(char *__restrict dest, const char *__restrict src)
  • 功能:将源字符串 src 追加到目标字符串 dest 的末尾(覆盖 dest 原有的 '\0',并在最后添加新 '\0')。
  • 参数
    • dest:目标字符串(需有足够空间容纳 src,且 dest 和 src 无重叠)。
    • src:源字符串(const 表示不修改)。
  • 返回值:目标字符串 dest 的起始地址。
  • 风险:若 dest 空间不足,会导致缓冲区溢出(推荐用 strncat 替代)。
  • 示例
    char dest[20] = "Hello, ";
    strcat(dest, "World!"); // dest 变为 "Hello, World!"
    
2. char *strchr(const char *s, int c)
  • 功能:在字符串 s 中查找字符 c(取低 8 位)第一次出现的位置。
  • 参数
    • s:待查找的字符串。
    • c:要查找的字符('\0' 也会被查找)。
  • 返回值:找到时返回该字符的地址;未找到返回 NULL
  • 与 memchr 区别strchr 遇到 '\0' 停止,memchr 按指定长度查找。
  • 示例
    char *p = strchr("abcabc", 'b'); // p 指向第一个 'b'(索引 1)
    
3. int strcmp(const char *s1, const char *s2)
  • 功能:比较两个字符串(逐个字符比较 ASCII 码,直到遇到 '\0' 或不同字符)。
  • 返回值
    • 0:两字符串完全相同;
    • 正数:s1 中第一个不同字符 > s2 中对应字符;
    • 负数:s1 中第一个不同字符 < s2 中对应字符。
  • 与 memcmp 区别strcmp 依赖 '\0' 终止,memcmp 需指定长度。
  • 示例
    strcmp("apple", "app"); // 返回正数("apple" 更长,且前 3 字符相同)
    
4. int strcoll(const char *s1, const char *s2)
  • 功能:类似 strcmp,但比较规则依赖当前语言环境(locale)(如多语言排序)。
  • 场景:需要按本地化规则比较字符串时使用(如德语、法语的特殊字符排序),默认 locale 下与 strcmp 效果一致。
5. char *strcpy(char *__restrict dest, const char *__restrict src)
  • 功能:将源字符串 src 复制到目标 dest(包括 '\0')。
  • 风险:若 dest 空间不足(无法容纳 src 及 '\0'),会导致缓冲区溢出(推荐用 strncpy 替代)。
  • 示例
    char dest[10];
    strcpy(dest, "test"); // dest 为 "test\0..."(剩余字节未初始化)
    
6. size_t strcspn(const char *s, const char *reject)
  • 功能:计算字符串 s 中连续不包含 reject 中任何字符的最大长度(从开头算起)。
  • 返回值:满足条件的长度(若 s 开头就有 reject 中的字符,返回 0)。
  • 示例
    strcspn("abc123", "123"); // 返回 3(前 3 个字符 'a','b','c' 不在 reject 中)
    
7. char *strerror(int errnum)
  • 功能:根据错误码 errnum(如 errno)返回对应的错误信息字符串(如 "Permission denied")。
  • 注意:返回的字符串可能是静态缓冲区,线程不安全(线程安全版本为 strerror_r)。
  • 示例
    #include <errno.h>
    printf("%s\n", strerror(ENOENT)); // 输出 "No such file or directory"
    
8. size_t strlen(const char *s)
  • 功能:计算字符串 s 的长度(不包含终止符 '\0')。
  • 返回值:字符个数(遇到 '\0' 停止,若字符串无 '\0',会越界访问)。
  • 示例
    strlen("hello"); // 返回 5('h','e','l','l','o')
    
9. char *strncat(char *__restrict dest, const char *__restrict src, size_t n)
  • 功能:与 strcat 类似,但最多追加 n 个字符(从 src 取前 n 个,或到 src 的 '\0' 为止),最后自动添加 '\0'
  • 安全点:限制追加长度,避免溢出(dest 需至少 strlen(dest) + n + 1 字节)。
  • 示例
    char dest[20] = "Hello, ";
    strncat(dest, "World!!!", 5); // 追加前 5 字符 "World",dest 为 "Hello, World\0"
    
10. int strncmp(const char *s1, const char *s2, size_t n)
  • 功能:类似 strcmp,但最多比较前 n 个字符(或遇到 '\0' 停止)。
  • 安全点:避免长字符串比较的性能问题或越界风险。
  • 示例
    strncmp("apple", "apples", 5); // 返回 0(前 5 字符相同)
    
11. char *strncpy(char *__restrict dest, const char *__restrict src, size_t n)
  • 功能:复制 src 的前 n 个字符到 dest(若 src 长度 < n,剩余部分用 '\0' 填充;若 src 长度 ≥ n,不自动添加 '\0')。
  • 注意
    • 若 src 长度 ≥ ndest 可能没有 '\0',需手动添加(否则不是有效字符串)。
    • 比 strcpy 安全,但仍需确保 dest 长度 ≥ n
  • 示例
    char dest[10];
    strncpy(dest, "test", 10); // dest 为 "test\0\0\0\0\0\0"(剩余 6 字节补 '\0')
    
12. char *strpbrk(const char *s, const char *accept)
  • 功能:在 s 中查找第一个属于 accept 中任意字符的位置。
  • 返回值:找到的字符地址;未找到返回 NULL
  • 示例
    strpbrk("abc123", "xyz1"); // 返回指向 '1' 的指针('1' 在 accept 中)
    
13. char *strrchr(const char *s, int c)
  • 功能:与 strchr 类似,但查找字符 c 在 s 中最后一次出现的位置。
  • 示例
    strrchr("abcabc", 'b'); // 返回指向最后一个 'b'(索引 4)的指针
    
14. size_t strspn(const char *s, const char *accept)
  • 功能:与 strcspn 相反,计算 s 中连续包含 accept 中字符的最大长度(从开头算起)。
  • 示例
    strspn("aabbcc", "abc"); // 返回 6(所有字符都在 accept 中)
    
15. char *strstr(const char *haystack, const char *needle)
  • 功能:在字符串 haystack 中查找子串 needle 第一次出现的位置(needle 为空串时返回 haystack)。
  • 返回值:子串起始地址;未找到返回 NULL
  • 示例
    strstr("hello world", "lo"); // 返回指向 "lo world" 中 'l' 的指针
    
16. char *strtok(char *__restrict str, const char *__restrict delim)
  • 功能:分割字符串 str 为多个子串,分隔符为 delim 中的任意字符。
  • 使用规则
    • 第一次调用:str 传入待分割字符串(会被修改,用 '\0' 替换分隔符)。
    • 后续调用:str 传 NULL,继续分割上一次的字符串。
    • 内部使用静态变量记录分割位置,线程不安全(线程安全版本为 strtok_r)。
  • 返回值:当前分割到的子串地址;无更多子串返回 NULL
  • 示例
    char str[] = "a,b,c";
    char *p = strtok(str, ","); // p = "a"
    p = strtok(NULL, ",");      // p = "b"
    p = strtok(NULL, ",");      // p = "c"
    

三、关键区别与使用建议

  1. 内存函数 vs 字符串函数

    • 内存函数(mem*):处理任意内存块,需指定长度,不依赖 '\0'
    • 字符串函数(str*):仅处理 '\0' 结尾的字符串,自动停止,无需指定长度。
  2. 安全问题

    • 避免使用 strcpystrcat(无长度限制,易溢出),改用 strncpystrncat(需手动处理 '\0')。
    • 内存重叠时用 memmove 而非 memcpy
  3. 线程安全

    • strtok 线程不安全,多线程环境用 strtok_r(POSIX 标准)。

掌握这些函数的细节,能有效处理嵌入式开发中的内存和字符串操作,避免常见的缓冲区溢出、越界访问等问题。

Logo

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

更多推荐