C语言----字符函数和字符串函数
在编程的过程中,我们要经常处理字符和字符串,为了方便操作字符和字符串,c语言标准库中提供的一系列库函数,接下来我们就开始学习与认识他们
1.字符分类函数
c语言中有一系列的函数是专门做字符分类的,也就是一个字符是属于什么类型的字符的
这些函数的使用需要包含一个头文件:ctype.h
用islower举例
格式:int islower (int c)
为什么后面的是int c--用整型接受的,因为传过来的是字符或者对应的ASCII值
那么我们就用整型接受
返回值也是int
如果这个字符是小写字母的话,那么返回值就是一个非0数字
如果这个字符是一个大写字符的话,那么返回值就是0
总之:如果括号内的不是小写字母,那么这个函数就会返回一个0
是小写字母就返回一个非0数字
int main()
{int ret1 = islower('b');//2printf("%d\n", ret1);int ret2 = islower('A');//0printf("%d\n", ret2);int ret3 = islower('0');//字符0不是字母printf("%d\n", ret3);return 0;
}
int main()
{int ret1 = isdigit('A');//不是数字字符就返回0printf("%d\n", ret1);int ret2 = isxdigit('A');//A是16进制的字符--返回的就是非0值printf("%d\n", ret2);return 0;
}
//写一个代码,将字符串中的小写字母转换成大写字母,其他字符不变
int main()
{char arr[] = "I am a student";//末尾隐藏\0//需要遍历字符串--通过下标进行访问int i = 0;while (arr[i] != '\0')//不等于'\0'就一直遍历数组,直到遇到\0就停止遍历数组{//判断这个数组内的字符是不是小写字母if (islower(arr[i]))//如果不是小写字母,返回值就是0,是小写字母返回值就是非0数字,//这个函数是用来判断输入的字符是不是小写字母{//小写字母转大写字母的方法://小写字母的ASCII-32=对应放入大写字母ASCIIarr[i] = arr[i] - 32;}i++;//没有遇到'\0'就i++}printf("%s", arr);return 0;
}//小写字母转大写字母出了这个toupper函数,还可以直接通过小写字母的ASCII-32就可以进行转换了//那么我们对上面的问题进行改造一下
int main()
{char arr[] = "I am a student";//末尾隐藏\0//需要遍历字符串--通过下标进行访问int i = 0;while (arr[i] != '\0')//不等于'\0'就一直遍历数组,直到遇到\0就停止遍历数组{//判断这个数组内的字符是不是小写字母if (islower(arr[i]))//如果不是小写字母,返回值就是0,是小写字母返回值就是非0数字,//这个函数是用来判断输入的字符是不是小写字母{//小写字母转大写字母的方法:arr[i] = toupper(arr[i]);//小写字母通过toupper这个函数转换为大写字母了}i++;//没有遇到'\0'就i++}printf("%s", arr);return 0;
}
//对于这个循环还有一种判断是不是小写字母的方法
//if(arr[i]>='a'&&arr[i]<='z')
//在这个区间内的就都是小写字母了
这些字符分类函数主要是进行判断
2.字符转换函数
c语言提供两个字符转换函数
int tolower(int c);//将参数传进去的大写字母转小写
int toupper(int c);//将参数传进去的小写字母转大写
//int main()
//{
// char ch = toupper('a');
// printf("%c\n", ch);//打印出来的就是A,大写的A
//
// //如果传进来的大写字母,那么输出的还是大写字母,不做判断
// ch = tolower('A');
// printf("%c\n", ch);//将大写字母转换为小写字母
//
//
// return 0;
//}
//小写字母转大写字母出了这个toupper函数,还可以直接通过小写字母的ASCII-32就可以进行转换了//那么我们对上面的问题进行改造一下
int main()
{char arr[] = "I am a student";//末尾隐藏\0//需要遍历字符串--通过下标进行访问int i = 0;while (arr[i] != '\0')//不等于'\0'就一直遍历数组,直到遇到\0就停止遍历数组{//判断这个数组内的字符是不是小写字母if (islower(arr[i]))//如果不是小写字母,返回值就是0,是小写字母返回值就是非0数字,//这个函数是用来判断输入的字符是不是小写字母{//小写字母转大写字母的方法:arr[i] = toupper(arr[i]);//小写字母通过toupper这个函数转换为大写字母了}i++;//没有遇到'\0'就i++}printf("%s", arr);return 0;
}
//对于这个循环还有一种判断是不是小写字母的方法
//if(arr[i]>='a'&&arr[i]<='z')
//在这个区间内的就都是小写字母了
3.strlen的使用和模拟实现
strlen格式:size_t strlen(const char * str);
统计的是\0之前的个数
strlen的返回值是size_t类型的
//
//int main()
//{
// char arr1[] = "abcdef";
// size_t len=strlen(arr1);//输出的数据是6
// printf("%zd\n", len);
//
//
// char arr2[] = { 'a','b','c','d','e','f' };//这里面是没有\0的
// size_t len1 = strlen(arr2);
// printf("%zd\n", len1);//输出数据是17
// return 0;
//}
//注意函数的返回值是sizeof_t,是无符号的int main()
{if (strlen("abc") - strlen("abcdef") > 0)
//另一种写法:if(strlen("abc")>strlen("abcdef")){// size_t类型 size_t类型printf(">\n");}else{printf("<=\n");}return 0;
}
//输出的结果是>,为什么呢?
//当两个无符号整型进行计算的时候,算出来的结果还是无符号整型的结果
//如果想得到-3的话,那么我们需要将strlen("abc")和strlen("abcdef")强制类型转换为int 类型的数据
strlen的,模拟实现的三种方法
1.计数器的方法
2.指针~指针
size_t my_strlen(const char*str)//返回类型是sizeof_t,因为传过来的是字符串首元素的地址,那么我们用一个字符指针进行接收
{//前面加上const防止被修改//利用传过来的首元素的地址,我们遍历数组,统计\0之前的元素个数//只要不是\0就统计一个数字int count = 0;//因为str是指针变量,为了防止str是空指针,我们要进行断言一下assert(str != NULL);//如果str为空指针就报错while (*str != '\0'){count++;str++;//使指针++,向后走一步}return count;}
int main()
{char arr[] = "abcdef";size_t len = my_strlen(arr);//自己创建一个函数来实现strlen函数//传过去一个数组名,字符串首元素的地址printf("%zd\n", len);return 0;
}
//第二种方法:指针-指针
//两个指针相减就能得到两个指针之间的元素个数了
size_t my_strlen(const char* str)
{char* start = str;assert(str != NULL);while (*str != '\0'){str++;//走到'\0'前面就停止了,那么到最后str的值是最后一个元素的地址}return str - start;//因为一开始将第一个元素的地址赋值给start了,那么现在指针相减,得到的就是两个指针之间的元素的个数了}int main()
{char arr[] = "abcdef";size_t len = my_strlen(arr);//自己创建一个函数来实现strlen函数//传过去一个数组名,字符串首元素的地址printf("%zd\n", len);return 0;
}
//这种写法可以不创建临时变量//使用递归来实现
size_t my_strlen(const char* str)
{if (*str != '\0'){ return 1 + my_strlen(str + 1);}else{return 0;}
}int main()
{char arr[] = "abcdef";size_t len = my_strlen(arr);//自己创建一个函数来实现strlen函数//传过去一个数组名,字符串首元素的地址printf("%zd\n", len);return 0;
}
4.strcpy的使用和模拟实现
功能:拷贝字符串
strcpy---cpoy string
strcpy在拷贝的过程中会将arr1里面的内容包括\0拷贝到arr2里面去
int main()
{char arr1[] = "hello world";char arr2[20] = { 0 };//现在想把arr1里面的hello world放到arr2里面去//我们可以用strcpy来实现strcpy(arr2, arr1);printf("%s\n", arr2);return 0;
}
注意注意:
被拷贝的字符串一定要包含'\0',保证strcpy遇到\0就能停止拷贝
1.源头的字符串中必须包含\0,没有\0,strcpy是不能结束的
2.目标空间必须足够大,以确保能存放原字符串
3.目标空间必须是可以修改的
int main()
{char arr1[] = "hello world";char* p = "xxxxxxxxxxxxxx";//常量字符串--不能修改strcpy(p, arr1);//会报错的return 0;
}
//模拟实现拷贝
char*my_strcpy(char*dest,const char*src)//传过来的是首元素的地址,返回值是目标空间的起始地址,所以就是char*
{//*dest指向的是arr2第// 一个元素,*src指向的是arr1第一个元素//保证这两个指针不为空指针/*assert(src != NULL);assert(dest != NULL);*///另一种写法:assert(dest && src);//如果dest为空指针,那么括号内就为0,就是假的,就会报错char* ret = dest;//将dest存起来while (*src != '\0'){*dest=*src;//进行加加操作,换下一个字符和下一个位置进行交换src++;dest++;}//当这个循环结束的时候,\0还没有被拷贝进去//最后的时候,src已经++成为\0了,那么现在再次赋值就能将\0拷贝进去了*dest =* src;//这里处理的就是\0return ret;//直接返回目标空间的起始地址}
int main()
{char arr1[] = "abcdef";//末尾隐藏\0char arr2[20] = { 0 };//1.直接打印arr2my_strcpy(arr2, arr1);printf("%s\n", arr2);//2.接收返回值char* p = my_strcpy(arr2, arr1);printf("%s\n", p);//直接将返回值放到打印里面也可以printf("%s\n", my_strcpy(arr2, arr1));return 0;
}//这个函数返回的是char*,为的是实现链式访问//strcpy函数返回的是字符串拷贝成功过后,目标空间的起始地址,
//返回值就是arr2的首元素的地址//总结:
//将arr1拷贝到arr2后,我们可以通过三种方法直接打印arr2
//一种就是直接打印arr2
//还有一种就是根据这个拷贝函数的返回值进行打印
//返回值是一个地址,在函数的一开始我们就将目标函数赋值给另一个临时指针,那么这个临时指针就指向了arr2
//在拷贝过后,我们直接将这个临时指针返回,所以在函数的开头我们用char*
//在返回了临时指针后,我们在主函数就用再创建一个临时指针变量进行返回值的接受,
//因为这个临时指针变量指向的是arr2的其实元素,那么我们就可以用这个临时指针变量直接打印arr2
//有了字符串起始元素的地址,我们就能打印这个字符串了
//对函数部分进行改进,
//思考:能不能将拷贝\0和前面的字符串放在一起呢?char*my_strcpy(char*dest,const char*src)
{assert(dest && src);char* ret = dest;while (*dest++ = *src++)//因为这里是后置++,所以先带进去数据进行解引用,再进行++{//arr1里面的字符通过*dest++ = *src++这个代码一个个拷贝到arr2里面去了//在最后,*src是\0拷贝过去了,然后因为while循环里面是\0,所以循环停止了// 但是\0拷贝到dest里面了 ;}//这个循环拷贝过去之后判断表达式的值//因为是后置++,所以延后产生return ret;}int main()
{char arr1[] = "abcdef";char arr2[20] = { 0 };my_strcpy(arr2, arr1);printf("%s\n", arr2);char* p = my_strcpy(arr2, arr1);printf("%s\n", p);printf("%s\n", my_strcpy(arr2, arr1));return 0;
}
5.strcat的使用和模拟实现
原字符串必须有\0结尾
目标字符串也得有\0,否则没办法知道从哪里开始
目标空间必须足够大,能容纳下字符串的内容
目标空间是可以进行修改的,而不是常量不能进行修改
int main()
{char arr1[20] = "hello ";char arr2[] = "world";//如何将arr2里面的字符串追加在arr1后面呢?strcat(arr1, arr2);//字符串追加printf("%s\n", arr1);return 0;
}//这个函数是如何实现追加呢?
//先找到目标函数的末尾\0的位置
//再将原字符串拷贝过来//要求目标字符串末尾有\0,因为后面要将原字符串拷贝过去,所以原字符串也要有\0
//要返回目标空间的起始地址
//目标空间是可修改的,但是源头需要进行限制,不能被修改
char* my_strcat(char* dest,const char* src)//返回类型为char*类型的
//dest指向的是arr1的首元素的地址
//src指向的是arr2首元素的地址
{char* ret = dest;//将起始位置存起来assert(dest && src);//进行断言,防止空指针//1.找目标空间的\0while (*dest != '\0'){dest++;}//while循环找到\0就停下来//2.拷贝----我们在字符串追加的时候,我们要将目标字符串末尾的\0覆盖掉while (*dest++ = *src++)//这个代码是进行字符串间的拷贝的,因为上面已经找到\0了,已经停止循环了,并且dest指向了arr1末尾的\0{;//写个分号就行了,循环得有一个循环语句}return ret;//直接返回arr1的起始地址
}
int main()
{char arr1[20] = "hello ";char arr2[] = "world";//如何将arr2里面的字符串追加在arr1后面呢?my_strcat(arr1, arr2);//字符串追加printf("%s\n", arr1);return 0;
}//这个函数是如何实现追加呢?
//先找到目标函数的末尾\0的位置
//再将原字符串拷贝过来//要求目标字符串末尾有\0,因为后面要将原字符串拷贝过去,所以原字符串也要有\0//总结: 我们应该先找到目标字符串末尾\0的位置,这样我们才好追加,
//切记:追加的时候我们要将目标字符串末尾的\0覆盖掉//在这个追加函数我们用了两个循环,第一个循环是找到\0,第二个循环是将原字符串拷贝到目标字符串的后面去
我们是不能对一个数组自己进行追加的
6.strcmp的使用和模拟实现
strcmp是用来比较两个字符串的
返回值是int
用返回值来比较这两个字符串大小
比较的是对应位置上的字符,如果对应位置字符相等就比较下一对字符
比较的不是字符串的长度,而是对应位置上字符的大小
如果两个字符串相等,返回值就是0
如果前面的字符串大于后面的字符串,那么就会返回一个大于0的数字
如果是后面的字符串大于前面的字符,前面的字符小,就返回一个小于 0的数字
int main()
{char arr1[] = "abcdef";char arr2[] = "abq";int ret=strcmp(arr1, arr2);//printf("%d\n", ret);//输出的结果是-1,就说明arr1<arr2if (ret == 1)//仅仅在vs平台可以写返回值为1,其他的平台就不知道返回值是多少了{printf(">" );}else{printf("<=");}return 0;
}
//
//int main()
//{
// char arr1[] = "abcdef";
// char arr2[] = "abq";
// int ret=strcmp(arr1, arr2);
// //printf("%d\n", ret);//输出的结果是-1,就说明arr1<arr2
// if (ret == 1)//仅仅在vs平台可以写返回值为1,其他的平台就不知道返回值是多少了
// {
// printf(">" );
// }
// else
// {
// printf("<=");
// }
// return 0;
//}//int my_strcmp(const char*str1,const char*str2)//返回值是Int,并且加上const进行限制
//{
// //我们一对一对字符进行比较
// assert(str1 && str2);
// while (*str1 == *str2)
// {
// if (*str1 == '\0')//如果str1在上一轮循环++到\0之后,就说明这两个字符串已经完全相等了
// {
// return 0;//两个字符串完全相等
// }
// str1++;
// str2++;
// }//两个字符相等我们就找下一对字符进行比较
// if (*str1 > *str2)
// {
// return 1;
// }
// else//*str1 < *str2
// {
// return -1;
// }
//}//另一种写法
int my_strcmp(const char* str1, const char* str2)//返回值是Int,并且加上const进行限制
{//我们一对一对字符进行比较assert(str1 && str2);while (*str1 == *str2){if (*str1 == '\0')//如果str1在上一轮循环++到\0之后,就说明这两个字符串已经完全相等了{return 0;//两个字符串完全相等}str1++;str2++;}//两个字符相等我们就找下一对字符进行比较return (*str1 - *str2);//直接返回他们相减的值}
int main()
{char arr1[] = "abcdef";char arr2[] = "abcdef";int ret=my_strcmp(arr1, arr2);printf("%d\n", ret);return 0;
}
7.strncpy函数的使用
int main()
{char arr1[20] = "abcdef";char arr2[20] = { 0 };strncpy(arr2, arr1, 3);printf("%s", arr2);//打印的结果就是abc,只选择arr1里面的前三位字符进行拷贝return 0;
}int main()
{char arr1[20] = "abcdef";char arr2[20] = "xxxxxxxx";strncpy(arr1, arr2, 3);printf("%s", arr1);//输出xxxdefreturn 0;
}
拷贝n个字符从原字符串到目标空间
如果原字符串的长度小于n的话,则拷贝完原字符串之后,在目标的后边追加0,直到凑齐n个
8.strncat函数的使用
可以用来给自己追加
在原有的基础上,可以选择性的追加n个字符
9.strncmp函数的使用
int main()
{char arr1[20] = "abcdef";char arr2[20] = "abc";int ret=strncmp(arr1, arr2, 3);//比较三个字符printf("%d", ret);//现在返回值就是0return 0;
}
指定几个字符进行比较久进行几个字符比较
总之来说还是比较方便的
10.strstr的使用和模拟实现
在一个字符串里面找子字符串,在一个字符串里面找另外一个字符串是否出现
如果要找的字符串出现一次以上,那么我们就返回第一次出现的位置
在str1中找str2
如果在str1中没有找到str2的话,就会返回一个空指针NULL
int main()
{char arr[] = "abcdefabcdef";char* p = "efab";//定义了一个指向字符串常量"efab"的指针pchar* ret=strstr(arr, p);//返回的是e的地址//printf("%s", ret);//打印结果是efabcdefif (ret == NULL){printf("不存在\n");}else{printf("%s\n", ret);}return 0;
}
char* my_strstr(const char* str1, const char* str2)//我们只是希望在str1中查找str2,并不希望将这两个字符串修改了,所以要加上const
{assert(str1 && str2);//保证两个指针不是空指针const char* s1 = NULL;//加上const限制住const char* s2 = NULL;const char* cur = str1;//一开始指向的是str1的起始位置的//*cur != '\0'简化如下:当*cur是\0我们就进不去循环了if (*str2 == '\0')//特殊情况,假如str2是空指针,那么我们直接返回str1{return str1;}while (*cur)//如果等于'\0'的话就说明这个字符串已经找完了{//只要*cur不是\0就能一直寻找要找的字符串//分别将起始位置赋值给s1和s2s1 = cur;s2 = str2; //*s1!='\0'&& *s2!= '\0'简化如下,效果还是一样的//就是反正你*s1和*s2是\0这个循环就进不去,直接跳出来了while(*s1&& *s2&& * s1 == *s2)//判断两个指针指向位置的字符是否相等{//如果这一对字符相等的情况下我们就往后走判断下一对字符s1++;s2++;}if (*s2 == '\0')//说明我们已经在str1里面已经找完了字符串{return cur;//那么我们就直接返回str1中我们记录的cur的位置}cur++;//如果*s1!=*s2的话,就cur++换下一个字符,就跳出这个while循环了//再次循环就s1又被重新赋值了,但是s2仍然是被srt2赋值,//就是相等与我们在仅仅只是将str1的出发点进行了更换,但是str2的还没变//直到能在str1里面找到str2了,就是str2语言\0了,就说明已经在str1里面找到str2了//如果*s1不为\0,但是*s2已经是\0了,那么这个while循环我们就跳出来了}return NULL;//如果cur为\0就是我们已经不可能在str1中找到str2了,那我们直接返回空指针}
int main()
{char arr[] = "abcdefabcdef";char* p = "bbs";const char* ret=my_strstr(arr, p);if (ret == NULL)//根据返回值进行判断str1中是否存在str2{printf("不存在\n");}else{printf("%s\n", ret);}return 0;
}
//如果在str1里面提前遇到了\0就说明这个字符串已经找完了还没遇到要找的字符串
//但是str2提前遇到\0的话,就说明我们已经找到了要找的字符串了//总结:
/*
我们在这个模拟函数中,我们最重要的就是创建了一个cur来不断重新定义来找的位置假如在第一次寻找的过程中,我们没找到,那么cur就进行++操作,然后s1=str2重新赋值,
就是我们将要找的字符串的指针重新定义在首元素,但是cur一直在往后走,
直到s2走到\0,就是说明我们已经在str1内找到str2了那么如果找到了的话,我们就将cur现在的地址return 回去,就是说明我们在str1中cur处可以找到str2了*/
我们当前写的strstr函数的实现不是最优的算法
KMP算法---字符串中找字符串---效率更高,但是实现更难
11.strtok函数的使用
charstrtok(charstr,const char *sep)
1.sep参数指向一个字符串,定义了用作分隔符的字符集合
2.第一个参数指定了一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记
3.strtok函数找到str中的下一个标记,并将用\0结尾,返回一个指向这个标记的指针。(注意:strtok会改变被操作符的字符串,所以被strtok函数切分的字符串一般都是临时拷贝的内容并且可以修改)
4.strtok函数的第一个参数不为NULL,函数将找到str中的第一个标记,就是第一个分隔符,strtok函数将保存他在字符串中的位置
5.strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置,查找下一个标记。
6.如果字符串中不存在更多的标记,则返回NULL指针,就是说明这个字符串已经被找完了,再没有任何的分隔符了,已经尽数转化为\0了
//int main()
//{
// char arr[] = "abdjskgb@vsfkv.net";
// char buf[256] = { 0 };
// strcpy(buf, arr);//把arr数据拷贝到buf内//abdjskgb\0vsfkv\0net
// char sep[] = "@.";//char*sep= "@."
// char* ret=strtok(buf, sep);
// printf("%s\n", ret);//输出结果就是abdjskgb
//
// char* ret1 = strtok(NULL, sep);
// printf("%s\n", ret1);//输出结果就是vsfkv
//
//
// char* ret2 = strtok(NULL, sep);
// printf("%s\n", ret2);//输出结果是net
//
//
// return 0;
//}
//strtok的返回值是buf的第一个标记的指针
//当这个函数返回的是一个空指针的时候,就说明这个函数已经找完了//上面这种写法必然是错误的,我们必须先知道提供的字符串需要切割几段int main()
{char arr[] = "abdjskgb@vsfkv.net";char buf[256] = { 0 };strcpy(buf, arr);//把arr数据拷贝到buf内//abdjskgb\0vsfkv\0netchar *sep = "@.";//分割符char* ret = NULL;//上个代码的buf只在strtok里面进行一次,后面都是空指针//我们利用了for循环的特点,初始化只执行一次//就是说只有第一次传的是buf,后面传的都是NULLfor (ret = strtok(buf, sep); ret != NULL;ret=strtok(NULL,sep)){printf("%s\n", ret);}//只要ret不等于NULL这个循环就一直执行//strtok(buf, sep)的返回值是第一个切割符前面的字符串的地址,并将其赋值给ret,//每次循环都会运行ret=strtok(NULL,sep),将新获得的返回值赋值给ret,然后每次循环就从新位置开始return 0;
}//总结:我们定义一个数组arr,里面带有分隔符
// 再定义一个空数组,将带有分隔符的数组拷贝过来,在后面的过程,我们都是用这个拷贝的数组
//
// char* ret = NULL;:定义了一个指向分割后子字符串的指针。
//char *sep = "@."; 定义了分隔符字符串,包含 @ 和 .。/*针对这个循环进行更加详细的解释ret = strtok(buf, sep)是初始化部分,
在循环开始之前,strtok被调用,使用buf和sep来分割字符串,并返回第一个子字符串的指针
这个指针被赋值给ret,作为循环的起始点循环条件:ret != NULL 这表示只要strtok返回的指针不是NULL,就能继续执行循环体,
因为strtok在没有更多子字符串可供分割时会返回NULL,所以在没有更多子字符串可供分割时会结束迭代部分:ret = strtok(NULL, sep)
在每次循环迭代时,strtok(NULL, sep)被调用,告知strtok继续从上一次的位置继续分割字符串
并返回下一个子字符串的指针,这个指针被赋值给ret,作为下一次循环的起始点这个循环的条件保证了每次循环迭代都能够正确地从输入字符串中分割出一个子字符串,
并且在没有更多子字符串可供分割时结束循环。*///strtok可以把一个字符串切成一段一段的,每切一次就将起始地址返回去//每次就直接将分隔符前面的字符串切割下来,并将切割符变为\0,就是\0后面的字符将不进行访问//函数会找到第一个分隔符,并记住位置,下次找就从这个位置开始找
12.strerror函数的使用
strerror可以将错误对应的错误信息字符的地址返回
strerror 函数可以把参数部分错误码对应的错误信息的字符串地址返回来。
在不同的系统和C语⾔标准库的实现中都规定了⼀些错误码,⼀般是放在 errno.h 这个头⽂件中说明的,C语⾔程序启动的时候就会使⽤⼀个全局的变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表⽰没有错误,当我们在使⽤标准库中的函数的时候发⽣了某种错误,就会将对应的错误码,存放在errno中,⽽⼀个错误码的数字是整数很难理解是什么意思,所以每⼀个错误码都是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。
//int main()
//{
// for (int i = 0; i < 10; i++)
// {
// printf("%d:%s\n", i,strerror(i));
// }
// return 0;
//}//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 descriptorint main()
{//打开文件FILE*pf=fopen("date.txt", "r");//r---读,以读文件的形式打开文件,如果文件不存在就是打开失败//打开失败就返回一个空指针if (pf == NULL)//打开失败{printf("打开文件失败,原因是%s",strerror(errno));//为了使用errno我们要包含头文件#include <errno.h>return 1;//打开失败我们就直接结束进程}else{printf("打开成功\n");fclose(pf);//关闭文件pf = NULL;}return 0;
}
//打开文件失败,原因是No such file or directory
strerror---将错误码对应的错误信息的字符串的起始地址返回
int main()
{//打开文件FILE*pf=fopen("date.txt", "r");//r---读,以读文件的形式打开文件,如果文件不存在就是打开失败//打开失败就返回一个空指针if (pf == NULL)//打开失败{printf("打开文件失败,原因是:%s\n",strerror(errno));//为了使用errno我们要包含头文件#include <errno.h>perror("打开文件失败,原因是");return 1;//打开失败我们就直接结束进程}else{printf("打开成功
perror--将errno中错误信息直接打印出来
perror函数线打印str指向的字符串,再打印冒号,再打印空格,再打印错误码对应的信息
相关文章:

C语言----字符函数和字符串函数
在编程的过程中,我们要经常处理字符和字符串,为了方便操作字符和字符串,c语言标准库中提供的一系列库函数,接下来我们就开始学习与认识他们 1.字符分类函数 c语言中有一系列的函数是专门做字符分类的,也就是一个字符…...

神经网络 torch.nn---Convolution Layers
torch.nn — PyTorch 2.3 documentation torch.nn - PyTorch中文文档 (pytorch-cn.readthedocs.io) torch.nn和torch.nn.functional的区别 torch.nn是对torch.nn.functional的一个封装,让使用torch.nn.functional里面的包的时候更加方便 torch.nn包含了torch.nn.…...

Linux常用基本命令-操作
目录 一、shell 1、什么是shell 二、Linux基本的命令分类 1、内部命令和外部命令 2、查看内部命令 2.1、help命令 2.2、enable 命令 2.3、type命令 2.4、whereis命令 2.5、which 命令 2.6、hash缓存 编辑 三、Linux常用命令 1、Linux命令格式 2、编辑Linux命…...

从零开始使用 Elasticsearch(8.14.0)搭建全文搜索引擎
Elasticsearch 是目前最常用的全文搜索引擎。它可以快速地存储、搜索和分析海量数据,广泛应用于维基百科、Stack Overflow、Github 等网站。 Elasticsearch 的底层是开源库 Lucene。直接使用 Lucene 需要写大量代码,而 Elasticsearch 对其进行了封装&am…...

流程与IT双驱动:锐捷网络如何构建持续领先的服务竞争力?
AI大模型及相关应用进入“竞赛时代”,算力作为关键要素备受关注,由于算力行业对网络设备和性能有较大需求,其发展也在推动ICT解决方案提供商加速升级,提升服务响应速度和服务质量。 锐捷网络是行业领先的ICT基础设施及行业解决方…...

CopyOnWriteArrayList 详细讲解以及示范
CopyOnWriteArrayList是Java集合框架中的一种线程安全的列表实现,特别适用于读多写少的并发场景。 它是通过“写时复制”(Copy-On-Write)策略来保证线程安全的,这意味着当有线程尝试修改列表时,它会先复制原列表到一个…...

01-Java和Android环境配置
appium是做app自动化测试最火的一个框架,它的主要优势是支持android和ios,同时也支持Java和Python脚本语言。而学习appium最大的难处在于环境的安装配置,本文主要介绍Java和Android环境配置,在后续文章中将会介绍appium的安装和具…...

【qt】视口和窗口坐标
视口和窗口坐标 一.视口和窗口坐标的原理二.视口和窗口坐标的好处三.演示好处四.总结 一.视口和窗口坐标的原理 在绘图事件中进行绘图 void Widget::paintEvent(QPaintEvent *event) {QPainter painter(this);QRect rect(200,0,200,200);painter.drawRect(rect);//设置视口的…...

优化SQL查询的策略和技巧 - AI提供
优化SQL查询以提高处理大型数据集的数据库性能是一个重要课题。 以下是一些关键策略和技巧,可以帮助您提升查询效率: 1、创建合适索引: 针对频繁出现在WHERE、JOIN、ORDER BY和GROUP BY子句中的列创建索引。索引能够显著加速数据检索过程。…...

平安科技智能运维案例
平安科技智能运维案例 在信息技术迅速发展的背景下,平安科技面临着运维规模庞大、内容复杂和交付要求高等挑战。通过探索智能运维,平安科技建立了集中配置管理、完善的运营管理体系和全生命周期运维平台,实施了全链路监控,显著提…...

基于深度学习的向量图预测
基于深度学习的向量图预测 向量图预测(Vector Graphics Prediction)是计算机视觉和图形学中的一个新兴任务,旨在从像素图像(栅格图像)生成相应的向量图像。向量图像由几何图形(如线条、曲线、多边形等&…...

鸿蒙HarmonyOS $r(““)与$rawfile(““)的区别
在鸿蒙(HarmonyOS)开发中,$r(“”) 和 $rawfile(“”) 是两种不同的资源引用方式,它们分别用于引用不同的资源类型。 1、$r(“”) $r 函数通常用于引用字符串、颜色、尺寸、样式等定义在资源文件(如 strings.json, c…...

简单了解java中的Collection集合
集合 1、Collection-了解 1.1、集合概述 集合就是一种能够存储多个数据的容器,常见的容器有集合和数组 那么集合和数组有什么区别嘞? 1、集合长度可变,数组的长度不可变 2、集合只能存储引用数据类型(如果要存储基本数据类型…...

java 实现导出word 自定义word 使用aspose教程包含图片 for 循环 自定义参数等功能
java 实现导出word 主要有一下几个知识点 1,aspose导入 jar包 和 java编写基础代码下载使用 aspose-words jar包导入 aspose jar 包 使用 maven导入java代码编写 2,if判断 是否显示2,显示指定值3,循环显示List 集合列表 使用 fore…...

CSS动画(炫酷表单)
1.整体效果 https://mmbiz.qpic.cn/sz_mmbiz_gif/EGZdlrTDJa6yORMSqiaEKgpwibBgfcTQZNV0pI3M8t8HQm5XliaicSO42eBiboEUC3jxQOL1bRe0xlsd8bv04xXoKwg/640?wx_fmtgif&fromappmsg&wxfrom13 表单,也需要具有吸引力和实用性。HTML源码酷炫表单不仅能够提供给用户…...

Stream
Stream 也叫Stream流,是Jdk8开始新增的一套API (java.util.stream.*),可以用于操作集合或者数组的数据。 优势: Stream流大量的结合了Lambda的语法风格来编程,提供了一种更加强大,更加简单的方式操作集合或者数组中的…...

鸿蒙轻内核A核源码分析系列五 虚实映射(5)虚实映射解除
虚实映射解除函数LOS_ArchMmuUnmap解除进程空间虚拟地址区间与物理地址区间的映射关系,其中参数包含MMU结构体、解除映射的虚拟地址和解除映射的数量count,数量的单位是内存页数。 ⑴处函数OsGetPte1用于获取指定虚拟地址对应的L1页表项数据。⑵处计算需要解除的无效…...

编程初学者用什么软件电脑:全方位指南及深度解析
编程初学者用什么软件电脑:全方位指南及深度解析 在数字化浪潮席卷而来的今天,编程技能逐渐成为了一项必备的基本素养。对于初学者来说,选择一款合适的编程软件电脑至关重要。本文将从四个方面、五个方面、六个方面和七个方面,深…...

代理IP池功能组件
1.IP池管理器:用于管理IP池,包括IP地址的添加、删除、查询和更新等操作。 2.代理IP获取器:用于从外部资源中获取代理IP,例如从公开代理IP网站上爬取代理IP、从代理服务商订购代理IP等。 3.IP质量检测器:用于检测代理…...

Sqlite3入门和c/c++下使用
1. SQLite3基本介绍 1.1 数据库的数据类型 1.2 基本语法 1. 创建数据表格 create table 表名(字段名 数据类型, 字段名 数据类型); create table student(id int, name varchar(256), address text, QQ char(32)); 2. 插入数据 insert into 表名 valu…...

pyinstaller打包exe多种失败原因解决方法
pyinstaller打包exe多种失败原因解决方法 目录 pyinstaller打包exe多种失败原因解决方法1、pyinstaller安装有问题1.1 安装pyinstaller1.2 采用anconda的环境启动 2、pyqt5与pyside6冲突2.1 打包生成.spec文件2.2 编辑spec文件 3、打包成功后打不开exe,exe闪退3.1 s…...

x64-linux下在vscode使用vcpkg
1.使用vscode远程连接上对应的linux ,或者直接在图形化界面上使用。 2.安装vcpkg 插件,然后打开插件设置。 注意:defalut和host的主机一定和你自己的主机一致,且必须符合vcpkg三元组格式,其中你可以选择工作台的设置&a…...

运营商二要素核验-手机号机主姓名核验接口-运营商二要素核验接口
通过电信运营商验证手机号码与姓名是否一致。广泛用于实名注册、风控审核等场景,如电商、游戏、直播、金融等需要用户实名认证的场景。支持携号转网核验。 更新周期:联通T1 电信T3 移动T3~5 均为工作日 接口地址: https://www.wapi.cn/api_de…...

C++设计模式-生产者消费者模式
运行在VS2022,x86,Debug下。 32. 生产者消费者模式 解耦生产者和消费者之间的关系,即生产者和消费者只依赖缓冲区,而不相互依赖。应用:多线程并发编程,可以解决生产者和消费者之间的同步问题。实现 生产者…...

VSTO Word.net 如何在另外的工程内添加CustomTaskPane
其他工程肯定是不能直接添加CustomTaskPane面板的,但我们可以在ThisAddIn 中先把对应的panel给新建出来再进行隐藏。步骤如下: 1.在另外工程中定义public static CustomTaskPane currMainForm;把需要隐藏的界面赋值给currMainForm; 2.在另外…...

ROS——自定义话题消息和使用方法
定义Person话题 定义Person发布者 /*** 该例程将发布/person_info话题,自定义消息类型: test_topic::Person*/#include <ros/ros.h> #include <test_topic/Person.h> //包含的头文件,ros相关的头文件,及自定义头文件…...

包装对象类型又是啥啊。。。
包装对象类型 目录 包装对象类型 目录包装对象的概念包装对象类型和字面量类型Objectobject 包装对象的概念 JavaScript 的8种类型之中,undefined和null其实是两个特殊值,object属于复合类型,剩下的五种属于原始类型(primiti…...

服务编排如何选?这几款可视化服务编排引擎,开发团队赶紧收藏
最近看到几款不错的服务编排的产品,先给大家上图看看 扣子: jvs-logic: node-red: jvs-rules: 上述几个产品是最近看到的 几个比较有特点的服务编排的系统。 接下来我对API详细分解下,说明下优点与挑战 服…...

web前端语言框架:探索现代前端开发的核心架构
web前端语言框架:探索现代前端开发的核心架构 在快速发展的web开发领域,前端语言框架的选择对于项目的成功至关重要。它们不仅影响着开发效率,更直接关系到用户体验与网站性能。本文将从四个方面、五个方面、六个方面和七个方面,…...

基于flask的网站如何使用https加密通信
文章目录 内容简介网站目录示例生成SSL证书单独使用Flask使用WSGI服务器Nginx反向代理参考资料 内容简介 HTTPS 是一种至关重要的网络安全协议,它通过在 HTTP 协议之上添加 SSL/TLS 层来确保数据传输的安全性和完整性。这有助于防止数据在客户端和服务器之间传输时…...