【C语言】——内存函数的使用及模拟实现

前言一、

m

e

m

c

p

y

memcpy

memcpy 函数1.1、函数功能(1)函数名理解(2)函数介绍

1.2、函数的使用1.3、函数的模拟实现

二、

m

e

m

m

o

v

e

memmove

memmove 函数2.1、函数功能2.2、函数的使用2.3、函数的模拟实现(1)思路一(2)思路二(3)代码实现

三、

m

e

m

s

e

t

memset

memset 函数3.1、函数功能3.2、函数的使用3.3、函数的模拟实现

四、

m

e

m

c

m

p

memcmp

memcmp 函数4.1、函数功能4.2、函数的使用4.3、函数的模拟实现

前言

  在【C语言】——字符串函数的使用与模拟实现(上)与【C语言】——字符串函数的使用与模拟实现(下)二文中,我曾详细介绍了字符串相关函数的具体功能及其模拟实现。      然而,实践过程中,我们往往不仅仅只是处理字符串类型的变量,若要对浮点型、整型、结构体等其他类型的数据进行相关操作,又该怎么办呢?      这时,就需要用到的内存操作函数了      接下来,让我们一起来学习学习吧

一、

m

e

m

c

p

y

memcpy

memcpy 函数

1.1、函数功能

(1)函数名理解

在介绍

m

e

m

c

p

y

memcpy

memcpy函数 的功能前,我们先来对函数名进行分析:

m

e

m

mem

mem 表示的是

m

e

m

o

r

y

memory

memory,它有记忆、内存的意思,这里它表示的是内存

c

p

y

cpy

cpy 即

c

o

p

y

copy

copy,表示拷贝

  这样我们从名字就能大体知道这个函数的大致功能啦,像之前的

s

t

r

c

p

y

strcpy

strcpy函数,它是实现对字符串的拷贝,而

m

e

m

c

p

y

memcpy

memcpy函数,实现的是对内存块的拷贝。      

(2)函数介绍

功能:将源字符串的前

n

u

m

num

num 个内存块内容拷贝到目标字符串中

函数

m

e

m

c

p

y

memcpy

memcpy 从

s

o

u

r

c

e

source

source 的位置开始向后复制

n

u

m

num

num 个字节的数据到

d

e

s

t

i

n

a

t

i

o

n

destination

destination 指向的内存为止函数的使用需包含头文件

<

s

t

r

i

n

g

.

h

>

因为拷贝的数据不一定是字符串,函数在遇到 ‘\0’ 时不会停下来如果

s

o

u

r

c

e

source

source 和

d

e

s

t

i

n

a

t

i

o

n

destination

destination 有任何的重叠,复制的结果都是未定义的

d

e

s

t

i

n

a

t

i

o

n

destination

destination 所指向的空间必须足够大,至少

n

u

m

num

num 个字节大小,以避免溢出返回值是目标空间的地址

注:对于重叠的内存,交给

m

e

m

m

o

v

e

memmove

memmove函数 来处理。

1.2、函数的使用

   例如:我们将整型数组

a

r

r

1

arr1

arr1 的前 5 个元素复制给

a

r

r

2

arr2

arr2 数组    代码如下:

#include

#include

int main()

{

int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };

int arr2[20] = { 0 };

memcpy(arr2, arr1, 20);

int i = 0;

for (i = 0; i < 10; i++)

{

printf("%d ", arr2[i]);

}

printf("\n");

return 0;

}

   运行结果:      可能有些小伙伴在调用

m

e

m

c

p

y

memcpy

memcpy 函数时,

n

u

m

num

num 的传参会写成 5 :memcpy(arr2, arr1, 5);,这里需要注意:

m

e

m

c

p

y

memcpy

memcpy 拷贝是内存块为单位,即字节,而不是要拷贝的元素的个数。   当然,

n

u

m

num

num 的传参也可以写成 元素个数

*

s

i

z

e

o

f

sizeof

sizeof(类型)   如:上述代码调用

m

e

m

c

p

y

memcpy

memcpy 函数可写成:memcpy(arr2, arr1, 5*sizeof(int));      那如果用

m

e

m

c

p

y

memcpy

memcpy 拷贝重叠空间会怎样呢?我们用自己写的模拟函数试一下

int main()

{

int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };

my_memcpy(arr1 + 2, arr1, 20);

int i = 0;

for (i = 0; i < 10; i++)

{

printf("%d ", arr1[i]);

}

printf("\n");

return 0;

}

   运行结果:    为什么会这样呢?具体原因稍后解释。      

1.3、函数的模拟实现

m

e

m

c

p

y

memcpy

memcpy函数 的实现思路和

s

t

r

c

p

y

strcpy

