首页 > 系统相关 > 字符串与内存函数(2)

字符串与内存函数(2)

时间:2023-02-08 20:37:11浏览次数:50  
标签:函数 memmove void num 内存 字符串 拷贝 memcpy

 本篇文章和大家继续分享一些与字符串和内存操作相关的函数,本次分享的函数包含有strerror函数,memcpy函数,memmove函数以及memcmp函数和memset函数。以上几个函数就是我们本次分享的主要内容,现在,让我们进入本次的分享吧!

首先,介绍的是strerror

strerror

char* strerror(int errnum);

函数strerror的参数为整型,传参的时候传error。为什么呢?c语言的库函数在运行的过程中,如果发生错误,会将错误码返回到一个变量中,而这个变量是error。错误码则是一些数字 1,2,3,4,5。每一个数字都包含这对应的错误信息,我们光看数字是看不出来的,所以,需要用到strerror这个函数的功能将其转换成对应的错误信息。它的返回值是错误信息字符串的地址。接下来,让我们看看使用的示例

strerror的使用示例

#include<stdio.h>
int main()
{
FILE*pf=fopen("text.c",'r');//这里使用到一个打开文件的函数,在后面的文
if(pf==NULL) //会和大家分享,等不及的小伙伴动手自己学习一下。
{
printf("%s\n",strerror(error));//当fopen打开文件失败的时候,就会打
return 1;} //印打开失败的原因
fclose(pf);
pf=NULL;
}

以上是函数strerror的一个使用示例,不知道大家理解没有。其实,在函数中还有一个同样功能的perror函数,不过,它的使用方法和打印效果是不同的,它会自动获取就近的error的变量,不需要我们认为传参。我们给它传递对应的提示信息,这里的传递的提示信息是为了便于让我们知道是那里的那个函数出了问题。同样的给大家演示一下

#include<stdio.h>
int main()
{
FILE*pf=fopen("text.c",'r');
if(pf==NULL)
{
perror("fopen");
return 1;}
fclose(pf);
pf=NULL;
}

我们假设我们要打开的文件不存在,看看两个程序的输出效果:

printf("%s\n",strerror(error));
//输出结果:No such file or directory
perror("fopen");
//输出结果:fopen:No such file or directory
//不用我多说,效果一目了然。其实通俗来说就是以下两种效果相同,只是传参不同
printf("fopen:%s\n",strerror(error));
//与
perror("fopen");//效果相同

函数perror与函数strerror的功能基本相同,不知道大家理解没。我们接着分享下一个函数memcpy。

memcpy

void* memcpy(void*destination,const void*source,size_t num);

函数memcpy有点与函数strncpy的类似,第一个参数是目标空间的地址,第二个参数是拷贝来源的空间地址,第三个参数是拷贝的数量,以字节为单位。不过,memcpy的功能比strncpy的功能更为强大,不再局限于拷贝字符,适用于拷贝所有的类型数据。我们就以整型来举个例子吧!

memcpy的使用示例

#include<stdio.h>
int main()
{
int arr1[20]={0};
int arr2[]={1,2,3,4,5,6,7};
//利用memcpy函数将数组arr2的所有元素拷贝到数组arr1中
memcpy(arr1,arr2,28);
return 0;
}

以上就是一个memcpy函数的使用,接下来,我们进行一个memcpy的模拟实现。

memcpy的模拟实现

思路

拷贝,本质就是复制。那我们如何进行复制,才能适用于所有类型呢?数据在储存的时候,都会分成一个一个字节的储存。那么我们是不是,一个一个字节的拷贝就能适用于所有类型的拷贝了。

好了,有了思路,我们进入对memcpy的一个模拟实现。

模拟实现

//首先,参考memcpy的原型,设置参数和返回值
void*my_memcpy(void*dest,const void*src,size_t num)
{
//保存目标空间的起始地址
void*ret=dest;
//其次,一一进行复制
while(num--)
{
(char*)dest=(char*)src;
dest=(char*)dest+1;
src=(char*)src+1;
}
return ret;
}

好了,memcpy的模拟实现已经讲完了,不知道我说明白没。接下来,我们要讲的是memmove.

memmove

void*memmove(void*destination,const void*source,size_t num);

和memcpy相比,memmove可以实现源内存块和目标内存块重叠的拷贝。不过,现在的vs2022中的memcpy已经和memmove的功能一样了,也能实现重叠内存块的拷贝。对于memmove的使用与memcpy相同

menmove的使用

与memcpy的使用相同,可以实现重叠内存块的拷贝。而mencpy具不具备实现重叠内存块的拷贝取决于编译环境。

接下来,我们讲讲menmove的模拟实现

me​mmove的模拟实现

思路

memmove比memcpy多了一个功能,能够实现重叠内存块的拷贝。那么,我们如何实现重叠内存块的拷贝呢?能不能直接是用memcpy的方法呢?

答案是否定的。如果我们使用从前拷贝(先拷贝低地址的)将同一内存块中的 1 位置中的内容拷贝到 2 位置,会将要拷贝的内容干掉,无法完成拷贝。那我们怎么样才能避免这样的问题呢?后面的内容会被干掉,那我们从后面开始拷贝不就避免了内容被干掉的问题了。

                — — — — — — — — — — — —

                — — — — —    1

                               — — — — —  2​

那以下这种把 1 位置的内容拷贝到 2 位置情况我们还能不能使用从后面开始拷贝呢?

                         ​— — — — — — — — — — — —

                                               — — — — —    1

                              — — — — —  2​

答案是否定的,这种情况如果从后拷贝就会把前面要拷贝的内容干掉,无法完成拷贝。前面的内容被干掉,那么,我们是不是可以考虑一下从前开始拷贝呢?

好了,了解完memmove模拟的一个基本思路,我们进入对memmove的模拟实现。

模拟实现

//首先,参照memmove的形式,设置参数和返回值
void*memmove(void*des,const void*src,size_t num)
{
//保留目标空间起始地址作为返回值
void*ret=des;
//分情况,使用不同的拷贝方式,以实现内存块重叠的拷贝
if(src>des)//目标空间地址小于源空间的地址,从前面开始拷贝
{
while(num--)
{
(char*)des=(char*)src;
des=(char*)des+1;
src=(char*)src+1;
}
return ret;
}//目标空间地址大于源空间的地址,从后面开始拷贝
else
{
while(num--)
{
*((char*)des+num)=*((char*)src+num);
}
return ret;
}
}

到这里,我们对memmove的模拟实现也就完成了。接下来,我们进入对memcmp的认识了解。

memcmp

int memcmp(const void*str1,const void*str2,size_t num);

memcmp函数与strncmp类似,但memcmp可以比较所有类型的类型,而strncmp只可以比较字符类型。memcmp的第一第二是要比较内容的空间地址,第三个是比较的数量,以字节为单位。它的返回值大于0表示str1>str2,返回值等于0表示str1=str2,返回值小于0表示str1<str2.

我们以比较整型为例:

mencmp的使用

#include<stdio.h>
int main()
{
int arr1[3]={1,2,3};
int arr2[3]={1,2,5};
int ret=memcmp(arr1,arr2,12);
printf("%d\n",ret);//结果是小于0的数表示arr1<arr2.
return 0;
}

好了,以上就是memcmp的使用,不知道我讲明白没有,接下来,我们说说memset

memset

void * memset ( void * ptr, int value, size_t num );

memset是一个内存设置函数。它是以字节为单位设置内存数据的。让我们看看如何使用它。

memset的使用

#include<stdio.h>
int main()
{
char arr[]="aaaaaaaaaaaaaaaaaaaaaaaaaa";
memset(arr,'x',5);//该函数调用会将前5个a改成x。这时,有疑惑了,第二个
//参数不是整型吗?其实,字符是以ASCII值储存的,本质也是整型。当然,你也可以
//传对应的ASCII码值
return 0;
}

好了,到这里,本次的分享就到此结束了,不知道我有没有说明白,给予你一点点收获。如果你有所收获,别忘了给我点个赞,这是对我最好的回馈,当然你也可以在评论发表一下你的收获和心得,亦或者指出我的不足之处。如果喜欢我的分享,别忘了给我点关注噢。

标签:函数,memmove,void,num,内存,字符串,拷贝,memcpy
From: https://blog.51cto.com/u_15933803/6044779

相关文章

  • 库函数的模拟实现
    1.1模拟实现strlen注意:参数指向的字符串必须要以'\0'结束。函数的返回值为size_t,是无符号的。1.2代码如下:三种方式:方式1://计数器方式intmy_strlen(constchar*str){in......
  • 1.5函数的调用机制
        哪怕是高级语言编写的程序,函数调用处理也是通过把程序计数器的值设定成函数的存储地址来实现的。不过,这和条件分支、循环的机制有所不同,因此单纯的跳转指令无法......
  • C语言填空:10进制转2进制输出函数
    #include<stdio.h>//将10进制正整数转化为二进制voiddec2bin(intm){intbin[32],j;for(j=0;【1】;j++){bin[j]=【2】;m=【3】;......
  • C# Newtonsoft.Json null 转空值{} 把对象null转换{}为JSON字符串
    ///<summary>///把对象null转换{}为JSON字符串///</summary>///<paramname="o">对象</param>///<returns>JSON字符串</return......
  • React函数式组件使用@emotion时一定要注意的问题!
    怎么说呢,一个坑,踩了两天,总觉得是useSate和input的传值方法问题在useMemo和useCallback反复测试问题最后没办法,通过最傻方式,一点点注释代码,发现了问题constContainer=......
  • 判断字符串是否包含某些字符
    1、工具方法publicstaticList<String>isContain(StringsourceStr,String[]targetSourceArr){ List<String>containList=newArrayList<>(); for(Stringta......
  • 【SQL Server】中的日期函数和日期数据类型
    SQLServerDate函数SQLServer的重要日期函数包括:函数描述参数含义GETDATE()返回当前的日期和时间 DATEPART(datepart,date) 返回日期/时间的单独部分......
  • JVM内存学习 2.0
    先说一下结果1.Linux的内存分配是惰性分配的.APP申明了kernel并不会立即进行初始化和使用.2.JVM的内存主要分为,堆区,非堆区,以及jvm使用的其他内存.比如直接内......
  • #yyds干货盘点# LeetCode面试题:字符串转换整数 (atoi)
    1.简述:请你来实现一个 myAtoi(strings) 函数,使其能将字符串转换成一个32位有符号整数(类似C/C++中的atoi函数)。函数 myAtoi(strings)的算法如下:读入字符串并丢弃......
  • 普通型生成函数
    普通型生成函数一、定义构造这么一个多项式函数\(F(x)\),使得\(x\)的\(n\)次方系数为\(f(n)\)。\[F[x]=\sum^\infty_{i=0}f(i)\x^i\]二、格式声明==为逻辑......