C语言中操作字符串的函数

       本章涉及的函数包括但不限于strlen、strcpy、strcat、strcmp、strncpy、strncat、strncmp、strstr、strtok、strerror函数的知识,都是与str(string字节串)相关的函数,操作对象也自然是字符串,使用这些函数前都需要包含头文件 <string.h> 。还有在编写函数时涉及指针的,都可以用 assert 断言判断一下是否为空指针。
       文章篇幅较长,同学们可以点点收藏,以免看到一半退出之后找不到了。

一、回顾字符串的一些知识

该知识点在我的文章《C语言中sizeof、strlen介绍》也有提及。

       字符串,在C语言中定义时只能用字符数组的形式定义,不像Java中有String这样的数据类型。因此在C语言中存储字符串的语法为 char str[] = "abcdef";

       值得注意的是该字符串后面隐藏了一个 \0 作为字符串结束标志。因此使用 sizeof 操作符计算字符数组的空间大小时,将会包含 \0 ;而使用strlen 函数计算字符串长度时,统计的是 \0 之前的字符个数(不包括 \0 )。

总而言之,就是strlen(str) == sizeof(str) - 1; 

       此外,字符串的创建还有另一种形式 char str[] = { 'a', 'b', 'c' }; 当字符串是这样子定义时,是没有字符串结束标志 \0 的,因此想要输出字符数组中的内容,输出a,b,c之后还有打印出空间上其他的字符,直到遇到 \0 为止。(打印单个字符使用占位符%c,打印字符串使用%s)

此处补充一个知识点:'\0'的ASCII码值为0,找 \0 也可以是在找十六进制 0。

 二、细说 strlen 函数

 2.1 strlen 语法形式

size_t strlen ( const char * str );

功能:统计参数 str 所指向的字符串中 \0 之前的字符个数,即字符串长度;

参数:str 是指针类型变量,指向了要统计长度的字符串;

返回值:长度不可能是负数,因此返回值类型是无符号整型 size_t。

2.2 注意事项

① strlen 函数的返回值是 size_t 无符号整型!

② strlen 的使用需要包含头文件 <string.h>(只要是与字符串有关的函数,使用前都要包含该头文件);

③ 参数所指的字符串必须以 '\0' 结束,才能正确计算出字符串的长度,否则得到的是随机值;

④ strlen 函数返回的是在字符串 '\0' 之前的字符个数,是不包含 \0 的。

2.3 返回值

试着分析以下代码会输出什么:

#include <string.h>
int main()
{
    if (strlen("abc") - strlen("abcdef") > 0)
    {
        printf(">\n");
    }
    else
    {
        printf("<=\n");
    }
    return 0;
}

       计算 strlen("abc") 得到3,而 strlen("abcdef") 得到6,按理来说 strlen("abc") - strlen("abcdef") 得到-3是 <0 的,那么将会输出 "<="。但实际上,该程序输出 ">"。

       那是因为-3的原码10000000 00000000 00000000 00000011,反码是符号位不变,数值位取反变成                          11111111   11111111   11111111  11111100,+1之后变成补码存储在计算机中,即                          11111111   11111111   11111111  11111101

       而-3被当成无符号整型类型 size_t 返回时,无符号整型的原码就是补码,因此11111111   11111111   11111111  11111101被当成原码返回,转换为十进制是非常大的数,肯定 >0 ,因此程序输出 ">"。


如果想要实现比较字符串长度的功能,可以采用以下两种方式:

① 直接比较字符串长度大小

#include <string.h>
int main()
{
    if (strlen("abc") > strlen("abcdef"))
    {
        printf(">\n");
    }
    else
    {
        printf("<=\n");
    }
    return 0;
}

 ② 对返回值进行强制类型转化

#include <string.h>
int main()
{
    if ((int)strlen("abc") - (int)strlen("abcdef") > 0)
    {
        printf(">\n");
    }
    else
    {
        printf("<=\n");
    }
    return 0;
}

