C语言字符完全指南:字符函数与字符串函数
字符串处理是C语言编程中的核心任务。标准库提供了丰富的字符分类、转换及字符串操作函数。本文将全面讲解
strlen、strcpy、strcat、strcmp、strstr、strtok等函数的用法与模拟实现,并深入分析常见陷阱,帮助读者安全高效地操作字符串。
目录
- 一、字符分类函数
- 二、字符转换函数
- 三、strlen的使用和模拟实现
- 四、strcpy的使用和模拟实现
- 五、strcat的使用和模拟实现
- 六、strcmp的使用和模拟实现
- 七、strncpy函数的使用
- 八、strncat函数的使用
- 九、strncmp函数的使用
- 十、strstr的使用和模拟实现
- 十一、strtok函数的使用
- 十二、strerror函数的使用
一、字符分类函数
头文件 <ctype.h> 提供了一系列字符分类函数,用于判断字符类型。
| 函数 | 作用 | 返回真条件 |
|---|---|---|
iscntrl |
判断控制字符 | 任何控制字符 |
isspace |
判断空白字符 | 空格、换页、换行、回车、制表符等 |
isdigit |
判断十进制数字 | 0~9 |
isxdigit |
判断十六进制数字 | 0`9`、`a`f、A~F |
islower |
判断小写字母 | a~z |
isupper |
判断大写字母 | A~Z |
isalpha |
判断字母 | a~z 或 A~Z |
isalnum |
判断字母或数字 | a`z`、`A`Z、0~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码值。
- 返回值
>0:str1大于str2 - 返回值
==0:两字符串相等 - 返回值
<0:str1小于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); 查找 str2 在 str1 中第一次出现的位置,返回指针,未找到返回 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'的细节。掌握这些函数,并理解它们的模拟实现,能够显著提升字符串处理的正确性和安全性。