c语言字符串函数讲解和模拟实现

目录

一.字符分类函数 

二. 字符转换函数

三.strlen的使⽤和模拟实现

1.使用

2.模拟实现

​编辑

四.strcpy的使⽤和模拟实现

1.使用

2.模拟实现strcpy函数

五. strcat的使⽤和模拟实现

1.使用

2.模拟实现strcat函数

六.strcmp的使⽤和模拟实现

1.使用

​编辑

2.strcmp模拟实现

七.strncpy函数的使⽤ - 拷贝

八.strncat函数的使⽤ - 追加

九. strncmp函数的使用- 比较

十.strstr的使⽤和模拟实现 - 找相同字符串

1.使用

2.strstr的模拟实现

3.分析:

十一.strtok函数的使用

十二.strerror函数的使用

十三.perror函数的了解


在编程的过程中,我们经常要/*处理字符和字符串*/,为了⽅便操作字符和字符串,C语⾔标准库中提供了⼀系列库函数。
比如:写一个判断大写字母和小写字母

代码举例:

int main()
{
	int ch = getchar();
	if (ch >= "A" && ch <= "Z")
	{
		printf("大写\n");
	}
	if (ch >= "a" && ch <= "z")
	{
		printf("小写\n");
	}
	return 0;
}

这样输入字符判断写代码太麻烦,所以C语言给了一系列库函数判断使用。

一.字符分类函数 

判断字符属于那一类的函数,都使用头文件<ctype.h>
函数如果他们的阐述符合下列条件就返回真
iscntrl:判断一个字符是不是控制字符。
isspace:判断是不是空白字符:就是敲出来看不见的字符:空格'',换页'\f',换行'\n',回车'\r',制表符'\t'或者垂直制表符'\v'
isdigit:判断十进制数字是不是/*数字字符*/,'0'~'9'
isxdigit:判断是不是十六数字字符,包括所有十进制数字字符,小写字母'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:判断是不是可打印字符,包括图形字符和空白字符 - ASCII码表0-31这32个字符不可打印字符。
这里的所有的库函数使用习惯是一样的。

比如:讲解islower和isupper
先要知道这个函数的类型和参数:int islower(int c)
1.参数是ASCLLII码值也行,字符也行
2.如果为真返回非0的值,如果为假返回0//Return Value

代码举例:

int main()
{
	int ch = getchar();
	int ret = islower(ch);
	printf("%d\n", ret);//看下函数的返回值
	if (islower(ch))
	{
		printf("小写\n");
	}
	else if (isupper(ch))
	{
		printf("大写\n");
	}
	return 0;
}

输入一个字符用islower函数和isupper函数判断。


练习:
写⼀个代码,将字符串转换成小写
//方法遍历数组,用个循环

int main()
{
	char arr[] = "I Am A Student";
	int i = 0;
	while (arr[i])
	{
		if (isupper(arr[i]))
		{
			arr[i] = arr[i] + 32;
		}
		putchar(arr[i]);
		i++;
	}
	return 0;
}

二. 字符转换函数

C语⾔提供了2个字符转换函数:库函数直接转换大小写字母
1.int tolower(int c); 将参数传进去的⼤写字⺟转⼩写 
2.int toupper(int c); 将参数传进去的⼩写字⺟转⼤写

代码举例1:

int main()
{
	char arr[] = "I Am A Student";
	int i = 0;
	while (arr[i])//当arr[i] = '\0'的时候跳出循环
	{
		if (isupper(arr[i]))//判断是不是大写字母,是就进入循环
		{
			//arr[i] = arr[i] + 32;
			arr[i] = tolower(arr[i]);
		}
		putchar(arr[i]);
		i++;
	}
	return 0;
}

代码举例2:

int main()
{
	int ch = getchar();
	//int r = tolower(ch);
	int r = tosupper(ch);
	printf("%c\n", r);
	return 0;
}

输入一个字母就能进行大小写改变。

