本章涉及的函数包括但不限于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 );
功能:用来比较str1和str2指向的字符串,从两个字符串的第一个字符开始比较,如果两个字符的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
好了,以上就是字符串函数的一些内容,可能笔者还没讲全,也可能出现一些错误,欢迎同学们来指正。

1994

被折叠的 条评论
为什么被折叠?



