一、字符函数:专注单个字符的分类与转换

字符函数主要处理单个字符的属性判断和大小写转换,使用前需包含头文件 <ctype.h>

1. 字符分类函数:判断字符属性

这类函数的参数是字符(本质是 ASCII 码值,接收int类型),返回非 0 值表示 “符合条件”,返回 0 表示 “不符合”。常用函数及功能如下:

函数名 功能描述
islower 判断字符是否为小写字母(a-z)
isupper 判断字符是否为大写字母(A-Z)
isdigit 判断字符是否为十进制数字(0-9)
isalpha 判断字符是否为字母(a-z 或 A-Z)
isalnum 判断字符是否为字母或数字(a-z、A-Z、0-9)
isspace 判断字符是否为空白字符(空格、换行 \n、制表符 \t 等)
ispunct 判断字符是否为标点符号(非字母、非数字的可打印图形字符)

使用示例:将字符串中的小写字母转为大写,其他字符不变

#include <stdio.h>
#include <ctype.h>
int main() {
    char str[] = "Test String. 123!";
    int i = 0;
    while (str[i]) {
        if (islower(str[i])) { // 判断是否为小写字母
            str[i] = toupper(str[i]); // 转为大写
        }
        putchar(str[i]);
        i++;
    }
    // 输出:TEST STRING. 123!
    return 0;
}

2. 字符转换函数:大小写互转

  • int toupper(int c):将小写字母转为大写,非小写字母返回原字符;
  • int tolower(int c):将大写字母转为小写,非大写字母返回原字符。

核心注意:函数参数接收int类型(实际是字符的 ASCII 码),返回值也是 ASCII 码,需赋值给字符变量使用。

二、字符串函数:处理字符串的核心工具

字符串函数专注于字符串的长度计算、拷贝、追加、比较、查找、分割等操作,使用前需包含头文件 <string.h>

1. 字符串长度计算:strlen

  • 功能:统计字符串中'\0'之前的字符个数(不包含'\0');
  • 原型size_t strlen(const char *str)
  • 参数str指向待统计的字符串(必须以'\0'结束);
  • 返回值size_t(无符号整数),表示字符串长度;
  • 关键注意
    1. 字符串必须以'\0'结尾,否则会越界查找,结果不确定;
    2. 返回值是无符号数,比较时需注意(如strlen("a") - strlen("abc")结果是无符号的大正数,而非 - 2)。

模拟实现思路(3 种常用方式):

  • 计数器法:遍历字符串,遇到'\0'停止,统计次数;
  • 递归法:递归遍历,直到'\0'返回 0,否则返回1 + 递归结果
  • 指针 - 指针法:用两个指针,一个指向字符串首,一个遍历到'\0',返回两指针差值。

2. 字符串拷贝:strcpy vs strncpy

(1)strcpy(不安全版本)
  • 功能:将源字符串(包含'\0')完整拷贝到目标空间;
  • 原型char *strcpy(char *destination, const char *source)
  • 注意事项
    1. 源字符串必须以'\0'结束,否则拷贝会越界;
    2. 目标空间必须足够大,能容纳源字符串(含'\0');
    3. 目标空间必须可修改(不能是字符串常量,如char *dest = "abc");
    4. 返回目标空间起始地址,支持链式调用。
(2)strncpy(安全版本)
  • 功能:最多拷贝num个字符到目标空间,解决 strcpy 的越界问题;
  • 原型char *strncpy(char *destination, const char *source, size_t num)
  • 核心差异
    1. 多了num参数,指定最大拷贝长度;
    2. 源字符串无需以'\0'结束,拷贝num个字符后停止;
    3. 若源字符串长度小于num,剩余部分会填充'\0'

3. 字符串追加:strcat vs strncat

(1)strcat(不安全版本)
  • 功能:将源字符串追加到目标字符串末尾(覆盖目标字符串的'\0',并在末尾加'\0');
  • 原型char *strcat(char *destination, const char *source)
  • 注意事项
    1. 源字符串必须以'\0'结束,目标字符串也需有'\0'(确定追加起始位置);
    2. 目标空间必须足够大,容纳原目标字符串 + 源字符串(含'\0')。
(2)strncat(安全版本)
  • 功能:最多追加num个字符到目标字符串,自动在末尾加'\0'
  • 原型char *strncat(char *destination, const char *source, size_t num)
  • 优势:指定追加长度,避免越界,更灵活安全。

4. 字符串比较:strcmp vs strncmp

(1)strcmp(全量比较)
  • 功能:从字符串第一个字符开始,按 ASCII 码值逐字符比较,直到遇到不同字符或'\0'
  • 原型int strcmp(const char *str1, const char *str2)
  • 返回值
    • 大于 0:str1 大于 str2(第一个不同字符的 ASCII 码差值);
    • 等于 0:str1 与 str2 完全相等;
    • 小于 0:str1 小于 str2。
(2)strncmp(部分比较)
  • 功能:最多比较num个字符,解决 strcmp 全量比较的冗余问题;
  • 原型int strncmp(const char *str1, const char *str2, size_t num)
  • 适用场景:只需比较字符串前 n 个字符(如判断文件后缀名、版本号前缀等)。

5. 字符串查找:strstr

  • 功能:在字符串str1中查找子字符串str2第一次出现的位置;
  • 原型char *strstr(const char *str1, const char *str2)
  • 返回值
    • 找到:返回子字符串首次出现的起始地址;
    • 未找到:返回NULL
  • 特殊情况:若str2为空字符串,直接返回str1

