字符函数与字符串函数

目录

  • 1.字符分类函数
  • 2.字符转换函数
  • [3. strlen](#3. strlen)
    • [3.1 代码演示](#3.1 代码演示)
    • [3.2 strlen函数的返回值](#3.2 strlen函数的返回值)
    • [3.3 strlen的模拟实现](#3.3 strlen的模拟实现)
  • [4. strcpy](#4. strcpy)
    • [4.1 代码演示](#4.1 代码演示)
    • [4.2 模拟实现](#4.2 模拟实现)
  • [5. strcat](#5. strcat)
    • [5.1 代码演示](#5.1 代码演示)
    • [5.2 模拟实现](#5.2 模拟实现)
  • [6. strcmp](#6. strcmp)
    • [6.1 代码演示](#6.1 代码演示)
    • [6.2 模拟实现](#6.2 模拟实现)
  • 7.strncpy
    • [7.1 代码演示](#7.1 代码演示)
    • [7.2 strcpy 与 strncpy 函数的对比](#7.2 strcpy 与 strncpy 函数的对比)
  • [8. strncat](#8. strncat)
    • [8.1 代码演示](#8.1 代码演示)
    • [8.2 strcat 与 strncat 函数的对比](#8.2 strcat 与 strncat 函数的对比)
  • 9.strncmp
    • [9.1 代码演示](#9.1 代码演示)
    • [9.2 strcmp 与 strncmp 函数的对比](#9.2 strcmp 与 strncmp 函数的对比)
  • [10. strstr](#10. strstr)
    • [10.1 代码演示](#10.1 代码演示)
    • [10.2 strstr的模拟实现](#10.2 strstr的模拟实现)
  • 11.strtok
    • [11.1 代码演示](#11.1 代码演示)
    • [11.2 注意事项](#11.2 注意事项)
  • [12. strerror](#12. strerror)
    • [12.1 代码演示](#12.1 代码演示)
    • [12.2 perror](#12.2 perror)

1.字符分类函数

在 C 语言中,字符函数 是一类专门用来处理单个字符的函数,主要用于判断字符的类型,或者对字符进行大小写转换。

这些函数通常用于判断一个字符是否属于某一类,例如:

函数名 功能说明
isalnum() 判断字符是否为字母或数字 ,a ~ z , A ~ Z,0 ~ 9
isalpha() 判断字符是否为字母 ,字母a~zA ~ Z
isblank() 判断字符是否为空白字符 ,如空格或制表符
iscntrl() 判断字符是否为控制字符
isdigit() 判断字符是否为十进制数字 ,十进制数字为'0' ~ '9' 的字符
isgraph() 判断字符是否具有图形表示 ,即可显示但不包括空格
islower() 判断字符是否为小写字母 :a ~ z
isprint() 判断字符是否为可打印字符,包括空白字符和图形字符
ispunct() 判断字符是否为标点符号
isspace() 判断字符是否为空白字符 ,如空格、换行、制表符等
isupper() 判断字符是否为大写字母A ~ Z
isxdigit() 判断字符是否为十六进制数字 ,包括所有十进制数字字符,小写字母a ~ f , 大写字母 A ~ F

这些函数的使用都要包含头文件:<ctype.h>

我来介绍一个函数isdigit,学习其他函数点击上述<ctype.h>头文件进行学习:

项目 内容
函数名 isdigit
所属头文件 <ctype.h>
函数原型 int isdigit(int c);
函数功能 判断字符是否为十进制数字字符
判断范围 '0''1''2''3''4''5''6''7''8''9'
参数说明 c:需要被检测的字符,传入时会被转换为 int 类型
返回值 如果 c 是十进制数字字符,返回非零值,即真
返回值 如果 c 不是十进制数字字符,返回 0,即假

代码演示:

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <ctype.h>

int main()
{
	int r = isdigit('8'); //是数字字符,返回非零值,表达式为真
	printf("r = %d\n", r);
	if (r)
		printf("是数字字符\n");
	else
		printf("不是数字字符\n");
	int r2 = isdigit('x'); //不是数字字符,返回零,表达式为假
	printf("r2 = %d\n", r2);
	if (r2) 
		printf("是数字字符\n");
	else
		printf("不是数字字符\n");
	return 0;
}


练习:

写⼀个代码,将字符串中的⼩写字⺟转⼤写,其他字符不变。

讲解这道题之前,我们先类了解一下怎么在大小写字母之间相互转化:

代码实现:

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

int main()
{
    // 定义一个字符数组,存放字符串
    char str[] = "I am a student";
    // 定义字符指针 p,指向字符串的首字符
    char* p = str;
    // 遍历字符串,直到遇到字符串结束标志 '\0'
    while (*p != '\0')
    {
        // 判断当前字符是否为小写字母
        if (islower(*p))
        {
            // 如果是小写字母,则减去 32 转换为大写字母
            *p -= 32;
        }
        // 指针向后移动,继续处理下一个字符
        p++;
    }
    // 输出转换后的字符串
    printf("%s\n", str);
    return 0;
}

2.字符转换函数

C语⾔提供了2个字符转换函数:

函数名 所属头文件 函数原型 函数功能 参数说明 返回值
tolower() <ctype.h> int tolower(int c); 将大写字母转换为小写字母 c:需要被转换的字符,会被转换为 int 类型进行处理 如果 c 是大写字母,则返回对应的小写字母;如果不能转换,则原样返回
toupper() <ctype.h> int toupper(int c); 将小写字母转换为大写字母 c:需要被转换的字符,会被转换为 int 类型进行处理 如果 c 是小写字母,则返回对应的大写字母;如果不能转换,则原样返回

上⾯的代码,我们将⼩写转⼤写,是-32完成的效果,有了转换函数,就可以直接使⽤ toupper 函数。

c 复制代码
int main()
{
    char str[] = "I am a student";
    char* p = str;

    while (*p != '\0')
    {
        *p = toupper(*p);  // 将小写字母转换为大写字母,并重新赋值给当前位置
        p++;
    }
    printf("%s\n", str);
    return 0;
}

注意:toupper() 会把小写字母转换成大写字母,但是它的结果需要接收回来。如果只调用了函数,并没有把转换后的字符重新赋值给 *p,字符串内容是不会改变的。


3. strlen

项目 内容
函数名 strlen()
所属头文件 <string.h>
函数原型 size_t strlen(const char *str);
函数功能 统计字符串的长度
参数说明 str:指向要统计长度的字符串
统计规则 统计字符串中 '\0' 之前的字符个数
是否统计 '\0' 不统计字符串结束标志 '\0'
返回值 返回字符串的长度,返回类型为 size_t
注意事项 strlen() 只计算字符串有效字符个数,不包括末尾的 '\0'

3.1 代码演示

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

int main()
{
	char arr[] = "abcdef";
	size_t len = strlen(arr);
	printf("%zu\n", len); //6
	return 0;
}

strlen函数的注意事项:

注意事项 说明
字符串以 '\0' 作为结束标志 strlen() 统计的是 '\0' 前面的字符个数
不包含 '\0' strlen() 返回的长度不包括字符串末尾的结束符 '\0'
参数必须是有效字符串 传入的字符串必须以 '\0' 结尾,否则可能会继续向后查找,导致结果错误
返回值类型是 size_t strlen() 的返回值是无符号整数类型(易错),表示字符串长度
需要包含头文件 使用 strlen() 需要包含头文件 <string.h>

3.2 strlen函数的返回值

下面代码的结果是什么:

c 复制代码
int main()
{
	if (strlen("abc") - strlen("abcdef") > 0)
		printf(">");
	else if (strlen("abc") - strlen("abcdef") == 0)
		printf("==");
	else
		printf("<");
	return 0;
}



结论:比较字符串长度的时候,要直接比较两个strlen()的结果,不要先相减。

正确写法如下:

c 复制代码
int main()
{
	if (strlen("abc") > strlen("abcdef")) //直接比较大小
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}
	return 0;
}

3.3 strlen的模拟实现

方式一(计数器的实现):因为这种方法在C语言深入浅出2中模拟实现了。

方式二(指针-指针):在C语言深入浅出1中模拟实现了。

方式三(递归):
递归的本质思想就是将大事化小 ,递归的相关知识点:C语言函数递归

代码展示:

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

size_t my_strlen(const char* str)
{
    // 断言 str 不是空指针
    assert(str);
    if (*str != '\0')
    {
        // 当前字符计数 1 个,然后递归统计后面的字符
        return 1 + my_strlen(str + 1);
    }
    else
    {
        // 遇到 '\0',说明字符串结束,返回 0
        return 0;
    }
}

int main()
{
    char str[] = "abcdef";
    size_t r = my_strlen(str);
    printf("%zu\n", r);
    return 0;
}

画图演示:


4. strcpy

项目 内容
函数名 strcpy()
所属头文件 <string.h>
函数原型 char *strcpy(char *destination, const char *source);
函数功能 source 指向的字符串复制到 destination 指向的字符数组中
复制内容 会复制整个字符串,包括字符串结束标志 '\0'
参数 destination 目标字符数组,用来存放复制后的字符串
参数 source 源字符串,要被复制的字符串
返回值 返回目标空间 destination 的起始地址

4.1 代码演示

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

int main()
{
	char arr1[20] = "";
	char arr2[] = "world";
	strcpy(arr1, arr2);
	printf("%s\n", arr1); //world
	return 0;
}

strcpy函数的注意事项:

注意事项 说明
源字符串必须以 '\0' 结尾 strcpy() 会一直复制,直到遇到字符串结束标志 '\0'
会复制 '\0' strcpy() 不仅复制普通字符,也会把末尾的 '\0' 一起复制到目标空间
目标空间必须足够大 目标数组要能存下源字符串的所有字符和最后的 '\0'
目标空间必须可修改 目标空间不能是字符串常量,必须是可以被写入的字符数组
防止越界访问 如果目标空间太小,可能会造成数组越界,程序出错

4.2 模拟实现

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

char* my_strcpy(char* dest, const char* src) // src 指向源字符串,const 保证不能通过 src 修改源字符串内容
{
    char* p = dest; // 保存目标空间的起始地址,方便最后返回
    assert(dest && src); //断言dest和src不为空指针
    while (*src != '\0') //while(*dest++ = *src++)
    {                    //{
        *dest = *src;    //     ;
        dest++;          //}
        src++;           
    }
    *dest = *src; // 将字符串结束标志 '\0' 复制到目标空间
    // 返回目标空间的起始地址
    return p;
}
int main()
{
	char arr1[20] = "";
	char arr2[] = "abcdef";
	my_strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

画图推演:


5. strcat

项目 内容
函数名 strcat()
所属头文件 <string.h>
函数原型 char *strcat(char *destination, const char *source);
函数功能 source 指向的字符串追加到 destination 字符串的末尾
拼接规则 destination 原本末尾的 '\0' 会被 source 的第一个字符覆盖
结束标志 拼接完成后,会在新字符串末尾自动添加 '\0'
参数 destination 目标字符串,用来接收拼接后的结果
参数 source 源字符串,需要被追加到目标字符串后面
返回值 返回目标字符串 destination 的起始地址

5.1 代码演示

c 复制代码
int main()
{
	char arr1[20] = "hello ";
	char arr2[] = "world";
	strcat(arr1, arr2);
	printf("%s\n", arr1); //hello world
	return 0;
}

strcat相关函数的注意事项:

注意事项 说明
源字符串必须以 '\0' 结尾 strcat() 会从源字符串 source 中逐个追加字符,直到遇到 '\0' 才停止
目标字符串也必须包含 '\0' strcat() 需要先找到目标字符串 destination 末尾的 '\0',然后从这个位置开始追加
目标空间必须足够大 destination 要能容纳原字符串、追加的源字符串以及最后的 '\0'
目标空间必须可修改 destination 不能是字符串常量,必须是可以被写入的字符数组
会覆盖目标字符串原来的 '\0' 追加时,source 的第一个字符会覆盖 destination 原来的结束标志 '\0'
最后会重新添加 '\0' 拼接完成后,会在新字符串末尾放入新的字符串结束标志

5.2 模拟实现

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

char* my_strcat(char* dest, const char* src)
{
    assert(dest && src);
    char* p = dest;
    // 先找到目标字符串末尾的 '\0'
    // strcat 要从 '\0' 的位置开始追加源字符串
    while (*dest != '\0')
    {
        dest++;
    }
    // 将源字符串 src 中的字符逐个追加到 dest 后面
    while ((*dest++ = *src++) != '\0')
    {
        ;
    }
    return p;
}

int main()
{
    char str1[20] = "hello ";
    char str2[] = "world";
    my_strcat(str1, str2);
    printf("%s\n", str1);
    return 0;
}

画图演示:


6. strcmp

项目 内容
函数名 strcmp()
所属头文件 <string.h>
函数原型 int strcmp(const char *str1, const char *str2);
函数功能 比较两个字符串的大小
比较方式 从两个字符串的第一个字符开始 ,逐个比较字符的 ASCII 码值
比较规则 如果当前字符相等,则继续比较下一个字符
停止条件 遇到两个不相等的字符,或者其中一个字符串结束时停止比较
参数 str1 指向第一个要比较的字符串
参数 str2 指向第二个要比较的字符串
返回值 > 0 第一个字符串大于第二个字符串
返回值 = 0 第一个字符串等于第二个字符串
返回值 < 0 第一个字符串小于第二个字符串
注意事项 strcmp() 比较的是字符的 ASCII 码值,不是字符串长度

6.1 代码演示

c 复制代码
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abq";
int ret = strcmp(arr1, arr2); // <0
printf("%d\n", ret); // -1
if(ret > 0)
printf("arr1 > arr2\n");
else if(ret == 0)
printf("arr1 == arr2\n");
else
printf("arr1 < arr2\n"); //arr1 < arr2
return 0;
}

6.2 模拟实现

c 复制代码
#include <stdio.h>
#include <assert.h>
int my_strcmp(const char* s1, const char* s2)
{
    // 断言 s1 和 s2 都不是空指针
    assert(s1 && s2);

    // 当两个字符串当前字符相等时,继续向后比较
    while (*s1 == *s2)
    {
        // 如果两个字符都为 '\0'
        // 说明两个字符串已经同时结束,内容完全相同
        if (*s1 == '\0')
        {
            return 0;
        }
        // 两个指针同时向后移动,比较下一个字符
        s1++;
        s2++;
    }
    // 遇到不同字符时,返回两个字符 ASCII 码值的差
    // 返回值 > 0:s1 大于 s2
    // 返回值 = 0:s1 等于 s2
    // 返回值 < 0:s1 小于 s2
    return *s1 - *s2;
}

int main()
{
    char str1[] = "abcdef";
    char str2[] = "abc";
    int r = my_strcmp(str1, str2);
    if (r > 0)
        printf("str1 > str2\n");
    else if (r == 0)
        printf("str1 == str2\n");
    else
        printf("str1 < str2\n");
    return 0;
}

画图演示:


7.strncpy

项目 内容
函数名 strncpy()
所属头文件 <string.h>
函数原型 char *strncpy(char *destination, const char *source, size_t num);
函数功能 source 指向的字符串拷贝到 destination 指向的空间中,最多拷贝 num 个字符
参数 destination 目标空间,用来存放拷贝后的内容
参数 source 源字符串,即要被拷贝的数据
参数 num 最多从 source 中拷贝的字符个数
返回值 返回目标空间 destination 的起始地址

7.1 代码演示

c 复制代码
#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "abcdef";
	char* str = strncpy(arr1, arr2, 5);
	printf("%s\n", arr1); //abcde
	return 0;
}

7.2 strcpy 与 strncpy 函数的对比

对比项 strcpy strncpy
函数原型 char* strcpy(char* dest, const char* src); char* strncpy(char* dest, const char* src, size_t n);
拷贝方式 src 开始一直拷贝,直到遇到 \0 最多拷贝 n 个字符
是否拷贝 \0 一定会拷贝源字符串末尾的 \0 不一定会拷贝 \0
结束条件 遇到 \0 停止 拷贝满 n 个字符或提前遇到 \0
目标空间要求 dest 必须足够大 dest 至少能放下 n 个字符
越界风险 如果 dest 空间不足,容易越界 限制了最多拷贝 n 个字符,但使用不当仍有风险
\0 情况 自动补,因为会拷贝源字符串的 \0 如果 src 长度小于 n,后面会补 \0;如果 src 长度大于等于 n,不会自动补 \0
安全性 较低,完全依赖目标空间足够大 相对可控,但不等于绝对安全
返回值 返回 dest 的起始地址 返回 dest 的起始地址

8. strncat

项目 内容
函数原型 char* strncat(char* destination, const char* source, size_t num);
所属头文件 <string.h>
功能 source 指向的字符串追加到 destination 指向的字符串末尾,最多追加 num 个字符
参数 1 destination:目标字符串空间
参数 2 source:源字符串
参数 3 num:最多追加的字符个数
返回值 返回 destination 的起始地址
是否自动补 \0

8.1 代码演示

c 复制代码
#include <stdio.h>
#include <string.h>
int main()
{
	char str1[20] = "hello\0----- ";
	char str2[] = "worldxxxxxx";
	strncat(str1, str2, 5);
	printf("%s\n", str1); //helloworld
	return 0;
}

画图演示:


8.2 strcat 与 strncat 函数的对比

对比项 strcat strncat
函数原型 char* strcat(char* dest, const char* src); char* strncat(char* dest, const char* src, size_t num);
参数数量 2 个参数 3 个参数,多了一个 num
功能 src 字符串全部追加到 dest 末尾 src 中最多 num 个字符追加到 dest 末尾
追加长度 不限制,直到遇到 src\0 最多追加 num 个字符
是否追加 \0 会把 src\0 一起追加过去 追加完成后会自动补一个 \0
源字符串要求 src 必须是以 \0 结尾的字符串 src 在前 num 个字符内可以没有 \0
目标字符串要求 dest 原本必须有 \0 dest 原本也必须有 \0
目标空间要求 必须能容纳 dest 原内容 + src 全部内容 + \0 必须能容纳 dest 原内容 + 最多 num 个字符 + \0
安全性 较低,容易因空间不足越界 相对更安全,但仍需保证目标空间足够
返回值 返回 dest 起始地址 返回 dest 起始地址

9.strncmp

项目 strncmp 函数说明
函数名称 strncmp
所属头文件 <string.h>
函数原型 int strncmp(const char* str1, const char* str2, size_t num);
函数功能 比较 str1str2 指向的两个字符串,最多比较 num 个字符。
比较方式 从两个字符串的第一个字符开始逐个比较 ASCII 值。
停止条件 遇到不同字符、遇到字符串结束符 \0,或已经比较完 num 个字符。
参数 str1 指向第一个参与比较的字符串。
参数 str2 指向第二个参与比较的字符串。
参数 num 最多比较的字符个数,类型为 size_t,是无符号整数类型。
返回值 < 0 第一个不同字符中,str1 的字符 ASCII 值小于 str2
返回值 = 0 两个字符串在前 num 个字符内相同。
返回值 > 0 第一个不同字符中,str1 的字符 ASCII 值大于 str2

9.1 代码演示

c 复制代码
#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); //-1
	return 0;
}

画图演示:


9.2 strcmp 与 strncmp 函数的对比

对比项 strcmp strncmp
函数原型 int strcmp(const char* str1, const char* str2); int strncmp(const char* str1, const char* str2, size_t num);
参数数量 2 个参数 3 个参数,多了 num
比较范围 比较整个字符串 最多比较 num 个字符
结束条件 遇到不同字符或 \0 结束 遇到不同字符、\0 或比较满 num 个字符结束
返回值 < 0 str1 小于 str2 str1 在前 num 个字符内小于 str2
返回值 = 0 两个字符串完全相同 num 个字符相同
返回值 > 0 str1 大于 str2 str1 在前 num 个字符内大于 str2
安全性 不限制比较长度,可能访问更多字符 限制比较长度,相对更安全
使用场景 比较完整字符串 比较字符串前几个字符

10. strstr

项目 strstr 函数说明
函数名称 strstr
所属头文件 <string.h>
函数原型 char* strstr(const char* str1, const char* str2);
函数功能 str1 指向的字符串中查找 str2 指向的子字符串
简单理解 在一个字符串中查找另一个字符串第一次出现的位置
参数 str1 被查找的字符串,也叫主字符串
参数 str2 要查找的字符串,也叫子字符串
返回值 如果找到 str2,返回 str2str1 中第一次出现位置的地址
返回 NULL 如果 str1 中不存在 str2,返回 NULL
查找方式 str1 的第一个字符开始,依次匹配 str2
注意事项 strstr 区分大小写,例如 "abc""ABC" 不相同
特殊情况 如果 str2 是空字符串 "",则返回 str1 的起始地址

10.1 代码演示

c 复制代码
#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[] = "abcdefabcdef";
	char arr2[] = "deq";
	char* ps = strstr(arr1, arr2);
	if (ps != NULL)
	{
		printf("%s\n", ps);
	}
	else
	{
		printf("没有找到\n"); //打印
	}
	return 0;
}

画图演示:


10.2 strstr的模拟实现

c 复制代码
#include <stdio.h>
#include <assert.h>
char* my_strstr(const char* str1, const char* str2)
{
    // 断言:两个指针都不能为空
    assert(str1 && str2);

    if (*str2 == '\0') // 如果要查找的是空字符串,直接返回主字符串起始地址
        return (char*)str1;

    const char* s1 = NULL;    // s1 用来从 str1 的某个位置开始尝试匹配
    const char* s2 = NULL;    // s2 用来遍历待查找字符串 str2
    const char* p = str1;     // p 用来遍历主字符串 str1,依次作为每次匹配的起始位置

    while (*p)// 只要主字符串还没有走到 '\0',就继续查找
    {
        s1 = p;    // 每一轮都从 p 指向的位置开始匹配
        s2 = str2; // 每一轮都从 str2 的起始位置开始匹配
        while (*s1 == *s2) // 当前字符相等时,继续比较下一个字符
        {
            s1++;
            s2++;
            if (*s2 == '\0')      // 如果 s2 走到了 '\0',说明 str2 已经全部匹配成功
                return (char*)p;  // 返回匹配开始的位置
        }
        p++;// 如果本轮匹配失败,就让 p 后移一位,重新尝试匹配
    }
    return NULL;// 遍历完整个 str1 仍未找到 str2,返回 NULL
}

int main()
{
    char str1[] = "abcdefabcdef";
    char str2[] = "bce";
    char* p = my_strstr(str1, str2);
    if (p != NULL)
        printf("%s\n", p);      
    else
        printf("没有找到\n");   
    return 0;
}

注意下面这两种写法的不同的效果。

对比项 原写法:先比较,后移动 简写:边比较,边移动
代码形式 while (*s1 == *s2) { s1++; s2++; } while (*s1++ == *s2++)
是否语法正确 正确 语法也正确
是否等价 与当前代码逻辑匹配 不等价,不能直接替换
指针移动时机 只有当前字符相等时,才进入循环并移动 每次比较后,s1s2 都会自动后移
比较失败时 指针停在第一个不相等的位置 即使比较失败,指针也已经后移
匹配成功时 s2 正好停在 '\0' 位置 s2 可能已经越过 '\0'
对后续判断的影响 后面 if (*s2 == '\0') 可以正确判断是否匹配成功 可能破坏 if (*s2 == '\0') 的判断

画图演示:


11.strtok

项目 内容
函数名 strtok
头文件 #include <string.h>
函数原型 char *strtok(char *str, const char *delim);
主要功能 根据 delim 指定的分隔符,将字符串 str 分割成多个子字符串。
工作原理 strtok 会在原字符串中找到分隔符,并将分隔符替换 为字符串结束符 '\0',从而把原字符串切分成多个部分。
是否修改原字符串 会修改原字符串,因此传入的字符串不能是字符串常量
参数 str 首次调用时传入待分割的字符串;后续调用传入 NULL,表示继续分割同一个字符串。
参数 delim 指定分隔符字符串,其中每个字符都可以作为独立的分隔符。
返回值 成功时返回当前分割出的子字符串的首地址
结束条件 当没有更多子字符串可分割时,返回 NULL
首次调用 strtok(str, delim),用于开始分割字符串。
后续调用 strtok(NULL, delim),继续从上一次结束的位置向后分割。
概念 说明
分隔符 用来分割字符串的字符
常见分隔符 空格、逗号、分号、冒号、换行符等
strtok 中的作用 告诉函数遇到哪些字符时进行切割
注意点 delim 中的每个字符都会被单独看作分隔符

11.1 代码演示

c 复制代码
#include <stdio.h>
#include <string.h>
int main()
{
    char str[] = "byte-wizard@byte";// 原始字符串,里面包含 '-' 和 '@' 作为分隔符
    // 定义分隔符字符串
    const char* sep = "@-";// 注意:这里的 '@' 和 '-' 都会被当作分隔符
    char buf[30] = { 0 };
    strncpy(buf, str, 30);
    /*
        for 循环结构说明:
        1. char* p = strtok(buf, sep)
           第一次调用 strtok,传入要分割的字符串 buf
        2. p != NULL
           判断是否还分割出了有效的子字符串
        3. p = strtok(NULL, sep)
           后续调用 strtok,传入 NULL,表示继续分割上一次的字符串
    */
    for (char* p = strtok(buf, sep); p != NULL; p = strtok(NULL, sep))
    {
        // 打印每次分割得到的子字符串
        printf("%s\n", p);
    }
    return 0;
}

画图演示:


11.2 注意事项

注意事项 说明 示例/结果
会修改原字符串 strtok 会把分隔符位置替换成 '\0',因此原字符串会被改变 "a-b-c" 会被改成 "a\0b\0c"
不能直接处理字符串常量 字符串常量通常存放在只读区,strtok 会修改字符串,可能导致程序崩溃 错误:strtok("a-b-c", "-");
建议使用字符数组 如果要使用 strtok,应把字符串放到可修改的字符数组中 正确:char str[] = "a-b-c";
连续分隔符会被当作一个分隔符处理 多个连续分隔符不会产生空字符串 "a--b" 使用 "-" 分割,结果是 "a""b"
后续调用第一个参数传 NULL 第一次传入字符串地址,之后继续分割时传入 NULL strtok(NULL, "-");
返回 NULL 表示分割结束 当没有更多子字符串时,strtok 返回 NULL 可作为循环结束条件
str 不能随便传 NULL 第一次调用时如果 strNULL,且之前没有保存分割状态,行为未定义 首次调用必须传入有效字符串
分隔符字符串中每个字符都单独生效 delim 中的每个字符都可以作为分隔符 ",;" 表示逗号和分号都是分隔符

12. strerror

项目 内容
函数名 strerror
头文件 #include <string.h>
函数原型 char *strerror(int errnum);
参数 errnum:错误码,一般传入 errno 的值
返回值 返回错误码对应的错误信息字符串的地址
主要功能 根据错误码 errnum,获取对应的错误描述信息
常见用途 当标准库函数调用失败时,通过 strerror(errno) 查看具体错误原因
是否修改原数据 不修改传入的错误码
返回内容 一个描述错误原因的字符串,例如 "No such file or directory"

12.1 代码演示

c 复制代码
#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;
}

输出结果如下:

c 复制代码
#include <stdio.h>    
#include <string.h>   // 使用 strerror 函数
#include <errno.h>    // 使用 errno 变量,保存错误码

int main()
{
    /*
        fopen 用于打开文件。
        参数说明:
        1. "data.txt":要打开的文件名
        2. "r":以只读方式打开文件
        如果文件打开成功,fopen 返回文件指针;
        如果文件打开失败,fopen 返回 NULL,并将错误码保存到 errno 中。
    */
    FILE* pf = fopen("data.txt", "r");
    // 判断文件是否打开失败
    if (pf == NULL)
    {
        printf("%s\n", strerror(errno));
        // 文件打开失败,程序异常结束,返回 1
        return 1;
    }
    // 文件打开成功后,可以在这里进行读文件操作
    //...
    /*
        文件使用完之后,需要关闭文件。
        fclose 用于关闭文件,释放相关资源。
    */
    fclose(pf);
    /*
        文件关闭后,将文件指针置为 NULL,
        避免 pf 成为野指针。
    */
    pf = NULL;
    return 0;
}

12.2 perror

项目 内容
函数名 perror
头文件 #include <stdio.h>
函数原型 void perror(const char *s);
参数 s:用户自定义的提示信息字符串
返回值 无返回值,返回类型是 void
主要功能 根据当前 errno 中保存的错误码,打印对应的错误信息
输出位置 默认输出到标准错误流 stderr

perror 会按照下面的格式输出:

c 复制代码
自定义提示信息: 系统错误信息

例如:

c 复制代码
perror("fopen");

如果文件不存在,可能输出:

c 复制代码
fopen: No such file or directory

其中:

部分 含义
fopen 传给 perror 的参数字符串
: perror 自动添加的冒号
空格 perror 自动添加
No such file or directory 根据 errno 得到的错误信息

代码演示:

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

int main()
{
    FILE* pf = fopen("data.txt", "r");

    if (pf == NULL)
    {
        // perror 会自动根据 errno 打印错误原因
        perror("fopen");
        return 1;
    }
    fclose(pf);
    pf = NULL;
    return 0;
}

如果 data.txt 文件不存在,输出结果可能是:

c 复制代码
fopen: No such file or directory

相关推荐
shylyly_2 小时前
文件操作函数
c语言·文件·文件操作·文件函数·顺序读写函数·随机读写函数·状态检测函数
AI科技星2 小时前
基于全域数学0-1-∞体系的1.237宇宙临界常数及时空超导统一理论
c语言·开发语言·线性代数·量子计算·agi
我星期八休息12 小时前
Linux系统编程—基础IO
linux·运维·服务器·c语言·c++·人工智能·算法
kkeeper~14 小时前
0基础C语言积跬步之深入理解指针(4)
c语言·开发语言
学会870上岸华师15 小时前
C 语言程序设计——第一章课后编程题
c语言·开发语言·学习·算法
wangjialelele17 小时前
【SystemV】基于建造者模式的信号量
linux·c语言·c++·算法·建造者模式
朔北之忘 Clancy18 小时前
2026 年 3 月青少年软编等考 C 语言一级真题解析
c语言·开发语言·c++·学习·青少年编程·题解·一级
不剪发的Tony老师19 小时前
Code::Blocks:一款免费开源的C/C++/Fortran集成开发环境
c语言·c++·ide
三品吉他手会点灯21 小时前
C语言学习笔记 - 32.嵌入式C语言学习阶段对初学编程者的建议
c语言·开发语言·笔记·学习