总结:也能自己实现这些函数,但自己写太麻烦了,所以我选择直接用。

三.strlen的使⽤和模拟实现

1.使用

size_t strlen(const char* str);
字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前⾯出现的字符个数(不包含 '\0')。
注意事项:
• 参数指向的字符串必须要以 '\0' 结束。
• 注意函数的返回值为size_t,是⽆符号的( 易错 ),因为返回的元素个数不可能是负数
• strlen的使⽤需要包含头⽂件
• 学会strlen函数的模拟实现

代码举例:要清楚返回类型size_t

int main()
{
	if (strlen("abc") - strlen("abcdef")>0) //3-6=3
	{
		printf("大于\n");
	}
	else
	{
		printf("小于等于\n");
	}
	return 0;
}

输出的结果是:大于
因为strlen函数返回的是无符号整形,无符号数字一定是大于等于0的,不可能等于负数。

代码举例:strlen返回类型size_t,想要得到负数值

int main()
{
	if ((int)strlen("abc") -(int) strlen("abcdef") > 0) //3-6 = -3
	{
		printf("大于\n");
	}
	else
	{
		printf("小于等于\n");
	}
	return 0;
}

输出的结果是:小于等于
因为只要强制类型转换成(int)strlen函数返回类型就可以了,有符号整数相减就得到负数

2.模拟实现

代码举例:方法1

size_t my_strlen1(const char* str)
{
	int count = 0;//count是临时变量
	assert(str);//判断不等于空指针
	while (*str)
	{
		count++;
		str++;
	}
	return count;
}
int main()
{
	char arr[] = "abcdef";
	size_t len = my_strlen1(arr);
	printf("%zd\n", len);//6
	return 0;
}

代码举例:方法2 指针-指针得到元素个数

