目录
1. 字符分类函数
1.1 小练习
1.1.1 方法一
2. 字符转换函数
2.1 小练习的方法二
3. strlen的使⽤和模拟实现
3.1 注意事项
3.1.1 注意事项2的练习
3.2 strlen函数的模拟实现
3.2.1 方法一
3.2.2 方法二(指针—指针)
3.2.3 方法三(递归方式)
3.2.3.1 注意事项
4. strcpy的使⽤和模拟实现
4.1 注意事项
4.2 使用举例
4.2.1 注意点!!!
4.3 stcpy函数的模拟实现
4.3.1 版本一(先拷贝\0之前的字符,再拷贝\0之后的字符)
4.3.2 版本二(拷贝\0前的字符包括\0)
4.3.3 版本三 (assert,防止死循环)
4.3.4 版本四 (dest指向的空间是需要改变的,但是src指向的空间是不被期望改变的)
4.3.5 版本五 (返回目标空间的起始地址)
5. strcat的使⽤和模拟实现
5.1 注意事项
5.2 使用举例
5.3 strcat函数的模拟实现
5.4 自己追加拼接自己可以吗?
6. strcmp的使⽤和模拟实现
6.1 使用举例
6.2 strcmp的模拟实现
7. strncpy函数的使⽤和模拟实现
7.1 使用举例
7.2 strncpy函数的模拟实现
8. strncat函数的使⽤和模拟实现
8.1 使用举例
8.2 strncat函数的模拟实现
9. strncmp函数的使⽤和模拟实现
9.1 使用举例
9.2 strncmp函数的模拟实现
10. strstr的使⽤和模拟实现
10.1 使用举例
10.2 strstr函数的模拟实现
10.2.1 模拟实现思路
10.2.2 模拟实现思路优化
10.2.3 代码实现
11. strtok函数的使⽤
11.1 用途介绍
11.2 使用举例
编辑
11.2.1 方法优化
12. strerror函数的使⽤
12.1 使用举例
12.2 相关函数perror的使用
C语⾔中有⼀系列的函数是专⻔做字符分类的,也就是⼀个字符是属于什么类型的字符的。
这些函数的使⽤都需要包含⼀个头⽂件是 ctype.h
这可以让我们更加方便的判断⼀个字符是属于什么类型的字符的
接下来,我们以islower举例,其他函数使用的规则一样
int islower ( int c );
islower是用来返回参数是否是小写字母的,如果是小写字母就返回非零的值,如果不是小写字母就返回零
#include
#include
int main()
{
int ret = islower('Q');
printf("%d\n", ret);
return 0;
}
接下来,让我们写一个代码,将字符串中的小写字母转大写,其他字符不变
思路:大小写字母相差32
#include
#include
#include
int main()
{
char arr[] = "i AM A Student.";
int i = 0;
int len = strlen(arr);
for (i = 0; i < len; i++)
{
if (islower(arr[i]))
{
arr[i] -= 32;
}
}
printf("%s", arr);
return 0;
}
但是,这种方法有点麻烦,让我们介绍完字符转换函数再来实现方法二
int tolower(int c);//将参数传进去的小写字母转大写
int toupper(int c);//将参数传进去的大写字母转小写
由此,我们可以得到方法二
#include
#include
#include
int main()
{
char arr[] = "i AM A Student.";
int i = 0;
int len = strlen(arr);
for (i = 0; i < len; i++)
{
if (islower(arr[i]))
{
arr[i]=toupper(arr[i]);
}
}
printf("%s", arr);
return 0;
}
size_t strlen ( const char * str );
注意事项:
1.strlen函数要正确获得字符串长度的话,字符串中必须要有\0
2.要注意strlen的返回值是size_t(字符串长度没有负数- - ->无符号整型)
观察以下代码,判断输出符号:
#include
#include
#include
int main()
{
if (strlen("abc") - strlen("abcdef") > 0)
printf(">");
else
printf("<=");
return 0;
}
可是,结果不是-3吗?为什么结果却是>呢?
返回类型觉得结果———>-3———>3
2个无符号类型相减得到的也是无符号整型
仿照strlen函数的参数,返回值,功能,写一个类似的函数
#include
#include
#include
size_t my_strlen(char* str)
{
int count = 0;//计数
while (*str != '\0')
{
count++;
str++;
}
return count;
}
int main()
{
char arr[] = "abcdef";
size_t ret = my_strlen(arr);
printf("%zd\n", ret);
return 0;
}
#include
#include
#include
#include
size_t my_strlen(const char* str)
{
const char* p = str;
assert(str != NULL);
while (*str != '\0')
str++;
return str - p;
}
int main()
{
char arr[] = "abcdef";
size_t ret = my_strlen(arr);
printf("%zd\n", ret);
return 0;
}
#include
#include
#include
#include
size_t my_strlen(const char* str)
{
if (*str == 0)
return 0;
else
return 1 + my_strlen(str + 1);
}
int main()
{
char arr[] = "abcdef";
size_t ret = my_strlen(arr);
printf("%zd\n", ret);
return 0;
}
可能会有人问str++可不可以?不行!!!
str++为什么不行?
后置++:先使用再++,str传址过去再++ ——>永远都传str的地址,str++或者++str导致str改变
str+1:str不变
但是,++str可以运行成功,但是str会改变,有隐患
char* strcpy(char * destination, const char * source );
注意事项:
1.原字符串必须包含\0;同时\0也会被拷贝到目标空间
2.要保证目标空间要足够大,能放得下拷贝来的数据
3.还要保证目标空间可修改
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include
int main()
{
char s1[] = "hello world";
char s2[20] = "xxxxxxxxxxxxx";
strcpy(s2, s1);
printf("%s\n", s2);
return 0;
}
按理来说,应该输出:hello worldxx
调试可以看到:拷贝的时候会将原字符串中的\0也拷进去,\0也拷贝过来后拷贝就结束了
常量字符串---不可以修改
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
void my_strcpy(char* dest, char* src)
{
while (*src != 0)
{
*dest = *src;
dest++;
src++;
}
//拷贝\0
*dest = *src;
}
int main()
{
char s1[] = "hello world";
char s2[20] = { 0 };
my_strcpy(s2, s1);
printf("%s\n", s2);
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
char* my_strcpy(char* dest, char* src)
{
char* ret = dest;
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char s1[] = "hello world";
char s2[20] = { 0 };
printf("%s\n", my_strcpy(s2, s1));
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include
char* my_strcpy(char* dest, char* src)
{
assert(dest);
assert(src);
char* ret = dest;
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char s1[] = "hello world";
char s2[20] = { 0 };
printf("%s\n", my_strcpy(s2, s1));
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include
char* my_strcpy(char* dest, const char* src)
{
assert(dest);
assert(src);
char* ret = dest;
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char s1[] = "hello world";
char s2[20] = { 0 };
printf("%s\n", my_strcpy(s2, s1));
return 0;
}
strcpy的功能是将原字符串的内容拷贝到目标空间,希望目标空间的内容发生改变
所以返回目标空间的起始地址,方便观察目标空间的内容
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include
char* my_strcpy(char* dest, const char* src)
{
assert(dest);
assert(src);
char* ret = dest;
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char s1[] = "hello world";
char s2[20] = { 0 };
printf("%s\n", my_strcpy(s2, s1));
return 0;
}
char *my_strcat(char *dest, const char*src)
注意事项:
1.从末尾追加拼接,末尾是\0,拼接结束末尾也是\0——>源头字符串要有\0(从哪里开始);目标空间中要有\0(到哪里结束)
2.目标空间要足够大,目标要可修改
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include
int main()
{
char s[20] = "hello ";
const char* p = "world";
strcat(s, p);
printf("%s\n", s);
return 0;
}
模拟实现步骤:
1.找到目标空间\0
2.拷贝数据——strcpy函数
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include
char* my_strcat(char* dest,const char* src)
{
char* ret = dest;
while (*dest != '\0')
{
dest++;
}
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char s[20] = "hello ";
const char* p = "world";
printf("%s\n", my_strcat(s, p));
return 0;
}
不可以❌
不行❌会自己把\0不断覆盖掉——>越界访问
比较2个字符串的大小
比较两个字符串中对应位置上的字符按照字典顺序比较
• 标准规定:
◦ 第⼀个字符串⼤于第⼆个字符串,则返回⼤于0的数字
◦ 第⼀个字符串等于第⼆个字符串,则返回0
◦ 第⼀个字符串⼩于第⼆个字符串,则返回⼩于0的数字
◦ 那么如何判断两个字符串? ⽐较两个字符串中对应位置上字符ASCII码值的⼤⼩。
#include
#include
#include
#include
int main()
{
char s1[] = "abcdef";
char s2[] = "abcq";
int ret=strcmp(s1, s2);
printf("%d\n", ret);
return 0;
}
#include
#include
#include
int my_strcmp(const char* str1, const char* str2)//只是比较大小,不用修改值
{
assert(str1 != NULL);//确保指针有效性
assert(str2 != NULL);
while (*str1 == *str2)
{
if (*str1 == '\0')//只需要比较其中一个是否是\0,成立则都是\0
return 0;
str1++;
str2++;
if (*str1 > *str2)
return 1;
else
return 1;
}
}
int main()
{
int ret = my_strcmp("abcdef", "abcdg");
printf("%d\n", ret);
return 0;
}
char * strncpy ( char * destination, const char * source, size_t num );
• 拷⻉num个字符从源字符串到⽬标空间。
• 如果源字符串的⻓度⼩于num,则拷⻉完源字符串之后,在⽬标的后边追加0,直到num个。
#include
#include
int main()
{
char arr1[10] = "xxxxxxxxxx";
char arr2[] = "ab";
strncpy(arr1, arr2, 5);
printf("%s\n", arr1);
return 0;
}
我们可以通过调试看见,多余的位置用\0来凑
提供参数大于传输全部个数,则全部传输,并且无论长短传输\0不变
#include
#include
int main()
{
char s1[20] = "abc\0xxxxxxxx";
char s2[] = "def";
strncat(s1, s2,5);
printf("%s\n", s1);
return 0;
}
int strncmp ( const char * str1, const char * str2, size_t num );
#include
#include
int main()
{
char s1[15] = "abcdef";
char s2[] = "abcg";
int ret = strncmp(s1, s2, 3);
printf("%d\n", ret);
return 0;
}
char * strstr ( const char *, const char * );
strstr函数的功能:
在字符串中找一个字符串
返回str2在str1中第一次出现的位置;如果str2在str1中没有出现,就返回NULL
#include
#include
#include
int main()
{
char s1[] = "abcdefabcdef";
char s2[] = "efabc";
char* ret = strstr(s1, s2);
if (ret != 0)
printf("找到了,是%s\n", ret);
else
printf("找不到\n");
return 0;
}
首先,我们会想到遍历,当我们成功找到字符串时,却发现没有记住匹配成功的首个字符位置,无法返回首元素的地址,而且这只是最简单一种情况,我们直接就找到了
我们要考虑到多次查找和首元素的地址确定
char * strtok ( char * str, const char * sep);
解析:
• sep参数指向⼀个字符串,定义了⽤作分隔符的字符集合
• 第⼀个参数指定⼀个字符串,它包含了0个或者多个由sep字符串中⼀个或者多个分隔符分割的标记。//第一个参数指向的字符串是由sep指向的第二个参数指向的字符串分割的
• strtok函数找到str中的下⼀个标记,并将其⽤ \0 结尾,返回⼀个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使⽤strtok函数切分的字符串⼀般都是临时拷⻉的内容并且可修改。)//将分割符变成\0,并返回标记的起始地址,打印的时候到\0停止
• strtok函数的第⼀个参数不为 NULL ,函数将找到str中第⼀个标记,strtok函数将保存它在字符串中的位置。//记住\0的位置
• strtok函数的第⼀个参数为 NULL ,函数将在同⼀个字符串中被保存的位置开始,查找下⼀个标记。
• 如果字符串中不存在更多的标记,则返回 NULL 指针。
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
int main()
{
char s[] = "[email protected]";//字符串
char* p = "@.";//分隔符
char *ret = strtok(s, p);
printf("%s\n", ret);
return 0;
}
如果分割符过多的话,则上面的方法过于冗长,需要我们一次次的运行,那我们可以将字符串拷贝到目标空间,再目标空间里进行循环输出,并且不改变源空间的字符串
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
int main()
{
char s[] = "[email protected]";//字符串
char* p = "@.";//分隔符
char buf[100] = { 0 };
strcpy(buf, s);
char* r = NULL;
for (r = strtok(s, p); r != NULL; r = strtok(NULL, p))
{
printf("%s\n", r);
}
return 0;
}
char * strerror ( int errnum );
strerror函数可以把参数部分错误码对应的错误信息的字符串地址返回来。
在不同的系统和C语⾔标准库的实现中都规定了⼀些错误码,⼀般是放在 errno.h 这个头⽂件中说明的,C语⾔程序启动的时候就会使⽤⼀个全局的变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表⽰没有错误,当我们在使⽤标准库中的函数的时候发⽣了某种错误,就会将对应的错误码,存放在errno中,⽽⼀个错误码的数字是整数很难理解是什么意思,所以每⼀个错误码都是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。
在 C语言的库函数中设计了一些错误码,当我们库函数在调用的过程中发生的各种错误,要记录下来,这时候记录的就是错误码
例如:404———>该网页不存在
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
int main()
{
int i = 0;
for (i = 0; i < 10; i++)
{
char* ret = strerror(i);
printf("%d:%s\n", i, ret);
}
return 0;
}
但是,我们怎么知道代码运行失败的原因是什么呢?
我们可以举个例子,来看看库函数在C语言中的作用,也可以看看错误的原因是什么
当库函数调用失败的时候,会将错误码记录到errno这个变量中
errno是C语言的一个全局变量
例如,我们要读取一个文件,读取文件前,我们要打开文件,打开成功,前提是文件存在,反之,则打开失败
我们看一下界面:
我想读区test_9_7这个文件,看看我们能不能成功
#include
#include
#include
#include
int main()
{
FILE* pf = fopen("test_9_7", "r");
if (pf == NULL)
{
printf("打开文件失败,失败的原因是:%s\n", strerror(errno));
return 1;
}
else
{
printf("打开文件成功\n");
}
return 0;
}
虽然我们看到了错误原因,但是为什么打开失败呢?明明我的界面上就有这个文件啊!
那是因为,我们没有显示文件拓展名
通过拓展名,我们发现原来我们输入的文件名不对
这样之后,就可以打开成功了
perror函数是打印错误信息的
#include
#include
#include
int main ()
{
FILE * pFile;
pFile = fopen ("unexist.ent","r");
if (pFile == NULL)
perror("Error opening file unexist.ent");
return 0;
}
本次的分享到这里就结束了!!!
PS:小江目前只是个新手小白。欢迎大家在评论区讨论哦!有问题也可以讨论的!
如果对你有帮助的话,记得点赞+收藏⭐️+关注➕