补充说明:在涉及到 size_t 无符号整型时,要注意整型提升。整型提升的知识点将在下一篇文章中讲到。

 2.4 模拟实现 strlen 函数

此处涉及到指针、递归、assert 断言的知识,看不懂的同学需要温习一下。

包括但不限于以下三种实现方式:计算器;指针-指针;递归函数

① 计算器 count

#include <stdio.h>
#include <assert.h>

size_t myStrlen(const char* str) // 加const修饰意思是源字符串不能被修改
{
    assert(str);
    // assert断言,判断str是否为空指针,如果为空则程序报错并停止运行,使用时要包含头文件 <assert.h>

    size_t count = 0; // 临时变量
    while(*str)
    {
        count++;
        str++;
    }
    return count;
}
int main()
{
    char str[] = "abcdef";
    size_t r = myStrlen(str); // 数值名相当于首元素的地址,因此函数形参用指针形式接收
    printf("%d\n", r);
    return 0;
}

 ② 指针 - 指针

#include <stdio.h>
#include <assert.h>

size_t myStrlen(const char* str)
{
    assert(str);
    char* start = str; // 临时变量
    while(*str)
    {
        str++;
    }
    return (size_t)(str - start);
}
int main()
{
    char str[] = "abcdef";
    int r = myStrlen(str);
    printf("%d\n", r);
    return 0;
}

 ③ 迭代,不创建临时变量的方法

#include <stdio.h>
#include <assert.h>

size_t myStrlen(const char* str)
{
    assert(str);
    if (*str != '\0')
        return 1 + myStrlen(str + 1);
    else
        return NULL;
}
int main()
{
    char str[] = "abcedf";
    int r = myStrlen(str);
    printf("%d\n", r);
    return 0;
}

三、strcpy 函数

3.1 strcpy 语法形式

char* strcpy(char * destination, const char * source );

功能:字符串拷贝,拷贝到源头字符中的 \0 为止( \0 也会被拷贝到目标字符串中);

参数:destination - char*类型的指针,指向目的地空间;source 则指向源字符串;

返回值:返回目标空间的起始地址。

3.2 使用示例

 想要将 arr1 的数据全部复制一份到 arr2,错误示范:

#include <stdio.h>
#include <string.h>

int main()
{
    char arr1[] = "Hello, I'm Jack.";
    char arr2[20] = { 0 }; // 必须声明数组容量,否则系统默认只有1个空间,是放不下arr1的,就会报错
    arr2 = arr1;
    return 0;
}

arr2 = arr1; 的操作相当于 4 = 9的数学算式,是错误的。因为数组名是地址,地址相当于门牌号,是固定的(即常量值),不能被修改。地址也没有存储任何数据,只是起到指向空间的作用。


正确用法:

#include <stdio.h>
#include <string.h>

int main()
{
    char arr1[] = "Hello, I'm Jack.";
    char arr2[20] = { 0 };
    char* ret = strcpy(arr2, arr1); // 后面的字符串内容复制到前一个字符串
    printf("%s\n", arr2);
    printf("%s\n", ret); // 从ret这个地址开始向后打印,与上一条语句结果一致

    return 0;
}

3.2 注意事项

① 源字符串必须以 \0 结束

② strcpy是将源字符串\0之前的元素拷贝到目标空间,包括\0也会被拷贝进去,\0之后的元素将不会被拷贝进去

③ 目标空间必须足够大,以确保能存放源字符串 

 报错信息:arr2已经被占满

④ 目标空间必须可以修改,即不能是常量字符串

什么是常量字符串? 用指针存放的字符串就是常量字符串,是不允许被修改的。语法形式为 const char* p = "abcdef";

3.3 模拟实现 strcpy 函数

 基本思路:只要源数据没有遇到 \0 就一个字节一个字节地赋值

#include <stdio.h>
#include <assert.h>