size_t my_strlen2(const char* str)
{
	assert(str);
	const char* start = str;//start是临时变量
	while (*str)
	{
		str++;
	}
	return str - start;
}
int main()
{
	char arr[] = "abcdef";
	size_t len = my_strlen2(arr);
	printf("%zd\n", len);//6
	

代码举例:方法3 写个函数不能使用临时变量求字符串长度 - 使用递归

分析:my_strlen3("abcdef");
1+my_strlen3("bcdef");
1+1+my_strlen3("cdef");
1+1+1+my_strlen3("def");
1+1+1+1+my_strlen3("ef");
1+1+1+1+1my_strlen3("f");
1+1+1+1+1+1my_strlen3("");
1 + 1 + 1 + 1 + 1 + 1 + 0; -- 去掉一个元素+1,相当于地址+1,当等于'\0'返回0

size_t my_strlen3(const char* str)
{
	if (*str == '\0')//当条件满足,回归
	{
		return 0;
	}
	else
	{
		return 1 + my_strlen3(str + 1);//(str+1)相当于b的地址传给了下个str,以此递推
	}
}
int main()
{
	char arr[] = "abcdef";
	size_t len = my_strlen3(arr);
	printf("%zd\n", len);//6
	return 0;
}

红色是递推的过程

蓝色是递归的过程

四.strcpy的使⽤和模拟实现

1.使用

strcpy的作用是拷贝字符的。 -- <string.h>
char* strcpy(char* destination, const char* source);
是把source指向的字符串拷贝destination空间里面去,包括原字符串\0也会拷贝过去。
参数(Parameters)
destination:指向要被内容覆盖的目标数组的指针 - 目标字符串
source:要拷贝的字符串 - 源头字符串

返回值(Return Value)
返回源字符串起始位置
注意事项:
• 源字符串必须以 '\0' 结束。
• 会将源字符串中的 '\0' 拷⻉到⽬标空间。- \0拷贝过去说明字符串拿完了
• ⽬标空间必须⾜够⼤,以确保能存放源字符串。 - 要是目标空间放不小源字符串,会拷贝过去,但程序会崩掉
• ⽬标空间必须可修改。- 目标空间不能是常量字符串
• 学会模拟实现。

代码举例:字符串拷贝应该放到字符数组

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

2个数组都有\0的时候,拷贝\0的看不出变化。

代码举例:为了看出拷贝\0的变化 

int main()
{
	char arr1[20] = "XXXXXXXXXX";//给数组提前放点东西,要超过hello的长度
	char arr2[] = "hello";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

发现确实拷贝了\0。

代码举例:如果不拷贝\0的结果

int main()
{
	char arr1[20] = "XXXXXXXXXX";
	char arr2[] = {'a','b','c'};
	strcpy(arr1, arr2);
	printf("%s\n", arr1);//abc后面随机值
	return 0;
}

结果abc确实会覆盖过去,后面的随机内容也会拷贝过去。

代码举例:⽬标空间必须⾜够⼤

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

结果:arr2的字符串会拷贝到arr1里面去,但是程序会崩掉。

代码举例:⽬标空间必须可修改

int main()
{
	char*p = "abcdefghigklmn";//常量字符串,不能修改 
	char arr2[] = "hello,world";
	strcpy(p, arr2);//目标空间是课修改的。
	printf("%s\n", arr1);
	return 0;
}

结果:会报错,写入某个位置会发生访问冲突

实现原理:
1.覆盖
2.覆盖到源头字符串\0为止

2.模拟实现strcpy函数

代码举例:方法不太好

void my_strcpy(char* dest, char* src)
{
	while (*src != '\0')//1.拷贝\0之前的,当*src == '\0'就跳出循环
	{
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src;//2.这里拷贝\0,就不会读取后面字符串的字符个数
}
int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "abcdef";
	my_strcpy(arr1, arr2);
	printf("%s", arr2);
	return 0;
}

代码举例:代码优化

char* my_strcpy(char* dest, char* src)
{
	assert(dest && src);
	char* ret = dest;//用指针变量ret保存目标起始返回地址
	while (*dest++ = *src++)//在表达式判断之前,表达式先赋值过去算出结果,为假才跳出去
	{
		;//空语句
	}
	return ret;//返回目标字符串起始地址
}
int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "abcdef";
	printf("%s", my_strcpy(arr1, arr2));
	return 0;
}

解析表达式:*dest++ = *src++
++的优先级比解引用优先级高
因为后置++,先解引用完后++指向下个字符

深入学习:了解本质
NULL -- 本质是#include NULL 0 也是0,一般用于指针初始化。
\0 -- 是\ddd形式的转义字符,本质也是0,一般字符串的末尾会有\0,是字符串结束标志
0 -- 数字0,数学那个0
null或NUL -- 都表示\0
'0' -- 字符0,ASCII码值是48,本质是48

五. strcat的使⽤和模拟实现

1.使用

strcat的作用是在一个字符串后面追加一个字符串 -- <string.h>
char* strcat(char* destination, const char* source)
是把source源头字符串追加到destination目标字符串后面,返回目标字符串起始地址。
注意事项:

• 源字符串必须以 '\0' 结束。
• ⽬标字符串中也得有 '\0',否则没办法知道追加从哪⾥开始。
• ⽬标空间必须有⾜够的⼤,能容纳下源字符串的内容。
• ⽬标空间必须可修改。
• 字符串⾃⼰给⾃⼰追加,如何?

代码举例:使用strcat

int main()
{
	char arr1[20] = "hello ";
	char arr2[] = "world";
	printf("%s",strcat(arr1, arr2));
}

实现的原理:得先从把目标字符串\0覆盖掉,在往后面追加
1.找到目标字符串的\0
2.拷贝源头字符串的数据到目标空间的\0位置及后面的空间

2.模拟实现strcat函数

代码举例:

char* my_strcat(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest && src);
	//1.找到目标空间\0的地址
	while (*dest != '\0')
	{
		dest++;//拿到目标字符串\0的地址
	}
	//2.拷贝源头字符串追加到目标字符串后面
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[20] = "hello ";
	char arr2[] = "world";
	printf("%s", my_strcat(arr1, arr2));
	return 0;
}

