C语言内存函数

目录

  • [1. memcpy](#1. memcpy)
    • [1.1 代码演示](#1.1 代码演示)
    • [1.2 模拟实现](#1.2 模拟实现)
  • [2. memmove](#2. memmove)
    • [2.1 代码演示](#2.1 代码演示)
    • [2.2 模拟实现](#2.2 模拟实现)
  • [3. memset](#3. memset)
    • [3.1 代码演示](#3.1 代码演示)
    • [3.2 总结](#3.2 总结)
  • [4. memcmp](#4. memcmp)
    • [4.1 代码演示](#4.1 代码演示)
    • [4.2 总结](#4.2 总结)

1. memcpy

项目 内容
函数名 memcpy
头文件 #include <string.h>
函数原型 void *memcpy(void *destination, const void *source, size_t num);
功能 source 指向的内存位置开始,复制 num 个字节的数据到 destination 指向的内存位置
参数 destination 目标空间地址,拷贝后的数据存放到这里
参数 source 源空间地址,要被拷贝的数据从这里开始
参数 num 要拷贝的数据字节数
返回值 返回目标空间的起始地址,即 destination
注意事项 memcpy 只负责按字节拷贝内存不关心数据类型
限制条件 如果 sourcedestination 指向的内存区域发生重叠,结果是未定义的

1.1 代码演示

  1. 字符拷贝:
c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>

int main()
{
	char arr[20] = {0};
	char arr1[] = "abcdef";
	memcpy(arr, arr1, 6);
	printf("%s\n", arr1); //abcdef
	return 0;
}
  1. 数字拷贝
c 复制代码
int main()
{
	int arr1[10] = { 0 };
	int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr2) / sizeof(arr2[0]);
	memcpy(arr1, arr2, sz * 4);
	for (int i = 0; i < sz; i++)
		printf("%d ", arr1[i]); // 1 2 3 4 5 6 7 8 9 10
	return 0;
}

画图演示:

注意:在拷贝中可能会出现重叠的情况,如下:

c 复制代码
int main()
{
	int arr1[10] = { 0 };
	int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr2) / sizeof(arr2[0]);
	memcpy(arr2, arr2 + 2, 20); 
	//源:arr2[2] ~ arr2[6]
  //目标:arr2[0] ~ arr2[4]
	for (int i = 0; i < sz; i++)
		printf("%d ", arr2[i]); // 3 4 5 6 7 6 7 8 9 10
	return 0;
}

这是从后往前 进行拷贝的,拷贝结果和预期效果一样(但不一定是对的,是依赖编译器的实现方式和效果),但是arr2arr2 + 2 属于同一个数组,源区域和目标区域发生重叠 ,使用 memcpy 会导致未定义行为 ,我们再来看从前往后拷贝 的情况:

下面列出在下面的情况下不能使用memcpy函数:

不能使用 memcpy 的情况 原因 正确做法
源内存和目标内存发生重叠 memcpy 不保证重叠拷贝的正确性,会导致未定义行为 使用 memmove
源指针或目标指针是空指针 memcpy 会访问非法内存,程序可能崩溃 拷贝前判断指针是否为 NULL
拷贝长度超过源内存大小 会读取越界内存,导致未定义行为 确保拷贝字节数不超过源空间大小
拷贝长度超过目标内存大小 会写入越界内存,可能破坏其他数据 确保目标空间足够大
拷贝的字节数写死为固定数字 不同平台上数据类型大小可能不同,容易出错 使用 sizeof 计算拷贝大小
目标内存是只读区域 向只读内存写入会导致程序崩溃 确保目标区域可写

1.2 模拟实现

c 复制代码
#include <stdio.h>
#include <string.h>
#include <assert.h>

void* my_memcpy(void* dest, const void* src, size_t num) //用const来修饰src,代表源头数据中的值不能被修改
{
	void* p = dest; //存储目标空间的起始地址
	assert(dest && src); //断言dest和src不为空
	while (num--)
	{
		*((char*)dest) = *((char*)src);
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return p;
}

int main()
{
	int arr1[10] = { 0 };
	int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr1) / sizeof(arr1[0]);
	//使用sizeof(arr2[0])来表示要拷贝的字节数便于代码的可移植性
	my_memcpy(arr1, arr2, 3 * sizeof(arr2[0]));
	for (int i = 0; i < sz; i++)
		printf("%d ", arr1[i]);
	return 0;
}

画图演示:


2. memmove

项目 内容
函数名 memmove
头文件 #include <string.h>
函数原型 void *memmove(void *destination, const void *source, size_t num);
功能 source 指向的内存位置开始,复制 num 个字节的数据到 destination 指向的内存位置
参数 destination 目标空间地址,拷贝后的数据存放到这里
参数 source 源空间地址,要拷贝的数据从这里开始
参数 num 要拷贝的数据所占的字节数
返回值 返回目标空间的起始地址,即 destination
重要特点 memmove 可以处理源内存块和目标内存块重叠的情况
memcpy 的区别 memcpy 不适合处理重叠内存,而 memmove 可以正确处理重叠内存

2.1 代码演示

c 复制代码
#include <stdio.h>
#include <string.h>

int main()
{
	int arr1[10] = { 0 };
	int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr2) / sizeof(arr2[0]);
	memmove(arr1, arr2, 4 * sizeof(arr2[0]));
	for (int i = 0; i < sz; i++)
		printf("%d ", arr1[i]); //1 2 3 4 0 0 0 0 0 0
	return 0;
}

2.2 模拟实现

c 复制代码
#include <stdio.h>
#include <string.h>
#include <assert.h>

void* my_memmove(void* dest, const void* src, size_t num)
{
	void* p = dest;// 保存目标空间的起始地址,最后用于返回
	assert(dest && src);
	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 p;
}

int main()
{
	int arr1[10] = { 0 };
	int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr1) / sizeof(arr1[0]);
	my_memmove(arr2 + 3, arr2, 4 * sizeof(arr2[0]));
	for (int i = 0;i < sz; i++)
		printf("%d ", arr2[i]); //1 2 3 1 2 3 4 8 9 0
	return 0;
}

画图演示:
从后向前的例子:(上述代码的模拟实现)

从前向后的例子:


3. memset

项目 内容
函数名 memset
头文件 #include <string.h>
函数原型 void *memset(void *ptr, int value, size_t num);
功能 ptr 指向的内存位置开始,将连续 num 个字节的空间设置为指定的值
参数 ptr 指针,指向要设置的内存空间,也就是目标内存空间的起始地址
参数 value 要设置的值,函数会将 value 转换为 unsigned char 类型后进行设置
参数 num 要设置的内存长度,单位是字节
返回值 返回目标内存空间的起始地址,即 ptr
重要特点 memset 是按字节设置内存内容的 ,不是按数据类型逐个赋值
注意事项 可以安全地把内存设置为 0,但不能随意用它把 int 数组设置为 12 等非零整数

3.1 代码演示

c 复制代码
#include <stdio.h>
#include <string.h>

int main()
{
	char str[] = "abcdef";
	memset(str, 'x', 6);
	printf("%s\n", str); //xxxxxx
	return 0;
}

3.2 总结

当有⼀块内存空间需要设置内容的时候,就可以使⽤memset函数,值得注意的是memset函数对内存单元的设置是以字节为单位 的。
注意,不能随意用它把 int 数组设置为非零整数,结果会有问题:


4. memcmp

项目 内容
函数名 memcmp
头文件 #include <string.h>
函数原型 int memcmp(const void *ptr1, const void *ptr2, size_t num);
功能 ptr1ptr2 指向的内存位置开始,比较连续 num 个字节的内容
参数 ptr1 指针,指向第一块要比较的内存空间
参数 ptr2 指针,指向第二块要比较的内存空间
参数 num 要比较的字节数
返回值 < 0 第一处不同字节中,ptr1 指向的字节值 小于 ptr2 指向的字节值
返回值 = 0 两块内存在前 num 个字节内完全相同
返回值 > 0 第一处不同字节中,ptr1 指向的字节值大于 ptr2 指向的字节值
重要特点 memcmp 是按字节比较内存内容不是按数据类型整体比较

4.1 代码演示

c 复制代码
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	//                   04 00 00 00
	int arr2[] = { 1,2,3,1,5 };
	//                   01 00 00 00
	int r = memcmp(arr1, arr2, 5 * sizeof(arr1[0]));
	printf("%d\n", r); //1
	return 0;
}

画图演示:


4.2 总结

memcmp 是一个按字节比较两块内存内容的函数,可以指定比较长度,并通过返回值表示大小关系。memcmp函数在比较int数组的时候,memcmp是按字节进行比较,不是按int数值进行比较。


相关推荐
学会去珍惜4 小时前
C++如何与C语言混合编程_在C++项目中调用C库函数的extern “C“方法
c语言·c++·混合编程·extern
社交怪人5 小时前
【打印菱形】信息学奥赛一本通C语言解法(题号1028)
c语言·开发语言
csdn_aspnet13 小时前
C语言 Lomuto分区算法(Lomuto Partition Algorithm)
c语言·开发语言·算法
谙弆悕博士14 小时前
【附C源码】从零实现C语言堆数据结构:原理、实现与应用
c语言·数据结构·算法··数据结构与算法
三品吉他手会点灯19 小时前
C语言学习笔记 - 35.数据类型 - printf函数的非输出控制符与格式优化
c语言·开发语言·笔记·学习
Ghost Face...20 小时前
U-Boot SPL阶段与主阶段深度解析:从ROM到Kernel的完整引导之旅(ARMv8)
c语言
三品吉他手会点灯20 小时前
C语言学习笔记 - 33.数据类型 - printf函数的详细用法
c语言·开发语言·笔记·学习·算法
t-think1 天前
深入理解指针(2)
c语言·开发语言
我不是懒洋洋1 天前
从零开始实现一个简单的神经网络:C语言版
c语言