strcpy 的实现差不多,即逐一拷贝每个内存块(详情请看:【C语言】——字符串函数的使用与模拟实现(上))   

void* my_memcpy(void* dest, void* src, size_t num)

{

void* ret = dest;

assert(dest && src);

while (num--)

{

*(char*)dest = *(char*)src;

dest = (char*)dest + 1;

src = (char*)src + 1;

}

return ret;

}

   代码逐行解析:

参数用

v

o

i

d

void

void* 的原因:

m

e

m

c

p

y

memcpy

memcpy 拷贝的是任意类型的数据,而函数事先并不知道你所用传递的数据的类型,因此用

v

o

i

d

void

void * 指针,以便能接收所有类型的数据。assert(dest && src);断言,后面要对指针进行解引用,因此不能传入空指针。while (num--)循环条件的判断,

n

u

m

num

num 不为 0,即为真,进入循环,后

n

u

m

num

num 自减*(char*)dest = *(char*)src;【C语言】—— 指针一 : 初识指针(上)一文中,曾提到不能对

v

o

i

d

void

void * 指针进行解引用操作,因此要改变其所指向的值,要对其进行强制类型转换。因为

c

h

a

r

char

char * 类型一次的访问权限只有一个字节,因此将其强转成

c

h

a

r

char

char* 类型,再进行解引用、赋值dest = (char*)dest + 1;指针向后移,指向下一个字节   

  这里有小伙伴可能会问:dest = (char*)dest + 1;我觉得有点啰嗦,能不能写成(char*)dest++;呢?   答案是:不能   为什么呢?   因为强制类型转换是临时的。这里的 ++,不是对

c

h

a

r

char

char* 类型的

d

e

s

t

dest

dest++,而是对

v

o

i

d

void

void* 类型的

d

e

s

t

dest

dest++   上述代码可拆分成两句:(char*)dest;,dest++;      那((char*)dest)++;可行吗?   基本是没问题的,但需要注意的是,有部分编译器可能编不过去。      因此,循环语句的内容也可写成:

while (num--)

{

*((char*)dest)++ = *((char*)src)++;

}

当然,最好还是上面的那种写法。

二、

m

e

m

m

o

v

e

memmove

memmove 函数

2.1、函数功能

     在前面将

m

e

m

c

p

y

memcpy

memcpy函数 时,我曾提到,对于重叠的内存,交给

m

e

m

m

o

v

e

memmove

memmove函数 来处理,那么

m

o

m

m

o

v

e

mommove

mommove函数 又是做什么的呢?   

功能:将源字符串的前

n

u

m

num

num 个内存块内容拷贝到目标字符串中

m

e

m

c

p

y

memcpy

memcpy函数 的差别就是

m

e

m

m

o

v

e

memmove

memmove函数 处理的源内存块和目标内存块可以重叠的。如果源空间和目标空间出现重叠,就得使用

m

e

m

m

o

v

e

memmove

memmove函数 处理。

2.2、函数的使用

#include

#include

int main()

{

int arr[] = { 1,2,3,4,5,6,7,8,9,10 };

memcpy(arr + 2, arr, 20);

int i = 0;

for (i = 0; i < 10; i++)

{

printf("%d ", arr[i]);

}

printf("\n");

return 0;

}

   运行结果:

   图示:

     可以看到,复制 5 个元素,目标空间与源空间重合了,这时,就不能再使用

m

e

m

c

p

y

memcpy

memcpy 函数,而要用

m

e

m

m

o

v

e

memmove

memmove 函数了   

2.3、函数的模拟实现

(1)思路一

     要实现重叠空间的拷贝,有什么办法呢?

  我们想到,可以另外开辟一个数组,将原数组所有数据进行拷贝。然后,再对需要拷贝的元素进行拷贝。

  但是,这个方法太过繁琐,而且要单独创建数组,浪费空间,这里我们暂不考虑。

(2)思路二

  那还有什么办法呢?我们不妨想一想,为什么内存空间重叠不能用memcpy函数?因为新拷贝的内容会将原来真正想拷贝的内容覆盖,这样最终拷贝出来的结果就不是自己想要的    图示:

  那有什么办法可以让先拷贝的内容不覆盖后拷贝的内容呢?

  我们可以从后往前拷贝,如图:   

     这时,有小伙伴就说了:只要以后我们都从后往前拷就行了   但问题又来了,如果是从

a

r

r

arr

arr + 2 开始拷贝 5 个元素到

a

r

r

arr

arr 呢?    如图:

     发现又出问题了,但问题和之前一样,先拷贝的内容覆盖了原数据,导致后拷贝的内容错误   

  这时就需要用到从前往后拷贝了   

     那什么时候从后往前拷,什么时候从前往后拷呢?    结论:

当两块空间重叠时,目标空间在源空间前面,从前向后拷贝当两块空间重叠时,目标空间在源空间后面,从后向前拷贝当两块空间不重叠时,两种拷贝方法都行   

图示:

(3)代码实现

     这里,空间不重叠的部分我们用从后向前,因为这样只用在

s

r

c

src

src 处分界即可

void* my_memmove(void* dest, void* src, size_t num)

{

void* ret = dest;

assert(dest && src);

if (src > dest)

{

while (num--)

{

//前->后

*(char*)dest = *(char*)src;

dest = (char*)dest + 1;

src = (char*)src + 1;

}

}

else

{

//后->前

while (num--)

{

*((char*)dest + num) = *((char*)src + num);

}

}

return 0;

}

     这段代码从前往后的情况与

m

e

m

c

p

y

memcpy

memcpy 情况相同,就不再过多解释,来看从后往前的情况

while (num--)这里循环判断条件与上面一样,当

n

u

m

num

num 大于 0,进入循环,随后进行自减。*((char*)dest + num) = *((char*)src + num);这里我们发现,如果要拷贝 20 字节内容,即

n

u

m

num

num 等于 20,那么起始位置加

n

u

m

num

num - 1 所指向的就是最后一个要拷贝的字节,而

n

u

m

num

num 在进入循环后已经自减,因此这里直接加

n

u

m

num

num 。再接着,再次循环,

n

u

m

num

num 再次自减,拷贝倒数第二个字节内容,以此类推,直至

n

u

m

num

num 为 0 拷贝结束。      

三、

m

e

m

s

e

t

memset

memset 函数

3.1、函数功能

功能:设置内存,将内存中的值以字节为单位设置成想要的内容

p

t

r

ptr

ptr:指向要设置的内存块

v

a

l

u

e

value

value:要设置的值

n

u

m

num

num:要设置字节的个数返回值:要设置的内存,即

p

t

r

ptr

ptr   

3.2、函数的使用

#include

#include

int main()

{

char str[] = "hello world";

memset(str, 'x', 6);

printf(str);

printf("\n);

return 0;

}

   运行结果:

     这时,有小伙伴问能不能用它来设置整型呢?当然可以

#include

#include

int main()

{

int arr[10];

memset(arr, 0, 10*sizeof(int));

int i = 0;

for (i = 0; i < 10; i++)

{

printf("%d ", arr[i]);

}

printf("\n");

return 0;

}

   运行结果:      那又有小伙伴问全部设置成 1 可以吗?我们来试试

#include

#include

int main()

{

int arr[10];

memset(arr, 1, 10*sizeof(int));

int i = 0;

for (i = 0; i < 10; i++)

{

printf("%d ", arr[i]);

}

printf("\n");

return 0;

}

   运行结果:   为什么会这样?   我们要注意:

m

e

m

s

e

t

memset

memset 是以字节为单位来设置的。   1 的二进制是 00000000 00000000 00000000 00000001   而

m

e

m

s

e

t

memset

memset 是将每个字节单独设置成 1,即一个整型被设置成:00000001 00000001 00000001 00000001,转换成二进制即:16843009

3.3、函数的模拟实现

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

{

assert(ptr);

while (num--)

{

*((char*)ptr + num) = value;

}

return ptr;

}

四、

m

e

m

c

m

p

memcmp

memcmp 函数

4.1、函数功能

功能:比较从

p

t

r

1

ptr1

ptr1 和

p

t

r

2

ptr2

ptr2 指针指向的位置开始,向后的

n

u

m

num

num 个字节    返回值如下:

如果返回值 < 0,则表示 str1 小于 str2。如果返回值 > 0,则表示 str1 大于 str2。如果返回值 = 0,则表示 str1 等于 str2。

4.2、函数的使用

int main()

{

char buffer1[] = "DWgaOtP12df0";

char buffer2[] = "DWGAOTP12DF0";

int n = 0;

n = memcmp(buffer1, buffer2, sizeof(buffer1));

if (n > 0)

printf("'%s' is greater than '%s'.\n", buffer1, buffer2);

else if(n < 0)

printf("'%s' is less than '%s'.\n", buffer1, buffer2);

else

printf("'%s' is the same '%s'.\n", buffer1, buffer2);

return 0;

}

   运行结果:   

4.3、函数的模拟实现

int my_memcmp(const void* ptr1, const void* ptr2, size_t num)

{

assert(ptr1 && ptr2);

while (*(char*)ptr1 == *(char*)ptr2 && num--)

{

ptr1 = (char*)ptr1 + 1;

ptr2 = (char*)ptr2 + 1;

}

return *(char*)ptr1 - *(char*)ptr2;

}

文章链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: