C语言字符完全指南:字符函数与字符串函数

C语言字符完全指南:字符函数与字符串函数

字符串处理是C语言编程中的核心任务。标准库提供了丰富的字符分类、转换及字符串操作函数。本文将全面讲解 strlenstrcpystrcatstrcmpstrstrstrtok 等函数的用法与模拟实现,并深入分析常见陷阱,帮助读者安全高效地操作字符串。

目录


一、字符分类函数

头文件 <ctype.h> 提供了一系列字符分类函数,用于判断字符类型。

函数 作用 返回真条件
iscntrl 判断控制字符 任何控制字符
isspace 判断空白字符 空格、换页、换行、回车、制表符等
isdigit 判断十进制数字 0~9
isxdigit 判断十六进制数字 0`9`、`a`fA~F
islower 判断小写字母 a~z
isupper 判断大写字母 A~Z
isalpha 判断字母 a~zA~Z
isalnum 判断字母或数字 a`z`、`A`Z0~9
ispunct 判断标点符号 非数字、非字母的可打印图形字符
isgraph 判断图形字符 任何可打印的非空白字符
isprint 判断可打印字符 包括图形字符和空白字符

示例:将字符串中的小写字母转为大写

c 复制代码
#include <stdio.h>
#include <ctype.h>
int main() {
    char str[] = "Test String.\n";
    for (int i = 0; str[i]; i++) {
        if (islower(str[i]))
            putchar(toupper(str[i]));
        else
            putchar(str[i]);
    }
    return 0;
}

二、字符转换函数

<ctype.h> 还提供了两个字符大小写转换函数:

  • int tolower(int c); // 大写转小写
  • int toupper(int c); // 小写转大写
c 复制代码
c = toupper(c);  // 等价于 c -= 32,但更安全可读

---

三、strlen的使用和模拟实现

size_t strlen(const char *str); 返回字符串中 '\0' 之前的字符个数(不含 '\0')。

注意事项

  • 字符串必须以 '\0' 结尾,否则会越界。
  • 返回值类型为 size_t(无符号整型),容易出错
c 复制代码
const char* str1 = "abc";
const char* str2 = "bbb";
if (strlen(str2) - strlen(str1) > 0)   // 2-3 = -1 但无符号下为很大的正数
    printf("str2>str1\n");              // 错误输出

模拟实现(计数器方式)

c 复制代码
size_t my_strlen(const char* str) {
    assert(str);
    size_t count = 0;
    while (*str++) count++;
    return count;
}

其他方式:递归、指针相减。


四、strcpy的使用和模拟实现

char* strcpy(char* dest, const char* src); 将源字符串(含 '\0')拷贝到目标空间。

条件

  • 源字符串必须以 '\0' 结束。
  • 目标空间必须足够大,且可修改。
  • 目标空间不能与源重叠。

模拟实现

c 复制代码
char* my_strcpy(char* dest, const char* src) {
    char* ret = dest;
    assert(dest && src);
    while ((*dest++ = *src++)) ;
    return ret;
}

注意strcpy 无法指定拷贝长度,容易导致目标缓冲区溢出。


五、strcat的使用和模拟实现

char* strcat(char* dest, const char* src); 将源字符串追加到目标字符串末尾(覆盖目标 '\0',并追加新 '\0')。

条件 :目标空间必须足够大,且目标字符串必须有 '\0' 作为起始追加位置。

注意自己给自己追加会导致死循环 ,因为源字符串的 '\0' 被覆盖后永不结束。

模拟实现

c 复制代码
char* my_strcat(char* dest, const char* src) {
    char* ret = dest;
    assert(dest && src);
    while (*dest) dest++;
    while ((*dest++ = *src++)) ;
    return ret;
}

六、strcmp的使用和模拟实现

int strcmp(const char* str1, const char* str2); 比较两个字符串对应字符的ASCII码值。

  • 返回值 >0str1 大于 str2
  • 返回值 ==0:两字符串相等
  • 返回值 <0str1 小于 str2

模拟实现

