目录
前言
内存区域划分与分配
内存操作函数
memcpy()函数
函数简介
memcpy()函数的模拟实现
memmove()函数
函数简介
memmove()函数的模拟实现
memcmp()函数
函数简介
memcmp()函数的模拟实现
memset()函数
函数简介
memset()函数的模拟实现
1.栈区(stack)- 程序运行时由编译器自动分配,存放函数的参数值,局部变量的值;
函数内的局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放;
2.堆区(heap)- 在内存开辟另一块存储区域;程序运行时用malloc() ,calloc(), realloc()或new申请任意多少的内存,程序员自己负责何时用free或delete释放内存;
3.静态区(static) - 编译器编译时即分配内存,用于存放全局变量和静态变量;这块内存在程序整个运行期间都存在;
4.文字常量区 - 用于存放常量字符串,程序结束后由系统释放;
5. 程序代码区 - 存放函数体的二进制代码;
通过访问地址的方式操作计算机内存的C语言内置函数;
参数解读:
1.memcpy()函数从指针变量source指向的位置开始拷贝num个字节到指针变量destnation指向的内存空间,并且返回目标空间的起始地址;
//memcpy()函数头文件 #include
# include
int main()
{
int arr1[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int arr2[5] = { 0 };
//一个整型为4个字节,5个整型元素即为20个字节;
memcpy(arr1, arr2, 20);
int i = 0;
int sz = sizeof(arr1) / sizeof(arr1[0]);
for (i = 0; i < sz; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
运行结果:
2. memcpy()函数遇到 '\0'并不会停止拷贝;
int main()
{
char arr1[8] = "abcdef";
//arr2数组存放5个元素 x x \0 x \0
char arr2[] = "xx\0x";
memcpy(arr1, arr2, 4);
int i = 0;
int sz = sizeof(arr1) / sizeof(arr1[0]);
for (i = 0; i < sz; i++)
{
printf("%c", arr1[i]);
}
return 0;
}
运行结果:
3. 如果指针source指向的那块空间与指针destination指向的那块空间重叠,此时使用memcpy()函数,因为需要指定的字节数是未知的,可能拷贝的数据会覆盖掉原先的数据,导致结果超出预期;
对于同一块内存空间,当我们将 1, 2, 3, 4, 5拷贝到 3,4,5,6,7,我们期望的结果是1 2 1 2 3 4 5 8 9 10,但是我们将数字1挪动到数字3的位置,数字2挪动到数字4的位置,此时3的内容被修改,4的内容也被修改,此时输出结果为 1 2 1 2 1 2 1 8 9 10;
实现思路:
1. 实现任意类型数据的拷贝,参数设置为void*用于接收任意类型数据,源头数据不需要修改内容,用const修饰,返回目标空间的起始地址,也用void*接收;
2. 将src指向的数据拷贝到dest所指向的目标空间,内存的最小单元是一个字节,将任意类型的指针强制转换为char*,每次拷贝一个字节,从前到后拷贝,每次拷贝结束后,(char*)指针变量+1跳过一个字节,循环往复,拷贝的次数由指针跳过多少字节即字节数决定;
# include
void* my_memcpy(void* dest, const void* src, size_t num)
{
assert(dest != NULL);
assert(src != NULL);
//记录起始位置地址
void* ret = dest;
while (num--)
{
*((char*)dest) = *((char*)src);
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
参数解读:
1.memmove()函数从指针变量source指向的位置开始拷贝num个字节到指针变量destnation指向的内存空间,并且返回目标空间的起始地址;memmove()函数处理源内存块和目标内存块是可以重叠的;
2.源内存块和目标内存块,使用memmove函数处理;
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
memmove(arr + 2, arr, 20);
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
运行结果:
情形一:
对于同一块内存空间,当我们将 1, 2, 3, 4, 5拷贝到 3,4,5,6,7,从前向后拷贝,会覆盖掉原先的数据,那么从后向前拷贝呢?先把5挪到7的位置,4挪动到6的位置,3挪动到5的位置,2挪动到4的位置,1挪动到3的位置,此时结果为1 2 1 2 3 4 5 8 9 10,我们发现从后向前拷贝并不会覆盖掉原先的数据;
情形二:
对于同一块内存空间,当我们将 3, 4, 5,6,7 拷贝到 1,2,3,4,5的位置,期望的输出结果是:3 4 5 6 7 6 7 8 9 10;当我们开始从后向前拷贝,将7挪动到5的位置,6的位置挪动到4的位置,此时原来数字5,4已经被数字7,6覆盖掉,输出结果:7 6 7 6 7 6 7 8 9 10;但是从前向后拷贝呢?数字3挪动到数字1的位置,数字4挪动到数字2的位置,数字5来到数字3的位置,数字6来到数字4的位置,数字7挪动到5的位置,输出结果:3 4 5 6 7 6 7 8 9 10;从前向后拷贝并不会覆盖原先的数据;
总结: 数组在内存中是连续存放的,并且随着下标的变化地址由低到高变化,
当src小于dest时,从后向前逐个字节的拷贝;
当src大于dest时,从前向后逐个字节的拷贝;
void* my_memmove(void* dest, const void* src, size_t num)
{
assert(dest != NULL);
assert(src != NULL);
void* ret = dest;
//从前向后拷贝
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;
}
参数解读:
将ptr1所指向的内存块与ptr2所指向的内存块的前num个字节进行比较,按照字节进行比较
(每个字节均解释为unsigned char);
比较俩个内存块中第一对字节,如果彼此相等,继续比较下一对字节的大小,直到字节大小不同或者指定字节数比较完成;
返回值小于0,表示俩个内存块中不相等的第一对字节,ptr1中的值小于ptr2中的值;
返回值等于0,指定比较的字节数的内存块内容相同;
返回值大于0,表示俩个内存块中不相等的第一对字节,ptr1中的值大于ptr2中的值;
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcd";
int ret = memcmp(arr1, arr2, 4);
printf("%d ", ret);
return 0;
}
运行结果:0
注:使用memcmp()函数比较字符串时在'\0'处不会停止比较
int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
assert(ptr1 != NULL);
assert(ptr2 != NULL);
while (num--)
{
//强转为char*,进行逐字节比较
if (*(char*)ptr1 != *(char*)ptr2)
{
return *(char*)ptr1 - *(char*)ptr2;
}
ptr1 = (char*)ptr1 + 1;
ptr2 = (char*)ptr2 + 1;
}
return 0;
}
参数解读:
1.memset()函数是一个初始化函数,作用是将ptr所指向的内存块中前num个字节设置为指定的value值,逐字节设置value值,返回这块内存块的起始地址;
# include
int main()
{
//以16进制表示前俩个数字,从指定的起始位置开始将8个字节设置为0;
int arr[10] = { 0x11111111, 0x22222222, 3, 4, 5, 6, 7, 8, 9, 10 };
int*p=memset(arr, 0, 8);
return 0;
}
内存窗口:
2.memset()函数是按照字节对内存块初始化,但是设置的值value是int类型4个字节,所以memset()函数只能取value值的后8位给输入范围的每个字节,即无论c多大只有后8位二进制是有效的;所以可以设置的value值的实际范围为0-255;
int main()
{
//-1的补码: 11111111 11111111 11111111 11111111
int arr[10] = { 0x11111111, 0x22222222, 3, 4, 5, 6, 7, 8, 9, 10 };
int*p=memset(arr, -1, 8);
return 0;
}
内存窗口:
//1023的补码: 00000000 00000000 00000011 11111111
int main()
{
int arr[10] = { 0x11111111, 0x22222222, 3, 4, 5, 6, 7, 8, 9, 10 };
int*p=memset(arr, 1023, 8);
return 0;
}
内存窗口:
void* my_memset(void* ptr, int value, size_t num)
{
assert(ptr != NULL);
void* ret = ptr;
while (num--)
{
*((char*)ptr) = value;
ptr=(char*)ptr + 1;
}
return ret;
}