void myStrcpy(char* dest, const char* src) // 源数据是不能修改的,因此用cosnt修饰
{
    assert(dest && src);
    while(*src != '\0')
    {
        *dest = *src;
        dest++;
        src++;
    }
}
int main()
{
    char arr1[] = "hello world";
    char arr2[] = "xxxxxxxxxxxxxxx";
    myStrcpy(arr2, arr1);
    return 0;
}

优化代码:

while 语句块中的 *dest = *src; dest++; src++; 可以合并为一条语句:*dest++ = * src++;

因为 dest++ 和 src++ 后置 ++ 是先使用再自增。所以是 dest 先被 src 赋值之后解引用,最后才+1。但直接将该语句放在 while 循环体中无法实现将 \0 也拷贝到目标空间的功能,但我们要是将该语句放到 while 语句的表达式里面呢?

当 *src = \0 的时候,已经将 \0 赋值给了 *dest ,之后整条语句才是 0 ,while循环才会终止。意思是 \0 被赋值到目标空间之后循环就跳出了。

#include <stdio.h>
#include <string.h>
#include <assert.h>

char* myStrcpy(char* dest, const char* src)
{
    assert(dest && src);
    char* ret = dest; // 刚开始接收的就是目标空间的起始地址,在进入循环之后,地址被++修改了,因此在进入循环前应该对其进行保存。
    while(*dest++ = *src++)
    {
        ; // 写成这样是为了代码的可读性
    }
    return ret; // 返回目标空间的起始地址
}
int main()
{
    char arr1[] = "hello world";
    char arr2[] = "xxxxxxxxxxxxxxx";
    char* r = myStrcpy(arr2, arr1);
    printf("%s\n", arr2);
    printf("%s\n", r);
    return 0;
}

 补充知识点:既然可以直接打印 arr2 ,那为什么函数返回值还要是 char* 呢?

是为了方便程序员灵活应用,可以通过接收函数返回地址的形式打印数组,也可以通过链式访问进行打印:printf("%s\n", my_strcpy(arr2, arr1));

四、strcat 函数

4.1 strcat 语法形式

 char * strcat ( char * destination, const char * source );

功能:字符串追加,把 source 指向的源字符串中的所有字符都追加到 destination 指向的空间中。(将 destination 目标字符串的 \0 覆盖掉了);

参数:destination 指针,指向目的地空间;source 指针,指向源头数据;

返回值:strcat 函数返回的目标空间的起始地址

4.2 使用示例

#include <stdio.h>
#include <string.h>
int main()
{
     char arr1[20] = "hello ";
     char arr2[] = "world";
     strcat(arr1, arr2);
     printf("%s\n", arr1);
     return 0;
}

 4.3 注意事项

① 源字符串必须以\0结束。

② 目标字符串中也得有\0,否则没办法知道追加从哪里开始。

③ 目标空间必须有足够的大,能容纳下源字符串的内容。

④ 目标空间必须可修改。

4.4 模拟实现 strcat 函数

其实就是在 strcpy 函数上增加一个条件就是 strcat 函数了,因为 strcat 函数相当于目标字符串 \0 以及 \0 之后的元素被源字符串复制的数据覆盖掉了。因此该条件就是目标字符串的首地址不断+1直到遇到 \0 之后元素才开始被 *src 覆盖。

#include <stdio.h>
#include <assert.h>

char* myStrcat(char* dest, const char* src)
{
    assert(dest && src);
    char* ret = dest;
    // 1、找到目标空间的\0
    while (*dest != '\0')
        dest++;
    // 2、拷贝
    while (*dest++ = *src++)
    {
        ;
    }
    return ret;
}
int main()
{
    char arr1[] = "hello\0Jack";
    char arr2[] = "Rose";
    myStrcat(arr1, arr2);
    printf("%s\n", arr1);
    return 0;
}

五、strcmp 函数

5.1 语法形式

int strcmp ( const char * str1, const char * str2 );