6. 字符串分割:strtok

  • 功能:按指定分隔符分割字符串(会修改原始字符串);
  • 原型char *strtok(char *str, const char *delim)
  • 参数说明
    1. 首次调用:str传入待分割字符串;后续调用:str传入NULL,表示继续分割同一个字符串;
    2. delim是分隔符集合(每个字符都是独立分隔符);
  • 注意事项
    1. 会修改原始字符串,将分隔符替换为'\0',需保留原字符串时先拷贝;
    2. 多个连续分隔符视为单个分隔符,不会返回空字符串;
    3. 分割完成后返回NULL

使用示例:分割 IP 地址192.168.6.111

#include <stdio.h>
#include <string.h>
int main() {
    char arr[] = "192.168.6.111";
    char buf[30];
    strcpy(buf, arr); // 拷贝原字符串,避免修改
    const char *sep = ".";
    char *str = strtok(buf, sep);
    while (str != NULL) {
        printf("%s\n", str);
        str = strtok(NULL, sep); // 后续调用传入NULL
    }
    // 输出:192、168、6、111
    return 0;
}

7. 错误信息获取:strerror

  • 功能:将错误码(errno)转换为对应的错误信息字符串;
  • 原型char *strerror(int errnum)
  • 使用场景:库函数调用失败时(如fopen打开不存在的文件),通过errno获取错误码,再用strerror获取可读错误信息;
  • 辅助函数perror(const char *str),直接打印错误信息(先打印str,再打印冒号 + 空格 + 错误信息)。

使用示例

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main() {
    FILE *pFile = fopen("nonexist.txt", "r"); // 打开不存在的文件
    if (pFile == NULL) {
        printf("strerror: %s\n", strerror(errno)); // 输出错误信息
        perror("perror"); // 直接打印,格式更简洁
        return 1;
    }
    fclose(pFile);
    return 0;
}

三、内存函数:按字节操作的通用工具

内存函数不关注数据类型,按字节对内存块进行拷贝、设置、比较,适用范围更广(可操作 int、float、结构体等),使用前需包含头文件 <string.h>

1. 内存拷贝:memcpy vs memmove

(1)memcpy(不支持重叠)
  • 功能:从源内存块拷贝num个字节到目标内存块;
  • 原型void *memcpy(void *destination, const void *source, size_t num)
  • 核心特点
    1. 不区分数据类型,按字节拷贝;
    2. 源内存块和目标内存块重叠时,拷贝结果未定义(可能出错);
    3. 参数destinationsourcevoid*,需强制类型转换后操作。
(2)memmove(支持重叠)
  • 功能:与 memcpy 一致,但支持源和目标内存块重叠;
  • 原型void *memmove(void *destination, const void *source, size_t num)
  • 实现逻辑
    • 若目标地址 <= 源地址 或 目标地址>= 源地址 + num(无重叠):从低地址到高地址拷贝;
    • 若存在重叠:从高地址到低地址拷贝,避免覆盖源数据。

使用示例(处理内存重叠):

#include <stdio.h>
#include <string.h>
int main() {
    int arr[] = {1,2,3,4,5,6,7,8,9,10};
    memmove(arr+2, arr, 20); // 拷贝前5个元素(20字节)到arr[2]开始的位置
    for (int i=0; i<10; i++) {
        printf("%d ", arr[i]); // 输出:1 2 1 2 3 4 5 8 9 10
    }
    return 0;
}

2. 内存设置:memset

  • 功能:将指定内存块的num个字节设置为特定值;
  • 原型void *memset(void *ptr, int value, size_t num)
  • 关键注意
    1. 按字节设置,value会被转换为unsigned char
    2. 不能直接设置非字符类型的数组(如int arr[5]; memset(arr, 1, 20),结果是每个字节为 1,每个 int 值为0x01010101,而非 1);
  • 适用场景:初始化字符数组、清空内存块等。

使用示例

#include <stdio.h>
#include <string.h>
int main() {
    char str[] = "hello world";
    memset(str, 'x', 6); // 将前6个字节设为'x'
    printf("%s", str); // 输出:xxxxxxworld
    return 0;
}

3. 内存比较:memcmp

  • 功能:比较两块内存块的前num个字节(按unsigned char值比较);
  • 原型int memcmp(const void *ptr1, const void *ptr2, size_t num)
  • 返回值
    • 大于 0:ptr1 的第一个不同字节大于 ptr2;
    • 等于 0:前num个字节完全相等;
    • 小于 0:ptr1 的第一个不同字节小于 ptr2;
  • 优势:可比较任意类型的内存块(如结构体、数组等)。

四、核心函数对比与使用场景总结

函数组 核心差异 适用场景
strcpy/strncpy strcpy 无长度限制(不安全),strncpy 有长度限制 字符串拷贝,优先用 strncpy 保证安全
strcat/strncat strcat 无长度限制(不安全),strncat 有长度限制 字符串追加,优先用 strncat
memcpy/memmove memcpy 不支持重叠,memmove 支持重叠 内存拷贝,有重叠用 memmove,否则均可
strcmp/strncmp strcmp 全量比较,strncmp 部分比较 全量比较用 strcmp,部分比较用 strncmp
字符串函数 / 内存函数 字符串函数依赖'\0',内存函数按字节操作 字符串用字符串函数,其他类型用内存函数

五、常见误区避坑指南

  1. strlen 返回值是无符号数:避免直接用strlen(a) - strlen(b)比较长度,需先转为 int 类型;
  2. 字符串函数依赖'\0':源字符串必须以'\0'结束,否则会越界;
  3. memset 按字节设置:不要用它设置 int、float 等非字符类型的数组;
  4. strtok 修改原始字符串:需保留原字符串时,先拷贝到临时缓冲区再分割;
  5. 目标空间可修改:strcpy、strcat 等函数的目标空间不能是字符串常量(如char *dest = "abc")。
Logo

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

更多推荐