1、 字符函数
一、字符分类函数(ctype.h 头文件)
这些函数用于判断字符的类型,使用前需包含头文件 #include <ctype.h>。
| 函数 | 功能说明 |
|---|---|
iscntrl |
判断是否为控制字符 |
isspace |
判断是否为空白字符(空格、换页\f、换行\n、回车\r、制表符\t、垂直制表符\v) |
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 |
判断是否为可打印字符(含图形字符和空格) |
💡 小细节:这些函数的参数是 int 类型 ,必须传入**unsigned char 类型的值或 EOF**,否则行为未定义。
如果满足功能要求 就会返回一个非0值 如果不满足就会返回0
例如 用isdigit判断是否为十进制数


练习: 将字符串中的小写字母转成大写,其他字符不变
cpp
//练习 将字符串中的小写字母转成大写,其他字符不变
#include<stdio.h>
#include<ctype.h>
int main()
{
int i = 0;
char arr[] = "I am a Student!";
while (arr[i] != '\0')
{
if (islower(arr[i]))//islower判断字符是否为小写字母
{
arr[i] -= 32;//转换成大写
}
printf("%c", arr[i]);
i++;
}
return 0;
}
运行结果为 : I AM A STUDENT!
二、字符转换函数(ctype.h 头文件)
C 语言标准库提供了两个专门用于字符大小写转换的函数:
// 功能:将大写字母转为小写
// 说明:如果传入的不是大写字母,会原样返回
int tolower(int c);
// 功能:将小写字母转为大写
// 说明:如果传入的不是小写字母,会原样返回
int toupper(int c);
核心说明
- 两个函数的参数和返回值 都是
int类型 ,这是为了兼容EOF(通常为-1)的情况。 - 使用前必须包含头文件 #include <ctype.h>。
- 相比直接用
c -= 32这种硬编码方式 ,使用标准库函数可读性更强、兼容性更好(不受字符集影响)。
1. 示例代码解析(小写转大写)
#include <stdio.h>
#include <ctype.h>
int main()
{
int i = 0;
char str[] = "Test String.\n";
char c;
while (str[i]) // 遍历字符串,直到遇到 '\0'
{
c = str[i];
if (islower(c)) // 判断是否为小写字母
c = toupper(c); // 转为大写
putchar(c); // 输出字符
i++;
}
return 0;
}
运行结果
TEST STRING.
优化说明
-
这段代码也可以去掉
islower判断 ,直接写c = toupper(c);,因为**toupper对非小写字母的字符会原样返回。** -
完整等价写法:
while (str[i]) { putchar(toupper(str[i])); i++; }
2、字符串函数
一、strlen 函数基础
1. 函数原型
#include <string.h>
size_t strlen(const char *str);
- 头文件 :
<string.h> - 功能 :统计字符串中
'\0'之前的字符个数(不包含'\0'本身)。 - 参数 :
str是指向要统计的字符串的指针。 - 返回值 :
size_t类型 (无符号整数 ),表示字符串长度。
2. 代码演示
#include <stdio.h>
#include <string.h>
int main()
{
const char* str = "abcdef";
printf("%zd\n", strlen(str)); // 输出:6
return 0;
}
3. 使用注意事项
-
字符串必须以
'\0'结尾,否则strlen会一直往后找**,导致未定义行为** (越界访问)。 -
返回值
size_t是无符号整数 ,这是一个高频易错点 。if (strlen("bbb") - strlen("abcdef") > 0) { printf("bbb 更长\n"); } else { printf("abcdef 更长\n"); }上面的代码会永远输出
bbb 更长,因为无符号数相减的结果 永远是非负数 ,不会得到负数。
二、strlen 的三种模拟实现
方式 1:计数器法(最直观)
int my_strlen(const char *str)
{
int count = 0;
assert(str != NULL); // 防止传入空指针
while (*str != '\0')
{
count++;
str++;
}
return count;
}
方式 2:递归法(不使用临时变量)
int my_strlen(const char *str)
{
assert(str != NULL);
if (*str == '\0')
return 0;
else
return 1 + my_strlen(str + 1);
}
方式 3:指针 - 指针法(指针运算)
int my_strlen(const char *str)
{
assert(str != NULL);
const char *p = str;
while (*p != '\0')
{
p++;
}
return p - str;
}
💡 原理:两个指向同一数组的指针相减 ,结果是它们之间的元素个数。
三、strcpy 基础介绍
1. 函数原型
#include <string.h>
char* strcpy(char* destination, const char* source);
- 头文件 :
<string.h> - 功能 :把
source指向的字符串,完整拷贝 到destination指向的内存中,直到遇到'\0'为止,并且会把'\0'也拷贝过去。 - 返回值 :返回**
destination** 的起始地址(方便链式调用)。
2. 代码演示
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[10] = {0};
char arr2[] = "hello";
strcpy(arr1, arr2);
printf("%s\n", arr1); // 输出:hello
return 0;
}
四、使用注意事项(高频考点)
- 源字符串必须以
'\0'结尾 :如果source没有结束符 ,strcpy会一直拷贝 ,导致越界访问。 - 会拷贝
'\0':目标数组的末尾也会被自动加上 '\0',保证拷贝后是合法字符串。 - 目标空间必须足够大 :
destination的大小 必须能容纳source的所有字符 (包括'\0'),否则会缓冲区溢出。 - 目标空间必须可修改 :不能传入字符串常量 (如
char* p = "abc"; strcpy(p, "def");会崩溃)。
五、strcpy 的模拟实现
#include <stdio.h>
#include <assert.h>
char* my_strcpy(char* dest, const char* src)
{
// 保存目标地址起始位置,因为后面dest会移动
char* ret = dest;
// 空指针检查
assert(dest != NULL);
assert(src != NULL);
// 核心拷贝逻辑
// *dest++ = *src++ 会先赋值,再自增
// 赋值结果就是被拷贝的字符,当拷贝到'\0'时,表达式结果为0,循环结束
while ((*dest++ = *src++))
{
// 循环体为空,所有操作都在条件里完成
}
return ret;
}
int main()
{
char arr1[10] = {0};
char arr2[] = "hello";
my_strcpy(arr1, arr2);
printf("%s\n", arr1); // 输出:hello
return 0;
cpp
//模拟实现strcpy
#include<assert.h>
char* my_strlen(char* dest, char* src)
{
char* ret = dest;
assert(dest && src);
while (*src != '\0')
{
*dest = *src;
dest++;
src++;
}
*dest = *src;//\0也要传入
return ret;
}
int main()
{
char arr1[] = "abcdef";
char arr2[20] = "--------";
char*pr = my_strlen(arr2, arr1);
printf("%s\n", pr);
printf("%s\n", arr2);
}
一. strcat 是什么?
strcat 是 C 标准库 (<string.h> )提供的字符串拼接函数 ,作用是把一个字符串追加到另一个字符串的末尾。
1. 函数原型
char *strcat(char *destination, const char *source);
- 参数说明 :
destination:目标字符串的起始地址 (必须是可修改的内存空间,且有足够大的容量)source:要追加的源字符串 (用const修饰 ,保证内容不会被修改)
- 返回值 :返回
destination的起始地址,方便链式调用。
2. 代码演示解析
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
strcat(arr1, arr2);
printf("%s\n", arr1); // 输出:hello world
return 0;
}
- 执行逻辑:
strcat先找到arr1字符串末尾的 '\0'。- 从
'\0'的位置开始 ,把**arr2的字符逐个复制过去** ,直到遇到arr2的'\0'。 - 最终**
arr1里的内容变成"hello world\0"**。
3. 使用注意事项(重点⚠️)
- 源字符串必须以
'\0'结尾 :否则strcat不知道复制到哪里停止,会一直向后访问内存 ,导致越界访问(野指针问题)。 - 目标字符串也必须包含
'\0':strcat是从目标字符串的**'\0'位置开始追加的** ,如果目标字符串没有'\0',就无法确定追加的起始位置。 - 目标空间必须足够大 :目标数组的容量必须能装下原字符串 + 追加的字符串 + 最后的
'\0',否则会发生缓冲区溢出,导致程序崩溃或内存错误。 - 目标空间必须可修改 :不能给
strcat****传入字符串常量 (比如char *arr = "hello";),因为常量字符串存储在只读内存区,无法修改。
4. 模拟实现 my_strcat 逐行解析
#include <stdio.h>
#include <assert.h>
char* my_strcat(char *dest, const char* src)
{
// 保存目标字符串的起始地址,最后要返回它
char *ret = dest;
// 断言:防止传入空指针,提高代码健壮性
assert(dest != NULL);
assert(src != NULL);
// 1. 让 dest 移动到目标字符串的末尾 '\0' 处
while(*dest)
{
dest++;
}
// 2. 从 '\0' 位置开始,复制 src 的字符到 dest,直到遇到 src 的 '\0'
while((*dest++ = *src++))
{
;
}
// 返回目标字符串的起始地址
return ret;
}
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
my_strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
一、 strcmp 是什么?
strcmp 是 C 标准库 <string.h> 提供的字符串比较函数 ,用来按 ASCII 码值逐字符比较两个字符串的大小。
1. 函数原型
int strcmp(const char *str1, const char *str2);
- 参数说明 :
str1:要比较的第一个字符串str2:要比较的第二个字符串- 两个参数都用
const修饰 ,保证函数内部不会修改原字符串。
- 返回值 (标准规定):
> 0:str1大于str2== 0:str1等于str2< 0:str1小于str2注意:不同编译器的具体返回值可能不同(比如有的返回1/-1,有的返回差值),但符号是固定的。
2. 代码演示解析
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abq";
int ret = strcmp(arr1, arr2);
printf("%d\n", ret);
if(ret > 0)
printf("arr1 > arr2\n");
else if(ret == 0)
printf("arr1 == arr2\n");
else
printf("arr1 < arr2\n");
return 0;
}
- 执行逻辑 :
- 逐字符比较
arr1和arr2:- 第 1 个字符
'a' == 'a',继续比较下一个; - 第 2 个字符
'b' == 'b',继续比较下一个; - 第 3 个字符**
'c'和'q'** :'c'的 ASCII 码值(99)小于'q'(113),比较停止。
- 第 1 个字符
- 函数返回一个小于 0 的值 ,所以最终会输出**
arr1 < arr2。**
- 逐字符比较
3. 模拟实现 my_strcmp 逐行解析
#include <stdio.h>
#include <assert.h>
int my_strcmp(const char *str1, const char *str2)
{
assert(str1 != NULL); // 断言防止空指针
assert(str2 != NULL);
// 当两个字符相等且都不是 '\0' 时,继续比较下一个
while(*str1 == *str2)
{
if(*str1 == '\0') // 两个字符串同时到末尾,说明完全相等
return 0;
str1++;
str2++;
}
// 遇到不相等的字符,返回两者的 ASCII 差值
return *str1 - *str2;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abq";
int ret = my_strcmp(arr1, arr2);
printf("%d\n", ret);
return 0;
}
4. 核心注意事项 & 易错点
-
比较的是 ASCII 码值,不是字符串长度 很多新手会误以为长字符串一定更大,比如
"abcd"和"ab"比较,实际上第 3 个字符'c' > '\0', 所以"abcd"更大 ,和长度无关。 -
必须用
strcmp比较字符串,不能用==// 错误写法 if(arr1 == arr2) { ... }这里的
==比较的是两个数组的地址 ,而不是字符串内容,永远不会相等。 -
字符串必须以
'\0'结尾 如果字符串没有结束符,strcmp会一直向后访问内存 ,导致越界访问,结果不可预测。
一、 strncpy 是什么?
strncpy 是 C 标准库 <string.h> 提供的安全版字符串拷贝函数 ,相比 strcpy ,它多了一个参数可以限制拷贝的字符数,避免缓冲区溢出。
1. 函数原型
char *strncpy(char *destination, const char *source, size_t num);
- 参数说明 :
destination:目标空间的起始地址(必须可修改,且空间足够)source:源字符串的起始地址(被const修饰,不可修改)num:最多从source拷贝的字符个数
- 返回值 :返回**
destination的起始地址**,方便链式调用。
2. 代码演示解析
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = {0}; // 初始化为全 0
char arr2[] = "abcdefghi";
char* str = strncpy(arr1, arr2, 5);
printf("%s\n", arr1); // 输出:abcde
printf("%s\n", str); // 输出:abcde
return 0;
}
- 执行逻辑:
- 从
arr2中拷贝前 5 个字符'a' 'b' 'c' 'd' 'e'到arr1中。 - 注意:
strncpy不会自动在末尾添加'\0'!因为**arr1初始化时是全 0** ,所以第 6 个位置本来就是**'\0'** ,所以能正常打印 。如果arr1没有初始化,这里可能会出现乱码。
- 从
3. strncpy vs strcpy 关键区别
| 特性 | strcpy |
strncpy |
|---|---|---|
| 拷贝终止条件 | 遇到 '\0' 才停止 |
拷贝满 num 个字符就停止,或遇到 '\0' 提前停止 |
| 安全问题 | 目标空间不足时会越界,导致缓冲区溢出 | 受 num 限制,不会超过目标空间大小,更安全 |
自动补 '\0' |
会自动拷贝源字符串的 '\0' |
只有当 num 大于源字符串长度时,才会补 '\0' ;否则不补 |
4. 核心注意事项(⚠️ 新手必看)
-
目标空间必须手动确保以
'\0'结尾 因为strncpy不会自动追加结束符,拷贝完成后 ,最好手动加上'\0':strncpy(arr1, arr2, 5); arr1[5] = '\0'; // 手动补结束符,防止乱码 -
当
num大于源字符串长度时strncpy会把源字符串的**'\0'也拷贝过去** ,并且会用额外的'\0'填充剩余空间,直到填满num个字符。 -
目标空间必须可修改 不能传入字符串常量(如
char *arr = "hello";),否则会导致程序崩溃。
5. 模拟实现 my_strncpy
#include <stdio.h>
#include <assert.h>
char* my_strncpy(char *dest, const char *src, size_t num)
{
assert(dest != NULL);
assert(src != NULL);
char *ret = dest;
// 先拷贝最多 num 个字符
while(num-- && (*src != '\0'))
{
*dest++ = *src++;
}
// 如果 src 提前结束,剩下的用 '\0' 填充
while(num--)
{
*dest++ = '\0';
}
return ret;
}
一、strncat 是什么?
strncat 是 C 标准库 <string.h> 提供的安全版字符串追加函数 ,是 strcat 的升级版本 ,通过限制追加的字符数,避免缓冲区溢出。
1.函数原型
char *strncat(char *destination, const char *source, size_t num);
- 参数说明 :
destination:目标字符串的起始地址(必须可修改,且空间足够)source:要追加的源字符串(被const修饰,函数内不会修改它)num:最多追加的字符个数
- 返回值 :返回**
destination的起始地址**,方便链式调用。
2. 代码演示解
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
char* str = strncat(arr1, arr2, 5);
printf("%s\n", arr1); // 输出:hello world
printf("%s\n", str); // 输出:hello world
return 0;
}
- 执行逻辑:
strncat先找到**arr1末尾的** '\0'。- 从
'\0'位置开始 ,从arr2中追加最多num个字符 (这里是 5 个字符'w' 'o' 'r' 'l' 'd')。 - 关键细节 :追加完成后,
strncat会自动在末尾补上'\0',所以不需要手动处理结束符。
3. strcat vs strncat 对比
| 特性 | strcat |
strncat |
|---|---|---|
| 追加长度 | 无限制,直到源字符串的 '\0' |
受 num 限制,最多追加 num 个字符 |
| 结束符处理 | 会自动追加源字符串的 '\0' |
追加 num 个字符后,一定会自动补上 '\0' |
| 安全风险 | 目标空间不足时会缓冲区溢出 | 受 num 限制,更安全,避免溢出 |
| 源字符串要求 | 必须以 '\0' 结尾 |
即使没有 '\0' ,也只会追加 num 个字符 |
4. 核心注意事项(⚠️ 必看)
- 目标空间必须足够大 虽然
strncat限制了追加的字符数,但目标数组的大小必须能装下原字符串 +num个追加字符 + 最后的'\0',否则依然会溢出。 num的取值技巧 通常num设为目标数组剩余的空间大小减 1 ,这样可以确保追加后还有位置放'\0',避免越界。strncat一定会补'\0'不管追加了多少个字符,strncat都会在追加完成后自动加上'\0',这一点和strncpy不同。
5. 补充拓展:模拟实现 my_strncat
#include <stdio.h>
#include <assert.h>
char* my_strncat(char *dest, const char *src, size_t num)
{
assert(dest != NULL);
assert(src != NULL);
char *ret = dest;
// 1. 先找到 dest 末尾的 '\0'
while(*dest)
{
dest++;
}
// 2. 追加最多 num 个字符
while(num-- && (*src != '\0'))
{
*dest++ = *src++;
}
// 3. 自动补上结束符
*dest = '\0';
return ret;
}
一、 strncmp 是什么?
strncmp 是 C 标准库 <string.h> 提供的安全版字符串比较函数 ,是 strcmp 的升级版本,可以限制比较的字符个数,避免越界访问。
1.函数原型
int strncmp(const char *str1, const char *str2, size_t num);
- 参数说明 :
str1:要比较的第一个字符串str2:要比较的第二个字符串num:最多比较的字符个数
- 返回值 (和
strcmp完全一致):> 0:str1大于str2== 0:str1等于str2(在num个字符内)< 0:str1小于str2
2. 代码演示解析
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcqw";
int ret1 = strncmp(arr1, arr2, 3);
printf("%d\n", ret1); // 输出:0
int ret2 = strncmp(arr1, arr2, 4);
printf("%d\n", ret2); // 输出:负数(因为 'd' < 'q')
return 0;
}
- 执行逻辑:
- 当
num=3时,只比较前 3 个字符'a' 'b' 'c',两者完全相同 ,返回0。 - 当
num=4时,比较第 4 个字符'd'和'q','d'的 ASCII 码值小于 'q',返回负数。
- 当
3. strcmp vs strncmp 对比
| 特性 | strcmp |
strncmp |
|---|---|---|
| 比较长度 | 无限制,直到遇到 '\0' |
受 num 限制,最多比较 num 个字符 |
| 安全风险 | 字符串无结束符时会越界访问 | 受 num 限制,不会越界,更安全 |
| 适用场景 | 比较完整字符串是否相等 | 比较字符串前缀(如版本号、文件头) |
4. 模拟实现 strncmp
#include <stdio.h>
#include <assert.h>
// 模拟实现 strncmp
int my_strncmp(const char* str1, const char* str2, size_t n)
{
// 断言:保证指针不为空
assert(str1 != NULL);
assert(str2 != NULL);
// 最多比较 n 个字符
while (n--)
{
// 字符不相等 → 返回差值
if (*str1 != *str2)
{
return *str1 - *str2;
}
// 字符相等,但已经到末尾 \0 → 两个字符串完全相等
else if (*str1 == '\0')
{
return 0;
}
// 继续比较下一个字符
str1++;
str2++;
}
// 比完 n 个字符都一样 → 相等
return 0;
}
// 测试
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abq";
// 比较前 3 个字符
int ret1 = my_strncmp(arr1, arr2, 3);
printf("%d\n", ret1); // <0,因为 'c' < 'q'
// 比较前 2 个字符
int ret2 = my_strncmp(arr1, arr2, 2);
printf("%d\n", ret2); // 0,ab == ab
return 0;
}
一、strstr 是什么?
strstr 是 C 标准库 <string.h> 提供的字符串查找函数 ,用来在一个主字符串中 ,查找子字符串第一次出现的位置。
1. 函数原型
char *strstr(const char *str1, const char *str2);
- 参数说明 :
str1:被查找的主字符串str2:要查找的子字符串
- 返回值 :
- 找到时:返回子字符串在主字符串中第一次出现 的起始地址
- 找不到时:返回
NULL - 特殊情况:如果**
str2是空字符串("")** ,直接返回str1的起始地址
2. 代码演示解析
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "This is a simple string";
char *pch;
pch = strstr(str, "simple");
if (pch != NULL)
printf("%s\n", pch); // 输出:simple string
else
printf("查找的字符串不存在\n");
return 0;
}
- 执行逻辑:
- 在
str中查找"simple"子串,找到它在第 10 个字符的位置。 strstr返回"simple"首字符的地址 ,所以pch指向"simple string"。- 用
printf打印pch时,会从"simple"开始一直打印到字符串末尾。
- 在
3. 模拟实现 my_strstr 逐行解析(暴力模拟法 效率较低)
#include <stdio.h>
#include <assert.h>
char *my_strstr(const char *str1, const char *str2)
{
// 1. 处理空指针和特殊情况:str2 是空字符串时,直接返回 str1
assert(str1 != NULL);
assert(str2 != NULL);
if (*str2 == '\0')
return (char *)str1;
// 2. 遍历主字符串的每个位置,尝试匹配子串
const char *cp = str1; // cp 记录当前尝试匹配的起始位置
while (*cp != '\0')
{
const char *s1 = cp;
const char *s2 = str2;
// 3. 从当前位置开始,逐个字符匹配子串
while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
{
s1++;
s2++;
}
// 4. 如果 s2 走到了末尾,说明子串匹配成功
if (*s2 == '\0')
{
return (char *)cp; // 返回匹配成功的起始地址
}
// 5. 匹配失败,主字符串的起始位置后移一位
cp++;
}
// 6. 遍历完主字符串都没找到,返回 NULL
return NULL;
}
关键逻辑拆解
- 特殊情况处理 :如果**
str2是空字符串,直接返回str1。** - 双重循环匹配 :
- 外层循环:遍历主字符串的每个位置 ,作为匹配的起点
cp。 - 内层循环:从
cp开始 ,逐个字符和子串str2比较。
- 外层循环:遍历主字符串的每个位置 ,作为匹配的起点
- 匹配成功判断 :如果内层循环结束时**
*s2 == '\0'** ,说明子串的所有字符都匹配上了 ,返回cp。 - 匹配失败处理 :如果当前位置匹配失败 ,主字符串的起点**
cp后移一位,** 继续下一轮匹配。
4. 核心注意事项(⚠️ 必看)
- 子字符串必须以
'\0'结尾 否则strstr会一直向后访问内存,导致越界访问,结果不可预测。 - 返回的是地址,不是索引 比如上面的例子中,
pch不是"simple"在str中的下标,而是指向它的指针 。你可以用pch - str来计算它的下标。 - 只返回第一次出现的位置 如果主字符串中存在多个相同的子串,
strstr只会返回第一个的地址。
一、 strtok 是什么?
strtok 是 C 标准库 <string.h> 提供的字符串分割函数 ,它能根据你指定的分隔符 ,把一个字符串拆分成多个子串。
1. 函数原型
char *strtok(char *str, const char *delim);
- 参数说明 :
str:要分割的字符串。首次调用时传字符串地址,后续调用传NULL,表示继续分割同一个字符串。delim:分隔符集合 (比如". "表示用.和空格作为分隔符)。
- 返回值 :
- 成功时:返回当前分割出来的子串的指针。
- 分割结束时 :返回
NULL。
2. 核心工作原理(必懂)
strtok 有两个关键行为:
- 破坏性修改原字符串 :它会直接把分隔符替换成
'\0',让每个子串都变成独立的字符串。 - (特殊性)内部静态变量记录位置 :它会记住上一次分割结束的位置 ,所以后续调用传
NULL时,它能接着往下分割。
3. 代码演示解析
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "192.168.6.111";
const char* sep = ".";
char* str = NULL;
// 为了不破坏原字符串,先拷贝一份到 buf
char buf[30] = {0};
strcpy(buf, arr);
// 循环分割
for (str = strtok(buf, sep); str != NULL; str = strtok(NULL, sep))
{
printf("%s\n", str);
}
return 0;
}
- 执行过程:
- 第一次调用
strtok(buf, sep):找到第一个.,把它改成'\0',返回"192"的地址。 - 第二次调用
strtok(NULL, sep):从上次结束的位置继续找下一个.,改成'\0',返回"168"的地址。 - 重复这个过程,直到找不到分隔符 ,返回
NULL,循环结束。
- 第一次调用
输出结果:
192
168
6
111
4. 必须注意的坑(⚠️ 必看)
- 会修改原字符串 分隔符会被替换成
'\0',原字符串会被破坏。如果需要保留原数据,一定要先拷贝一份 再分割,像例子里的**buf一样**。 - 不能分割字符串常量 比如
char* arr = "192.168.6.111";这种常量字符串,存储在只读内存区,strtok要修改它会直接导致程序崩溃。 - 连续分隔符会被当成一个 比如
"a..b"用.分割,只会返回"a"和"b",中间的连续分隔符会被跳过,不会返回空字符串。
一、 strerror 是什么?
strerror 是 C 标准库 <string.h> 提供的错误码转字符串函数 ,它能把一个数字错误码 ,转换成人类能看懂的错误信息字符串。
1.函数原型
char *strerror(int errnum);
- 参数说明 :
errnum是错误码,通常直接传errno(一个全局变量,记录标准库函数调用后的错误码)。 - 返回值 :返回错误信息字符串的首地址。
核心背景:errno 变量
errno是一个全局变量 ,定义在**<errno.h>中。**- 当标准库函数(如
fopen、malloc)调用失败时 ,会把对应的错误码写入errno。 - 错误码是一个数字 ,比如
2代表 "文件不存在" ,直接打印很难理解,所以需要strerror把它转成文字。
2. 代码演示解析
示例 1:打印所有错误码对应的信息
#include <errno.h>
#include <string.h>
#include <stdio.h>
int main()
{
int i = 0;
for (i = 0; i <= 10; i++) {
printf("%d: %s\n", i, strerror(i));
}
return 0;
}
-
输出结果(Windows 环境):
0: No error 1: Operation not permitted 2: No such file or directory 3: No such process 4: Interrupted function call 5: Input/output error ...
示例 2:实际场景(文件打开失败)(后续会学习文件指针)
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE *pFile = NULL;
pFile = fopen("unexist.ent", "r");
if (pFile == NULL)
{
printf("错误信息是: %s\n", strerror(errno));
return 1;
}
return 0;
}
- 执行逻辑:
fopen尝试打开一个不存在的文件 ,调用失败,就会返回一个空指针 并且会设置错误码。errno被自动设置为2(对应 "文件不存在" 的错误码)。strerror(errno)把错误码2转换成字符串"No such file or directory"。- 最终输出:
错误信息是: No such file or directory。
一、perror 是什么?
perror 是 <stdio.h> 提供的一站式错误打印函数 ,它会自动读取 errno ,并把错误信息打印到控制台。
1. 函数原型
void perror(const char *str);
- 参数说明 :
str是你自定义的提示信息 ,函数会先打印这个字符串,再打印一个冒号和空格 ,最后打印错误信息。 - 返回值 :无返回值,直接输出到屏幕。
2. 代码演示(和上面 strerror 的例子等价)
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE *pFile = NULL;
pFile = fopen("unexist.ent", "r");
if (pFile == NULL)
{
perror("错误信息是");
return 1;
}
return 0;
}
- 输出结果:
错误信息是: No such file or directory
3. strerror vs perror 对比
| 特性 | strerror |
perror |
|---|---|---|
| 功能 | 只返回错误信息字符串 ,不直接打印 | 直接把提示信息 + 错误信息打印到控制台 |
| 使用方式 | printf("错误: %s", strerror(errno)); |
perror("错误"); |
| 适用场景 | 需要把错误信息存起来、传给其他函数,或者格式化输出 | 快速打印错误信息,调试用 |
| 头文件 | <string.h> + <errno.h> |
<stdio.h> |
4. 核心注意事项(⚠️ 必看)
errno是全局变量,会被后续函数调用覆盖 比如fopen失败后,errno被设为2,但如果你接下来调用了printf出错,errno会被改成新的错误码。所以要立即读取并保存errno的值。strerror返回的字符串是只读的,不能修改 它指向的是库函数内部的静态缓冲区 ,多次调用可能会被覆盖。perror只能输出到标准输出 如果你想把错误信息写入文件,就只能用**strerror。**