功能:用来比较str1str2指向的字符串,从两个字符串的第一个字符开始比较,如果两个字符的ASCII码值相等,就比较下一个字符。直到遇到不相等的两个字符,或者字符串结束。是一对一对进行比较的。

参数:str1指针,指向要比较的第一个字符串;str2指针,指向要比较的第二个字符串。

返回值:

标准规定:

① 第一个字符串大于第二个字符串,则返回大于0的数字;

② 第一个字符串等于第二个字符串,则返回0;

③ 第一个字符串小于第二个字符串,则返回小于0的数字。

 5.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 的 a 与 arr2 的 a 进行比较,然后是 arr1 的 b 与 arr2 的 b 比较,它们的ASCII码值都是相等的,到 c 和 q 比较时,q 的ASCII码值比 c 大,因此上述程序中 ret 得到的值是负数(VS下是-1),并输出 arr1 < arr2 的结果。

5.3 模拟实现 strcmp 函数


#include <stdio.h>
#include <assert.h>

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0; // 相等且只要有一个遇到了\0,说明两个都是\0,则两个字符串相等
		str1++;
		str2++;
	}
	// 两字符不相等就跳出来
	if (*str1 > *str2)
		return 1;
	else
		return -1;
}
int main()
{
	char str1[] = "abc";
	char str2[] = "abcdef";
	int r = my_strcmp(str1, str2);
	if (r > 0)
		printf("str1 > str2");
	else if (r < 0)
		printf("str1 < str2");
	else
		printf("str1 == str2");

	return 0;
}

以上 strcpy、strcat、strcmp 函数操作对象都是不受长度限制的,而以下 strncpy、strncat、strncmp (比上面三个函数命名多了一个n)则是可以通过传输num的值来指定控制操作字符个数。后面三个函数都要比前面三个函数安全、灵活。

六、 strncpy 函数

6.1 语法形式

char * strncpy ( char * destination, const char * source, size_t num );

比strcpy函数多了一个参数num,是可以指定拷贝多少个字符的。

strncpy 函数指定了拷贝的长度,源字符串不⼀定要有 \0,同时在设计参数的时候,就会多一层思考:目标空间的大小是否够用,因此 strncpy 相对 strcpy 函数更加安全。

6.2 使用示例及注意事项 

当源字符串长度不够指定的长度时,函数将自动在后面补 \0 :

6.3 模拟实现 strncpy 函数

写法1,使用指针:

#include <stdio.h>
#include <assert.h>

char* myStrncpy(char* dest, const char* src, size_t num)
{
	char* ret = dest;
	assert(dest && src);

	while (num--)
	{
		if ((*dest = *src) != '\0') // 源字符串遇到\0就停止复制
		{
			src++;
		}
		dest++; // src遇到\0之后跳出,dest还继续++的原因是实现上面提及到的“源字符串长度不够指定的长度时,函数将自动在后面补 \0 ”的功能
	}
	*dest = '\0';  // 确保目标字符串以 \0 结尾

	return ret;
}
int main()
{
	char str1[] = "Hello, I'm Jack."; // 此处有16个字符
	char str2[30] = "xxxxxxxxxxxxxxxxxxxxxxxx";
	myStrncpy(str2, str1, 18);
	for (int i = 0; i < 30; i++)
	{
		printf("%c", str2[i]);
	}
	return 0;
}

 写法2,不使用指针:

char * mystrncpy(char * dest, const char * src, size_t n)
{
	int i;
    for (i = 0; src[i] && i < n; i++)
    {
        dest[i] = src[i];
    }
   
    if (i < n)
    {
        dest[i] = 0;
    }
    return dest;
}

上面函数在变化的只有 i ,dest 仍然是指向目标函数的首地址,因此可以直接返回。

七、strncat 函数

7.1 语法形式

char * strncat ( char * destination, const char * source, size_t num );

strcat函数在追加的时候要将源字符串的所有内容,包括 \0 都追加过去;但 strncat 函数指定了追加的长度。

7.2 使用示例及注意事项 

① strncat函数都会在追加完要求长度的字符之后,再追加一个\0;如果源字符串长度不够指定长度的,只会追加一个 \0 ,与 strncpy 函数缺多少补多少不同。

②  strncat 函数中源字符串中不一定要有 \0

 7.3 模拟实现 strncat 函数

写法1:

#include <Stdio.h>
#include <assert.h>

char* myStrncat(char* dest, const char* src, size_t num)
{
	assert(dest && src);
	char* ret = dest;
	while (*dest != '\0')
	{
		dest++;
	}
	while (num-- && (*dest++ = *src++))
	{
		;
	}
	*dest = '\0'; // 确保目标字符串以\0结尾
	return ret;
}
int main()
{
	char str1[200] = "Hello, I'm Jack."; // 要记得预留足够的空间才能在后面追加字符串
	char str2[] = "Nice to meet you.";
	myStrncat(str1, str2, 12);
	for (int i = 0; i < 30; i++)
	{
		printf("%c", str1[i]);
	}
	return 0;
}

写法2:

char* mystrncat(char* dst, const char* src, size_t n)
{
	char* tmp = dst;

	while (*dst)
	{
		dst++;
	}

	int i;
	for (i = 0; src[i] && i < n; i++)
	{
		dst[i] = src[i];
	}

	dst[i] = 0;
	return tmp;
}

八、strncmp 函数

与 strcmp 函数非常类似,多了一个可以自定义的长度使得 strncmp 函数比 strcmp 函数更灵活且安全而已,此处不再赘述。

8.1 语法形式

int strncmp ( const char * str1, const char * str2, size_t num );

8.2 使用示例

#include <stdio.h>
#include <string.h>

int main()
{
	char str1[] = "abc";
	char str2[] = "abcdef";
	int r = strncmp(str1, str2, 3); // 输出str1 == str2
    //int r = strncmp(str1, str2, 4); // 输出str1 < str2
	if (r > 0)
		printf("str1 > str2");
	else if (r < 0)
		printf("str1 < str2");
	else
		printf("str1 == str2");

	return 0;
}

九、strstr 函数

 9.1 语法形式

char * strstr ( const char * str1, const char * str2);

功能:strstr函数,查找str2指向的字符串在str1指向的字符串中第一次出现的位置。

简而言之:在⼀个字符串中查找子字符串。使用时记得包含头文件<string.h>

参数:str1指针,指向了被查找的字符串;str2指针,指向了要查找的字符串。

返回值:
① 如果str1指向的字符串中存在str2指向的字符串,那么返回第一次出现位置的指针;
② 如果str1指向的字符串中不存在str2指向的字符串,那么返回NULL。

9.2 使用示例

 #include <stdio.h>
 #include <string.h>

int main()
{
	char str1[] = "abcdjackefgjack";
	char str2[] = "jack";
	char* r = strstr(str1, str2);
	if(r != NULL)
		printf("找到了,%s\n", r);
	else
		printf("没找到\n");

	return 0;
}

 找到的话,返回的值是要查找的字符串在被查找的字符串中首次被找到的地址,因此输出的时候会将被查找字符串连同要查找的字符串内容+后面的字符一起打印出来。

9.3 模拟实现 strstr 函数

 此处模拟实现的思路比较复杂,笔者将通过画图的方式解释希望同学们能看懂:

#include <stdio.h>
#include <assert.h>

char* my_strstr(const char* str1, const char* str2)
{
  	assert(str1 && str2);
	char* p = str1;
	char* s1 = NULL;
	char* s2 = NULL;
	// 特殊情况处理
	if (*str2 == '\0')
	{
		return str1; // str1被const修饰,而函数返回类型是char*,因此此处出现警告
	}


	while (*p) // 枚举查找的次数
	{
		s1 = p;
		s2 = str2;
		// 找一次的匹配过程
		// while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
		while (*s1 && *s2 && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
			return p;
		p++;
	}
	return NULL;
}
int main()
{
	char str1[] = "abcdjackefgjack";
	char str2[] = "jack";
	// char str2[] = ""; //如果为空,则my_strstr函数返回str1的首字符地址
	
	char* r = my_strstr(str1, str2);
	if(r != NULL)
		printf("找到了,%s\n", r);
	else
		printf("没找到\n");

	return 0;
}

通过类型匹配、添加 const 修饰等方式对上述代码进行优化,使得代码更加健壮:

char* my_strstr(const char* str1, const char* str2)
{
  	assert(str1 && str2);
	const char* p = str1; // 类型匹配,赋值才不会报警告
	const char* s1 = NULL; // 这里和下一行也要用const修饰的原因是下面循环中进行了赋值
	const char* s2 = NULL;
	// 特殊情况处理
	if (*str2 == '\0')
	{
		return (char*)str1; //将其强制类型转化
		//return str1; // str1被const修饰,而函数返回类型是char*,因此此处出现警告
	}


	while (*p) // 枚举查找的次数
	{
		s1 = p;
		s2 = str2;
		// 找一次的匹配过程
		// while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
		while (*s1 && *s2 && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
			return (char*)p; // 强制类型转化以满足函数返回类型
		p++;
	}
	return NULL;
}

十、strtok 函数

10.1 语法形式

char *strtok(char *str, const char *delim);

功能:
① 分割字符串:根据delim参数中指定的分隔符,将输入字符串str拆分成多个子字符串;
② 修改原始字符串:strtok会直接在原始字符串中插入\0终止符,替换分隔符的位置,因此原始字符串会被修改。 (一般操作:创建一个空字符串后使用strcpy函数将原始字符复制一份再进行分割。)

参数:
str:首次调用时传入待分割的字符串;后续调用传入NULL,表示继续分割同一个字符串。
delim:包含所有可能分隔符的字符串(每个字符均视为独立的分隔符)。

返回值:
① 成功时返回指向当前子字符串的指针。
② 没有更多子字符串时返回NULL

使用步骤:
① 首次调用:传入待分割字符串和分隔符。
② 后续调用:传入NULL和相同的分隔符,继续分割。
③ 结束条件:当返回NULL时,表示分割完成。

10.2 使用示例

#include <stdio.h>
#include <string.h>

int main()
{
	char arr[] = "Jack@green.cn";
	char sep[] = "@.";

	//char* p1 = strtok(arr, sep); // 因为strtok函数会修改原始字符串(将@修改为\0),因此作出下面的修改:
	char duf[30] = { 0 };
	strcpy(duf, arr);

	char* p = strtok(duf, sep);
	printf("%s\n", p); // Jack  遇到\0停止输出了

	p = strtok(NULL, sep); // 再次调用strtok函数时传的不再是duf,而是NULL
	printf("%s\n", p); // green

	p = strtok(NULL, sep);
	printf("%s\n", p); // cn

	//p = strtok(NULL, sep);
	//printf("%s\n", p); // (null) 后面没有内容时将会输出空指针,实际上不能打印任何东西,此处只是测试用

	return 0;
}

调用一次就输出分割字符前的一段;再次调用输出第二段……以此类推,直到没有内容的时候就返回空指针。但是这种写法属于手动,需要自行查看源字符串被分隔符分成几段从而调用几次 strtok 函数。

正确使用方式应该是使用 for 循环:

#include <stdio.h>
#include <string.h>

int main()
{
	char arr[] = "Jack@@green.cn";
	char sep[] = "@.";

	char buf[30] = { 0 };
	strcpy(buf, arr);

	char* p = NULL;
	for (p = strtok(buf, sep); p != NULL; p = strtok(NULL, sep))
	{
		printf("%s\n", p);
	}

	return 0;
}

10.3 注意事项

 ① 破坏性操作:strtok函数会直接修改原始字符,将其中的分隔符替换为 \0 。如果需要保留原字符串,应先拷贝一份。

② 连续分隔符:多个连续的分隔符会被视为单个分隔符,不会返回空字符串。

③ 空指针处理:如果输入的str为NULL且没有前序调用,行为未定义。意思是一开始调用strtok函数就写入NULL作为参数,程序会崩溃的:

char* p = NULL;
for (p = strtok(NULL, sep); p != NULL; p = strtok(NULL, sep))
{
	printf("%s\n", p);
}

十一、strerror 函数

11.1 语法形式

char* strerror ( int errnum );

功能:

① strerror函数可以通过参数部分的errnum表示错误码,得到对应的错误信息,并且返回这个错误信息字符串首字符的地址。
② strerror函数只针对标准库中的函数发生错误后设置的错误码的转换。
③ strerror的使用需要包含<string.h>

在不同的系统和C语言标准库的实现中都规定了一些错误码,一般是放在 errno.h 这个头文件中说明的,C语言程序启动的时候就会使用一个全局的变量 errno 来记录程序的当前错误码,只不过程序启动的时候 errno 是0,表示没有错误,当我们在使用标准库中的函数的时候发生了某种错误, 就会将对应的错误码,存放在 errno 中,而⼀个错误码的数字是整数,很难理解是什么意思,所以每一个错误码都是有对应的错误信息的。strerror 函数就可以将错误码对应的错误信息字符串的地址返回。 

参数:
errnum:表示错误码
这个错误码一般传递的是errno这个变量的值,在C语言有一个全局的变量叫:errno,当库函数的调用发生错误的时候,就会将本次错误的错误码存放在errno这个变量中,使用这个全局变量需要包含一个头文件<errno.h>

返回值:函数返回通过错误码得到的错误信息字符串的首字符的地址。

11.2 使用示例

打印0~10这些整数错误码对应的信息

#include <stdio.h>
#include <string.h>

int main()
{
	int i = 0;
	for (; i <= 10; i++)
	{
		printf("%d: %s\n", i, strerror(i));
	}
	return 0;
}

在Windows11+VS2022环境下输出的结果:

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
6: No such device or address
7: Arg list too long
8: Exec format error
9: Bad file descriptor
10: No child processes

演示:

#include <stdio.h>
#include <string.h>
#include <errno.h>

int main()
{
	// C语言中有一个函数可以打开文件夹
	// fopen
	// 如果以读的方式打开文件,文件是必须要存在的,如果文件不存在,则打开文件失败
	// fopen函数就会将错误码放在errno中
	// 同时函数会返回NULL

	FILE* pf = fopen("data.text", "r"); // 以r读的方式打开当前文件夹下名为"data.text"的文件
	if (pf == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	// 读文件

	// 关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

上述代码在当前文件夹下如果没有一个命名为 data 后缀名为 .text 的文件的话将会输出:No such file or directory;如果当前文件夹下有这个文件,将无事发生。

11.3 认识 perror 函数

perror 是 printf + strerror 命名的

11.3.1 语法形式
void perror ( const char* str );

perror 函数相当于 printf("%s\n", strerror(errno));,直接将错误信息打印出来,是它自动去找错误的地方并打印出来,不用管 errno。
perror 函数打印完参数部分的字符串后,再打印⼀个冒号和⼀个空格,再打印错误信息。

11.3.2 使用示例
#include <stdio.h>

int main()
{
	FILE* pf = fopen("data.text", "r");
	if (pf == NULL)
	{
		perror("test"); // test是自己指定的,也可以写“错误信息是”,输出就是“错误信息是: xxxxxxx”
		return 1;
	}
	// 读文件

	// 关闭文件
	fclose(pf);
	pf = NULL;

	return 0;
}

上述程序输出test: No such file or directory
如果改为 perror("错误信息是"); 那么输出结果就是错误信息是: No such file or directory


 好了,以上就是字符串函数的一些内容,可能笔者还没讲全,也可能出现一些错误,欢迎同学们来指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值