代码举例:模拟实现strcat函数,能不能追加自己

char* my_strcat(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest && src);
	//1.找到目标空间\0的地址
	while (*dest != '\0')
	{
		dest++;//拿到目标字符串\0的地址
	}
	//2.拷贝源头字符串追加到目标字符串后面
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[20] = "hello";
	char arr2[] = "world";
	printf("%s", my_strcat(arr1, arr1));
	return 0;
}

不可以,因为\0被h覆盖了永远找不到,代码最终会死循环然后他俩一直往后追加越界访问,程序会崩掉。
以后尽量不要使用strcat,虽然库里面能自己追加,但是就算库里面的也少使用,使用也不要自己追加自己。

六.strcmp的使⽤和模拟实现

1.使用

strcmp的作用是字符串比较 -- <string.h>
比较的是对应位置上的字符大小,小的字符所在的字符串,小于另外一个字符串,注意比较的不字符串长度
int strcmp(const char* str1, const char* str2);
比较参数str1指向的字符串和str2指向的字符串
比较的结果:返回值是整形
• 标准规定:
◦ 第⼀个字符串⼤于第⼆个字符串,则返回⼤于0的数字
◦ 第⼀个字符串等于第⼆个字符串,则返回0
◦ 第⼀个字符串⼩于第⼆个字符串,则返回⼩于0的数字

代码举例:一般是来判断strcmp返回值的。

int main()
{
	int ret = strcmp("abcdef", "abq");
	if (ret > 0)
	{
		printf("大于\n");
	}
	else if (ret == 0)
	{
		printf("等于\n");
	}
	else
	{
		printf("小于\n");
	}

	return 0;
}

2.strcmp模拟实现

int my_strcmp(const char* s1, const char* s2)//这2个指针要const修饰,因为不希望被修改
{
	while (*s1 == *s2)//先判断是不是相等,因为不止一次判断所以用while循环,不相等就跳出循环谁大
	{
		if (*s1 == '\0')//因为2个字符串相等才会进入循环,其次字符串判断到\0,说明2个字符串相等
		{
			return 0;
		}
		s1++;
		s2++;
	}
	/*方法1
	if (*s1 > *s2)
	{
		return 1;//因为判断的返回值
	}
	else
	{
		return -1;
	}*/

	//方法2:
	return *s1 - *s2;//返回2个元素相减的ASCII码值
}
int main()
{
	char arr1[] = "abc";
	char arr2[] = "abcdef";
	int ret = my_strcmp(arr1, arr2);
	if (ret > 0)
	{
		printf("大于\n");
	}
	else if (ret == 0)
	{
		printf("等于\n");
	}
	else
	{
		printf("小于\n");
	}

	return 0;
}

"abc";
"abcdef"相比是a跟a比,b跟b相比,c跟c相比,\0跟d比,\0的ASCII码值是0最小的。

知识补充:
%d
%hd - short
%ld - long
%lld - longlong
只要有d打印都是有符号数

%u - 无符号数 
%hu - 短整型无符号数
%lu 
%zu - 彻底针对无符号数打印,%zd是vs提醒采用,打印无符号数最好用%zu,只要打印符号值就行

总结:
1.strcpy - 要一直拷贝到连'\0'拷贝过去为止
2.strcat - 要一直追加到连'\0'追加过去为止
3.strcmp - 一直要比较到'\0'为止
这3个函数没有明确长度指定要一直追加到\0,这三个函数叫长度/*不受限限制的字符串函数*/,他们没有长度追加几个,拷贝几个,比较几个,没有长度的限制! - //在vs看来是相对不安全的
4.strncpy - strncat - strncmp/*长度受限制的字符串函数*/,//相对安全

