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' 的细节。掌握这些函数,并理解它们的模拟实现,能够显著提升字符串处理的正确性和安全性。

相关推荐
狼与自由11 小时前
jdk版本升级
java·开发语言
云姜.11 小时前
Langchain快速上手编程-Runnable 与 LCEL
java·开发语言·langchain
墨白曦煜11 小时前
算法实战笔记:链表的底层逻辑与指针的高阶玩法(二)
笔记·算法·链表
折哥的程序人生 · 物流技术专研11 小时前
《Java 100 天进阶之路》第40篇:浮点数转成十进制问题
java·开发语言·后端·面试·求职招聘
zhendianluli11 小时前
在 RTX 4090 + Python 3.10 上成功安装 Mamba‑3 的避坑指南
开发语言·python
人还是要有梦想的11 小时前
Qt WebEngine需要MSVC进行编译运行,QT如何用MSVC编译(包含64位和32位)
开发语言·qt·msvc
wuweijianlove11 小时前
算法复杂度评估的实验统计方法与可视化的技术7
算法
名不经传的养虾人11 小时前
从0到1:企业级AI项目迭代日记 Vol.35|追问比演示重要——技术团队问出的五个工程缺口
人工智能·算法·机器学习·ai编程·ai工作流·企业ai
xxl大卡11 小时前
Redis 主从复制与哨兵模式
java·开发语言