c 复制代码
int my_strcmp(const char* s1, const char* s2) {
    assert(s1 && s2);
    while (*s1 == *s2) {
        if (*s1 == '\0') return 0;
        s1++, s2++;
    }
    return *s1 - *s2;
}

七、strncpy函数的使用

char* strncpy(char* dest, const char* src, size_t num); 拷贝 num 个字符从源到目标。

  • 若源字符串长度 < num,则拷贝完源后在目标后补 0 直到 num 个字符。
  • 若源字符串长度 >= num,则不会自动追加 '\0'可能导致目标没有结束标志
c 复制代码
char dest[10];
strncpy(dest, "abc", 3);   // dest 没有 '\0',需手动添加
dest[3] = '\0';

八、strncat函数的使用

char* strncat(char* dest, const char* src, size_t num); 追加源字符串的前 num 个字符到目标末尾,并自动加 '\0'

  • 若源长度 < num,只追加到 '\0' 为止。
  • 无论 num 多大,结果都会以 '\0' 结尾。
c 复制代码
char str1[20] = "To be ";
char str2[] = "or not to be";
strncat(str1, str2, 6);   // str1 = "To be or no"

九、strncmp函数的使用

int strncmp(const char* s1, const char* s2, size_t num); 比较前 num 个字符。

c 复制代码
strncmp("abc", "abd", 2);  // 0(前两个字符相等)
strncmp("abc", "abd", 3);  // -1(第三个字符 'c' < 'd')

十、strstr的使用和模拟实现

char* strstr(const char* str1, const char* str2); 查找 str2str1 中第一次出现的位置,返回指针,未找到返回 NULL

模拟实现

c 复制代码
char* my_strstr(const char* s1, const char* s2) {
    if (!*s2) return (char*)s1;
    const char* cp = s1;
    const char* p1, *p2;
    while (*cp) {
        p1 = cp;
        p2 = s2;
        while (*p1 && *p2 && *p1 == *p2) p1++, p2++;
        if (!*p2) return (char*)cp;
        cp++;
    }
    return NULL;
}

十一、strtok函数的使用

char* strtok(char* str, const char* sep); 按分隔符分割字符串。

  • sep 包含所有可能的分隔符。
  • 第一次调用传入待切分的字符串,后续调用传入 NULL 继续切分。
  • 该函数会修改原字符串 (将分隔符替换为 '\0'),因此通常拷贝临时字符串进行操作。
c 复制代码
char arr[] = "192.168.6.111";
char* sep = ".";
char* token;
for (token = strtok(arr, sep); token != NULL; token = strtok(NULL, sep)) {
    printf("%s\n", token);
}

十二、strerror函数的使用

char* strerror(int errnum); 返回错误码对应的错误信息字符串。

程序启动时有一个全局变量 errno(需包含 <errno.h>),当库函数出错时会设置 errno

c 复制代码
#include <errno.h>
#include <string.h>
#include <stdio.h>

int main() {
    FILE* pf = fopen("unexist.txt", "r");
    if (pf == NULL)
        printf("Error: %s\n", strerror(errno));
    return 0;
}

也可使用 perror("Error message") 直接打印错误信息。


总结:本文系统讲解了 <ctype.h> 中的字符分类与转换函数,以及 <string.h> 中常用字符串函数的用法、注意事项和模拟实现。重点包括:strlen 返回值的无符号陷阱、strcpy/strcat 的缓冲区溢出风险、strtok 修改原字符串的特性以及 strncpy 不自动添加 '\0' 的细节。掌握这些函数,并理解它们的模拟实现,能够显著提升字符串处理的正确性和安全性。

相关推荐
JieE2121 天前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE2121 天前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
vivo互联网技术1 天前
CVPR 2026 | 全新强化学习框架 BeautyGRPO:重塑真实人像
算法·大模型·cvpr·影像
Darling噜啦啦1 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
用户497863050731 天前
(一)小红的数组操作
算法·编程语言
怕浪猫1 天前
Electron 系列文章封面图
算法·架构·前端框架
徐小夕2 天前
JitWord 3.0 正式发布,高精度Word异构解析+复杂组件兼容,打造web端协同Word编辑器
前端·vue.js·算法
LDR0062 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术2 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript