首先常用字符串函数有:
strlen函数
strcpy函数
strcmp函数
strcat函数
strstr函数
strtok函数
memcpy函数
memmove函数
strerror函数
本次模拟的是strtok函数,memcpy函数,strerror函数,memmove函数,这三个比较难的函数
首先是
strtok函数
通过一个案例
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = "hanchun@yeah.net";
char buf[30] = { 0 };//"hanchun@yeah.net"
strcpy(buf,arr);
//将arr数组内的字符串拷贝到buf中
printf("%s\n",buf);
const char* p = "@.";
char *str = strtok(buf, p);//切割buf字符串,找到@改为\0变为"hanchun\0yeah.net",同时返回'h'的地址
printf("%s\n",str);
str = strtok(NULL, p); //从刚刚保存好的地址开始找, 找到.改为\0变为"hanchun\0yeah\0net", 同时返回'y'的地址
printf("%s\n", str);
str = strtok(NULL,p);//从'y'的地址开始找,往后找不到要修改的字符了,遇到\0就会停止
printf("%s\n", str);
return 0;
}
运行结果如下:
知道了如何使用srtok函数,如何更高效的使用这个函数呢,可以参考for循环初始化和判断最后执行代码块的思路
int main()
{
char arr[] = "192.168.3.212";
char buf[30] = { 0 };
strcpy(buf, arr);
const char* p = ".";
//首先初始化str为空指针
char* str = NULL;
for (str = strtok(buf, p); str != NULL; str = strtok(NULL, p))
{
printf("%s\n", str);
}
return 0;
}
运行结果也没有问题
既然已经搞懂了如何使用,那么他的应用场景也能看出来,用于像是邮箱的后缀名的切割和剪切之后传输数据,
strerror函数
这个函数比较重要,需要模拟他在库函数里面的实现,还是老样子,先看看这个函数如何使用
这个函数是将错误码翻译成错误信息
:C语言的库函数在调用失败的时候,会将一个错误码存放在一个叫:errno的变量中
当我们想知道调用库函数的时候发生了什么错位信息,就可以将Lerrno中的错误码进行翻译成错误信息,下面是该函数的使用情况:当需要打开文件需要确保有打开还是没有打开都需要用这个文件,进行报错
#include<stdio.h>
#include<errno.h>
#include <string.h>
#define _CRT_SECURE_NO_WARNINGS fopen,strerror
int main()
{
//打开文件
// 打开文件的时候,如果文件的打开方式是"r"
//打开文件失败的话,会返回NULL
FILE* pf = fopen("test.txt","r");
if (pf == NULL)
{
printf("打开文件失败,原因是 %s\n",strerror(errno));
return 1;
}
//读写文件
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
这里替换printf("打开文件失败,原因是 %s\n",strerror(errno));为perror("打开文件失败")
可以把perror函数理解成printf+strerror
接下来就是重头戏了
memcpy函数
意为内存拷贝函数
#include<stdio.h>
int main()
{
int arr1[] = {1,2,3,4,5,6,7,8,9,10};
int arr2[8] = { 0 };
//把arr1中的前五个数据拷贝到arr2中
memcpy(arr2,arr1,20);
//将arr1的值赋给arr2,通过访问arr1中的20个字节=刚好是五个int元素
return 0;
}
当然如果是其他数据类型呢?memset函数又会报错吗?代码所示
void test01()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[8] = { 0 };
//把arr1中的前五个数据拷贝到arr2中
memcpy(arr2, arr1, 20);
//将arr1的值赋给arr2,通过访问arr1中的20个字节=刚好是五个int元素
}
很明显是不会错误的,因此memcpy函数甚至适应程度高于strcpy函数(仅用于拷贝字符串)
那么重点来了,该如何模拟实现呢?------我们可以查一下他的源代码
注意:
1. 这里的void*--通用类型的指针,可以接收任何数据类型的地址,这下懂了吧,废话不多说,模拟实现
2.返回的是目标空间的起始地址
#include<stdio.h>
#include <corecrt_memory.h>
void* my_memcpy(void *dest,const void *str,size_t byte)
{
void* ret = dest;
while(byte--)
{
//将指针的地址的数据类型转换为char*类型再进行解引用,访问一个字节的替换
*(char*)dest = *(char*)str;
//右值可修改,左值不可修改,因此不可以使用(char*)dest = (char*)dest+1
dest = (char*)dest + 1;
str = (char*)str + 1;
}
return ret;
}
那如果任何数据类型的数组都可以进行拷贝,那么如果出现了自己拷贝自己呢?
此时如果自己拷贝自己,就会报错或者拷贝的值不一样,这是为什么呢?,其实是因为拷贝过程中两个数组之间有重叠部分,当重叠部分进行拷贝,可能会出现将自身str2自己更改了元素,因此我们使用另一种函数memmove
memmove函数
memmove函数的三个参数跟memcpy函数功能相同,不再赘述
这里语言会繁琐,因此使用图像进行分析
例如:我要拷贝数组str到dest中,就会出现从前->后拷贝和从后->前拷贝,两种情况不一样,如图所示:
因此代码也要分为两种情况
void* my_memmove(void*dest, const void*str,size_t byte)
{
//从前->后
void* ret = dest;
if (dest > str)
{
while (byte--)
{
*(char*)dest = *(char*)str;
dest = (char*)dest + 1;
str = (char*)str + 1;
}
}
//从后->前
else
{
while (byte--)
{
*((char*)dest + byte) = *((char*)str + byte);
}
}
return ret;
}
void test02()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr1,arr1+4,20);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
}
int main()
{
test02();
return 0;
}
因此在C语言中,memcpy拷贝不重叠的内存,出现了重叠的就交给memmove函数
memcmp函数
其实是strcmp函数的优化版,也是无论什么数据类型的数组或者字符串进行比较,都可以个需要比较,多少个字节内谁大,一般要考虑机器是大小端后才能进行比较,以小端为例
void test03()
{
int arr1[] = {1,2,3,4,5};//01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00后面这俩部分都没进行比较
int arr2[] = {1,2,3,4,6};//01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 06 00 00 00后面这俩部分都没进行比较
int ret = memcmp(arr1,arr2,17);
printf("ret = %d\n",ret);//结果为-1,arr1<arr2
}
该函数模拟实现与strcmp函及其类似就不实现了
memset函数
这个函数用得很广,一定需要掌握
代码演示如下:
void test04()
{
char arr[] = "hello world";
memset(arr,'x',5);
printf("%s",arr);//结果为xxxxx world
}
错误使用案例
void test05()
{
int arr[10] = {0};
//想将数组内的所有元素都变为1
memset(arr,1,sizeof(arr));
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ",arr[i]); //结果为16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009 16843009
}
}
这是为什么?原来是因为memset函数是对着每个字节都进行更改,二进制表达是101010 ,换算结果就为16843009,因此在初始化数组为0时不能用memset函数