本章包含C语言内存函数memcpy、memmove、memset、memcmp函数的讲解,从语法形式到使用示例再到模拟实现几个层次进行讲解。
一、memcpy 函数
1.1 语法形式
#include <string.h>
void * memcpy ( void * destination, const void * source, size_t num );
功能:
① 对于 strcpy 函数只操作字符串,而 memcpy 函数则是完成内存块的拷贝,即不关注内存中存放的数据是什么(不管是字符串还是整型还是浮点型,都能完成拷贝);
② 函数 memcpy 从 source 的位置开始向后复制 num 个字节的数据到 destination 指向的内存位置;
③ 如果 source 和 destination 有任何的重叠,复制的结果都是未定义的。(所以再处理内存重叠的情况时使用的是 memmove 函数);
参数:
① destination:指针,指向目标空间,拷贝的数据存放在这里;
② source:指针,指向源空间,要拷贝的数据从这里来;
③ num:要拷贝的数据占据的字节数。
返回值:
拷贝完成后,返回目标空间的起始地址
1.2 使用示例
① 整型
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = { 0 };
memcpy(arr2, arr1, 40); // 将40个字节,即10个整型元素(arr1全部元素)复制到arr2中
memcpy(arr2, arr1, 12); // 3个元素(1,2,3)复制到arr2中
memcpy(arr2 + 2 , arr1, 12); // 从arr2第3个元素开始复制
return 0;
}

② 浮点型
#include <stdio.h>
#include <string.h>
int main()
{
float arr1[] = { 1.1f,2.3f,3.8f,4.9f,5.6f };
float arr2[20] = { 0 };
memcpy(arr2, arr1, (5 * sizeof(float))); // memcpy(arr2, arr1, 20);
for (int i = 0; i < 6; i++)
{
printf("%.1f ", arr2[i]); // 保留一位小数
}
return 0;
}

浮点型 float 类型的数据在计算机中存储是不精确的,这涉及到浮点数在内存中的存储的知识,下一篇文章将会对此讲解。但是如果要输出 arr2 中的元素内容,输出结果就是 arr1 的元素:

1.3 模拟实现 memcpy 函数
实现思路与 strcpy 函数类似,见上文《C语言中操作字符串的函数》。改变的条件是循环次数,在 memcpy 函数中传入的参数 num 多大,循环就循环多少次。
#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
while (num--) // num是多少,循环就遍历多少
{
*(char*)dest = *(char*)src;
/*src = (char*)src + 1;
dest = (char*)dest + 1;*/
// 强制类型转化是临时的,因此不能写成(char*)dest++ / (char*)src++;
++(char*)dest;
++(char*)src;
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = { 0 };
my_memcpy(arr2, arr1, 40);
size_t i = 0;
for (; i < 10; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
如果想使用 my_memcpy 函数将 arr 数组中的1,2,3,4,5复制到元素 2 的后面,即覆盖3,4,5,6,7。意思是将 { 1,2,3,4,5,6,7,8,9,10 } 修改为 { 1,2,1,2,3,4,5,8,9,10 },这就是上文提及到的内容重叠:
int main()
{
// 调用模拟的memcpy函数:
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memcpy(arr + 2, arr, 20); // 结果为1,2,1,2,1,2,1,8,9,10,没达到预期
// 调用库函数:
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
memcpy(arr + 2, arr, 20); // 达到预期,但实际上memcpy函数在处理重叠的情况时,复制的结果都是未定义的。
// 内存重叠的情况使用memmove就行
return 0;
}


二、memmove 函数
2.1 语法形式
#include <string.h>
void* memmove(void* destination, const void* source, size_t num)
功能:也是完成内存块拷贝的,与 memcpy 函数类似,但区别在于是 memmove 函数多了一项功能,即处理的源内存块和目标内存块是可以重叠的。
2.2 使用示例
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = { 0 };
//memcpy(arr2, arr1, 20);
memmove(arr2, arr1, 20); // 与memcpy函数的效果一样
return 0;
}
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr + 2, arr, 20);
return 0;
}
2.3 模拟实现 memmove 函数
实现思路比较复杂,笔者通过画图的方式讲解:



根据第(2)种情况编写程序:
#include <stdio.h>
#include <assert.h>
void* my_memmove(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
if (num == 0)
return dest; // 处理 num=0 的情况
if (dest < src)
{
// 从前向后拷贝
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else
{
// 重叠区域:从后向前拷贝
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
}
return ret;
}
int main()
{
// 内存重叠
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr + 2, arr, 20);
//my_memmove(arr, arr + 2, 20);
return 0;
}


根据第(1)种情况编写:
void* my_memmove(void* dest, const void* src, size_t num)
{
if (num == 0)
return dest; // 处理 num=0 的情况
assert(dest && src);
void* ret = dest;
if (dest >= src && dest < ((char*)src + num))
{
// 后 -> 前
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
}
else
{
// 前 -> 后
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
return ret;
}
三、memset 函数
3.1 语法形式
#include <string.h>
void * memset ( void * ptr, int value, size_t num );
功能:
memset函数是用来设置内存块的内容的,将内存中指定长度的空间设置为特定的内容。
参数:
① ptr:指针,指向要设置的内存空间,也就是存放了要设置的内存空间的起始地址;
② value:要设置的值,函数将会把value值转换成unsigned char的数据进行设置的。也就是以字节为单位来设置内存块的。
③ num:要设置的内存长度,单位是字节。
返回值:返回的是要设置的内存空间的起始地址。
3.2 使用示例
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "hello world";
memset(str + 2, 'r', 4);
printf("%s\n", str);
return 0;
}
输出 herrrrworld
3.3 注意事项
重点:memset 函数是以字节为单位来设置内存数据的
#include <stdio.h>
#include <string.h>
int main()
{
int arr[10] = { 0 };
memset(arr, 1, 40);
return 0;
}

一个元素4个字节,每个字节都被修改为01,没达到将数组全部元素改为1的效果。但如果想将数组所有元素修改为0呢?
#include <stdio.h>
#include <string.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
memset(arr, 0, 40);
return 0;
}

若要使用 memset 函数将数组元素全部修改为0,是可行的
四、memcmp 函数
4.1 语法形式
#include <string.h>
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
功能:比较指定的两块内存块的内容,比较从ptr1和ptr2指针指向的位置开始,向后的num个字节
参数:
① ptr1:指针,指向一块待比较的内存块;
② ptr2:指针,指向另外一块待比较的内存块
③ num:指定的比较长度,单位是字节
4.2 使用示例
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[] = { 1,2,3,4,8 };
int r = memcmp(arr1, arr2, 16);
if (r > 0)
printf(">\n");
else if (r < 0)
printf("<\n");
else
printf("==\n");
return 0;
}
前16个字节就是前4个整型元素1,2,3,4;输出结果为 ==
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[] = { 1,2,3,4,8 };
int r = memcmp(arr1, arr2, 17);
if (r > 0)
printf(">\n");
else if (r < 0)
printf("<\n");
else
printf("==\n");
return 0;
}

因为 arr1 的第17个字节比 arr2 的要小,因此输出 <
4.3 模拟实现 memcmp 函数
#include <stdio.h>
#include <assert.h>
int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
assert(ptr1 && ptr2);
if (num == 0)
return 0;
while (num--)
{
if (*(char*)ptr1 != *(char*)ptr2)
{
if (*(char*)ptr1 > *(char*)ptr2)
return 1;
else
return -1;
}
ptr1 = (char*)ptr1 + 1;
ptr2 = (char*)ptr2 + 1;
}
return 0;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[] = { 1,2,3,4,4 };
int r = my_memcmp(arr1, arr2, 17);
if (r > 0)
printf(">\n");
else if (r < 0)
printf("<\n");
else
printf("==\n");
return 0;
}
五、总结
计算机中存储的最小单位是字节,因此以上内存函数传参的 num 值都是以字节为单位的,是一个一个字节进行处理的。
① memcpy 函数是对内存块进行拷贝,不管内存块存储的数据是什么形式的;
② memmove 函数也是对内存块进行拷贝,与 memcpy 函数的差别在 memmove 函数能实现重叠;
③ memset 函数是用来设置内存块的内容的,单位是字节,因此不能将整型数组中所有元素设置成大于0的数;
④ memcmp 函数是用来比较指定的两块内存块的内容的。
以上就是C语言内存函数的一些内容,有错误的地方欢迎同学们指正。
1626

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