七.strncpy函数的使⽤ - 拷贝

char* strncpy(char* destination, const char* source, size_t num);
拷⻉num个字符从源字符串到⽬标空间
• 如果源字符串的⻓度⼩于num,则拷⻉完源字符串之后,在⽬标的后边追加0,直到num个。
参数:num
意思是:最多到source字符串拷贝num个字符到destination字符串里面去。

代码举例:使用strncpy函数的使⽤  

int main()
{
	char arr1[20] = "xxxxxxx";
	char arr2[] = "hello";
	strncpy(arr1, arr2, 3);
	return 0;
}

不会拷贝'\0',你让我拷贝3个就拷贝3个不会多拷贝。

int main()
{
	char arr1[20] = "xxxxxxx";
	char arr2[] = "hello";
	strncpy(arr1, arr2, 7);
	return 0;
}

你让拷贝7个就拷贝7个,如果源字符串字符个数不够,在后面补'\0',补到7个为止。

八.strncat函数的使⽤ - 追加

这个函数适合自己追加自己,追加完了还会自动放个\0。
char* strncat(char* destination, const char* source, size_t num);
参数:num
意思是:最多追加的字符字符个数。

代码举例:使用strcat函数的使⽤ 

int main()
{
	char arr1[20] = "hello\0xxxxxxxx";
	char arr2[] = "world";
	strncat(arr1, arr2, 4);
	r

你让我追加4个就追加4个字符,在末尾要单独放个\0,追加完还是个字符串,要是追加的个数>源字符串的个数,把源字符串全部追加过去,再最多把源字符串\0追加过去,你在让我追加不会追加了。

九. strncmp函数的使用- 比较

int strncmp(const char* str1, const char* str2, size_t num);
参数:num
意思是:2个字符串最多比较num个字符,如果num个字符都相等,就是相等返回0.

代码举例:使用strncmp函数的使⽤ - 比较

int main()
{
	char arr1[20] = "helloxxxxxxxx";
	char arr2[] = "world";
	int ret = strncmp(arr1, arr2, 4);
	printf("%d", ret);
	

2个字符字符串你让我比较几个就比较几个,但比较次数要在有限范围内。

十.strstr的使⽤和模拟实现 - 找相同字符串

1.使用

strstr的作用是在一个字符串找另一个字符串 -- <string.h>
char* strstr(const char* str1, const char* str2);
参数:
在str1字符串找str2字符串。
返回值(Return Value)
1.返回字符串str2在字符串str1中第⼀次出现的位置。
2.str2在字符串str1找不到返回NULL。
3.字符串的⽐较匹配不包含 \0 字符,str2以 \0 作为结束标志。

代码举例:strstr函数的使⽤

int main()
{
	char arr1[] = "abcdefabcdef";
	char arr2[] = "def";//deq,找不到会返回NULL
	char* ret = strstr(arr1, arr2);
	if (ret != NULL)
	{
		printf("%s\n", ret);//defabcdef
	}
	else
	{
		printf("找不到\n");
	}
	return  0;
}

2.strstr的模拟实现

代码举例:这是暴力求解方式,可以用KMP算法 - 难理解,效率高。

char* my_strstr(const char* str1, const char* str2)
{
	const char* cur = str1;//cur记录起始位置
	const char* s1 = NULL;//遍历str1字符串
	const char* s2 = NULL;//遍历str2字符串

	assert(str1 && str2);
	if (*str2 == '\0')//判断第二个字符串第一个字符是否为空字符串
	{
		return (char*)str1;//一个被cosnt修饰为安全的,但返回是一个没被修饰不安全的,所以要强制类型转换 
	}
	while (*cur)//判断cur是否等于\0,或NULL
	{
		s1 = cur;
		s2 = str2;
		while (*s1 && *s2 && *s1 == *s2)
		{
			//*s1==\0 为假就跳出来,判断*s2 == \0,说明相等
			//*s2==\0 为假就跳出来,判断*s2 == \0,说明找到了
			//*s1==\0 为假就跳出来,判断*s2 != \0,说明找不到
			s1++;
			s2++;
		}
		if (*s2 == '\0')//只要为假都会跳出来判断*s2,等于说明
		{
			return (char*)cur;
		}
		cur++;
	}
	return NULL;
}
int main()
{
	char arr1[] = "abbbcdef";
	char arr2[] = "bbc";
	char* ret = my_strstr(arr1, arr2);
	if (ret != NULL)
	{
		printf("%s\n", ret);
	}
	else
	{
		printf("找不到\n");
	}
	return  0;
}


3.分析:

1.考虑3种情况 - //测试用例
(1)abcdef bcd 
(2)abbbcdef bbc 
(3)abcdef bcq - 模拟实现要考虑这3种情况
2.创建3个指针
(1)s1遍历str1字符串 - 执向arr1起始位置
(2)s2遍历str2字符串 - 执向arr2起始位置
(3)cur记录str1被查找元素的位置,要是cur==\0,不进入循环,直接返回NULL因为空字符串找不到
3.s1!=s2 //重新遍历是个整体循环
(1)cur++ //cur是主循环
(2)s1 == cur指向起始位置//s1的位置会有变化
(3)s2 == str2首元素地址//s2的位置还是str2首元素地址
(4)重新遍历判断
(5)s1==\0,说明找不到了,返回NULL
4.s1==s2
(1)s1++
(2)s2++
(3)相等是循环用while,只要不相等cur++在重新遍历判断
5.\0为结束标准,要判断*s2==\0不,等于说明就找到了,就返回cur起始的位置
6.BUG第四种情况
(1)abcdef\0 == abcdef\0;这2个字符串相等
    s1和s2会一直++,同时加到\0,发现还相等
    还会一直++,会越界访问。//所以2个相等的字符串一直走
(2)在是s1 == s2相等之前首先得判断一下各自是否为\0
(3)如果s1已经等于\0,s2还没找到\0,说明肯定找不到s2的字符串
(4)如果s2已经找到\0,说明已经找到了,可以停下来了
7.三种情况要停下来
1.s1找到\0,也*s2 == \0,找到了 
2.s2找到了\0 - 找到了
3.*s1 != *s2

总结:写代码一定要考虑周到,所以公司有个代码测试岗位,测试代码在任何场景

十一.strtok函数的使用

解析:
比如:"zhangsan@163.com" - "@和."是分割符,隔开的字符串 - 怎么提取出来?
zhangsan - 邮箱名字
163 - 域名
com - 域名后缀
char* strtok(char* str, const char* sep);
参数解析:
1.sep参数指向⼀个字符串,定义了⽤作分隔符的字符集合。单独用个字符串放分割符"@.",指针指向了常量字符串。
2.str第⼀个参数指定⼀个字符串,是由sep字符串里面的字符分割开的标记。
3.功能:strtok函数找到str中的下⼀个标记,并将其⽤ \0 结尾,返回⼀个指向这个标记的指针。
(注:strtok函数会改变被操作的字符串,所以在使⽤strtok函数切分的字符串⼀般都是临时拷⻉的内容并且可修改。)
zhangsan@163.com - 他会从str指向的字符串找到分割符的标记,并把@改成以\0结尾,同时会返回起始位置z,但是我给你一个字符串,不能被你改掉,得拷贝一份进行操作。
4.strtok函数的第⼀个参数不为NULL,函数将找到str中第⼀个标记, 并把@改成以\0结尾,同时会返回起始位置z,strtok函数将保存它在字符串中的位置。
5.strtok函数的第⼀个参数为 NULL,函数将在同⼀个字符串中被保存的位置开始,查找下⼀个标记。
6.如果字符串中不存在更多的标记,则返回 NULL 指针。

代码举例:方法一

int main()
{
	char arr1[] = "zhangsan@169.com";
	char arr2[30] = { 0 };//"zhangsan\0169\0com";
	strcpy(arr2, arr1);
	const char* p = "@.";

	char* s = strtok(arr2, p);
	printf("%s\n", s);//zhangsan

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

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

	return 0; 
}

代码举例:方法二

int main()
{
	char arr1[] = "zhangsan@169.com#hehe";
	char arr2[30] = { 0 };//"zhangsan\0169\0com";
	strcpy(arr2, arr1);
	const char* p = "@.#";

	char* s = NULL;
	//利用了for循环初始化只执行一次
	for (s = strtok(arr2, p); s != NULL;s = strtok(NULL,p))
	{
		printf("%s\n", s);
	}

	return 0;
}

讲解:
1.第一次调用,初始化s的值。
2.第一次调用完,判断s的值因为\0是结束标记,没有更多分隔符标记,返回NULL,说明找完了跳出循环。并返回保存好位置第一个字符的位置。
3.后面几次调用,第一个参数传NULL,从被保存好的位置查找,调用完返回的地址给s, 不为NULL就打印,为NULL跳出循环 。
奇怪点:函数第一次调用之后里面的变量不是不在了嘛?为啥的保存地址?因为这个函数可能有静态变量保存,出了函数不销毁。

十二.strerror函数的使用

char* strerror(int errnum);
功能:strerror函数可以把参数部分错误码对应的错误信息的字符串地址返回来。对应的错误码翻译成错误信息。错误码0 - 9.
参数:errnum参数部分传递的是错误码。


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

代码举例:验证错误码

int main()
{
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d: %s\n", i,strerror(i));//0-9的数字翻译成对应的错误码。
	}

	return 0;
}

c语言是可以进行文件操作的
打开文件用到函数fopen:FILE * pF = fopen(...);
如果文件打开成功,则返回一个地址
如果文件打开失败,则返回NULL

strtrror函数:<string.h>
errno函数:<errno.h>

代码举例:使用

int main()
{
	FILE* pFile;
	pFile = fopen("unexist.ent", "r");
	//打开一个unexist.ent文件,如果文件不存在,打开失败错误码信息放在errno全局变量中
	if (pFile == NULL)
		printf("Error opening file unexist.ent: %s\n", strerror(errno));
	else
		printf("打开文件成功\n");
	
	return 0;
}

总结:这个strerror这个函数不仅可以用文件操作上,只要你认为这个库函数使用的时候出错了,就能立马调用它,但是调用晚了不行,比如下个库函数也出错了,反映的就不是上个库函数的错误信息了,每次发生错误都会更新全局变量errno变量值的信息。

十三.perror函数的了解

void perror(const char* str);
perror函数直接将错误信息打印出来。perror函数打印完参数部分的字符串后,再打印⼀个冒号和⼀个空格,再打印错误信息。
perror函数的使用:首先会把自定义信息打印出来,自己会在第一个字符串后面加上冒号打印错误信息。
//使用perror根本不会关心errno,已经分装在里面了,会自动把错误码所对应的错误信息打印出来。

代码举例:

int main()
{
	FILE* pFile;
	pFile = fopen("unexist.ent", "r");
	//打开一个unexist.ent文件,如果文件不存在,打开失败错误码信息放在errno全局变量中
	if (pFile == NULL)
		//printf("Error opening file unexist.ent: %s\n", strerror(errno));
		perror("Error opening file unexist.ent");
	else
		printf("打开文件成功\n");

	return 0;
}

未来选择perror和strerror那个函数:
1.perror打印的错误信息是你想要的,你就可以用这个函数,不用借助printf打印,它直接就给你打印了。
2.如果你只想拿到错误信息,还不想打印就必须用strerror函数,这个函数只能拿到错误信息不打印,借助printf可以打印。
3.可以认为perror函数分装了strerror这个函数。
 

  • 19
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值