讲解目录
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
memcpy(不重复地址拷贝模拟实现)
语法格式
在C语言中,`memcpy` 是一个标准库函数,用于在内存之间复制数据。它的原型定义在 `<string.h>` 头文件中。`memcpy` 的语法格式如下:
```c
void *memcpy(void *destination, const void *source, size_t num);
```
参数说明:
- `destination`: 指向目标内存块的指针,即将要复制数据到的位置。
- `source`: 指向源内存块的指针,即数据的起始位置。
- `num`: 要复制的字节数。
`memcpy` 函数会从 `source` 指向的内存位置开始,复制 `num` 个字节到 `destination` 指向的内存位置。请注意,`memcpy` 不会检查目标内存是否足够大以容纳要复制的数据,这可能会导致未定义行为,如内存越界。
示例用法:
```c
#include <stdio.h>
#include <string.h>
int main() {
int src[] = {1, 2, 3, 4, 5};
int dest[10];
// 复制数组 src 的前 5 个元素到 dest
memcpy(dest, src, 5 * sizeof(int));
// 打印结果
for (int i = 0; i < 5; i++) {
printf("%d ", dest[i]);
}
printf("\n");
return 0;
}
在这个例子中,`memcpy` 被用来复制 `src` 数组的前 5 个元素到 `dest` 数组中。
具体说明
memcpy这个是拷贝内存快 什么都能拷贝
函数的返回类型是void*
void*类型也就说明 计算的时候是需要强制类型转化的 因为void的类型是无法计算的
使用规则 num 拷贝长度 这里是长度的大小是字节的大小,因为是地址的拷贝,也就是有可能是拷贝字符,这样的情况下只能是拷贝一个字节一个字节拷贝的情况下才能完成。
这里arr1 和arr2不要有内存的重叠
函数的模拟实现
src源头
num长度
这里不能写成int* 因为int*也就是说明类型固定
所以只能是void*(这里针对各种类型的类型)这里可以加上const
这里说明返回目标空间的起始地址
图解
这里是20个字节 这个数组是20个元素 80个字节 不能理解错了 这个数组不是20个字节
这里还是一个字节一个字节的拷贝
20个字节传递5个元素
这里使用char*指类型的指针解应用是最好的选择
也就这里是一个字节一个字节的交换 相当于strstr
完整代码
这里强制类型转化是不行的 因为强制类型转化是临时的
除非这样
版本2
这里说明 已经放1 2 了此时3 准备放到5的时候 此时 3 本身的位置变成1 所以也就是1 2 1 2 1 2 1 8 9 10
这里可以加上const 在source里面 因为source是被拷贝的地址 我们是不希望进行改变的
代码总结
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>
void* my_memcpy(void* dest, void* src, size_t num)
{
assert(dest && src);
while (num--)
{
*(char*)dest = *(char*)src;//void类型是不能进行计算的 只能进行强制类型转化 要转化成char*类型的原因是 一方面这个是指针 另一方面这个是按照字节大小进行计算拷贝的
dest = (char*)dest + 1;
src = (char*)src + 1;//或者写成这样 这里强制类型转化的情况下
//src=((char*)(src))++;这样的话可以++
}
}
void* MY_memcpy(void* str1, void* str2, size_t num)
{
assert(str1 && str2);
while (num--)//这里是每次计算的时候减少一个字节 所以会直到为0 的时候自己跳出循环
{
*(char*)str1 = *(char*)str2;//进行强制类型转化的原因是因为这里是void类型 void类型是无法参与计算的 所以要进行强制类型转化
str1 = (char*)str1 + 1;//这里不能和拷贝库函数一样直接++ 因为void是无法参与计算的
str2 = (char*)str2 + 1;
}
}
int main()
{
char arr1[] = "sdfhilbgjju ";
char arr3[100] = { 0 };
memcpy(arr3, arr1, 20);//这里是拷贝空间和被拷贝空间 num是字节大小 不是
printf("%s \n", arr3);
int arr2[] = { 1,2,3,4,5,6,7,8,9 };
int arr4[100] = { 0 };
memcpy(arr4, arr2, 20);
for (int i = 0; i < 20; i++)
{
printf("%d ", arr4[i]);
}
printf("\n");
//模拟memcpy函数
int arr5[] = { 1,2,3,4,5,6,7,8,9 };
int arr6[100] = { 0 };
my_memcpy(arr6, arr5, 20);
for (int i = 0; i < 20; i++)//这里的20 不是二十字节 而是二十个数值
{
printf("%d ", arr6[i]);//这里是直接打印数组
}
printf("\n");
//二次模拟memcpy函数
int str1[] = { 1,2,3,4,5,6,7,8,9 };
int str2[100] = { 0 };
MY_memcpy(str2, str1, 20);
for (int i = 0; i < 10; i++)
{
printf("%d ", str2[i]);
}
return 0;
}
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
memmove(重复地址拷贝模拟实现)
语法讲解
C语言中的`memmove`函数是一个非常有用的内存操作函数,它可以将一段内存内容从一个位置移动到另一个位置,即使在源内存和目标内存有重叠的情况下,它也能正确地完成内存复制工作。
函数原型定义如下:
```c
void *memmove(void *dest, const void *source, size_t n);
```
参数说明:
- `dest`:指向目标内存的指针,这里将复制数据到这个地址空间。
- `source`:指向源内存的指针,这里是要复制的数据所在的位置。
- `n`:要复制的字节数。
返回值:
- 函数成功执行后,返回指向`dest`的指针。
- 如果参数指向的内存区域非法,则返回`NULL`。
`memmove`的工作原理是,如果源地址和目标地址是不重叠的,那么它就像`memcpy`函数一样工作,直接复制数据。如果源地址和目标地址重叠,`memmove`会采取一种高效的方法,从后向前复制数据,以避免在复制过程中覆盖数据。
使用示例:
```c
#include <stdio.h>
#include <string.h>
int main() {
char source[] = "Hello, World!";
char dest[20];
// 确保目标数组有足够的空间来存放源字符串
strcpy(dest, source);
// 使用memmove将source字符串移动到dest数组的前面
memmove(dest, source, strlen(source) + 1);
printf("Copied string: %s\n", dest);
return 0;
}
```
在上面的示例中,我们首先用`strcpy`将字符串复制到`dest`数组,然后使用`memmove`将`source`数组的内容移动到`dest`数组的前面。注意,这里我们使用了`strlen`函数来获取字符串长度,并加上1来确保还包括字符串结束符`\0`。
需要注意的是,在使用`memmove`时,要确保移动操作不会导致内存访问越界,特别是在处理动态分配的内存或者指针运算时要格外小心。
memcpy和memmove的区别
memcpy的逻辑
memmove的逻辑
这个函数和memcpy其实是差不多的 ,本质上都是拷贝函数地址从而达到目的,但是需要知道的是memcpy是不能完成重复函数的拷贝的 什么意思呢 简单的说就是,重叠就交给memove不成重叠交给memcpy。
第一种情况 把同一个数组里面的 1 2 3 拷贝到2 3 4 里面去这个时候就会产生问题 也就是
我们需要的是 1 1 2 3 5 6 7 8
但是实际我们得到的可能是 1 1 1 1 5 6 7 8
也就是1 拷贝给2,2 变成1,2->1拷贝给3,此时3也变成1,但是实际我们需要的是1 1 2 3 5 6 7 8所以是不能满足目的的,如果有些编译器可以满足拷贝要求 ,那更大的可能就是,memcpy和memmove函数一样。
也就是
1
2
图解(原理讲解)
也就是此时存在两种情况,一种是被拷贝空间在拷贝空间前面,一种是被拷贝空间在拷贝空间后面, 这个时候我们提出两种解决方式,也就是一种是从前向后拷贝,一种是从后向前拷贝
1,需要把前面的空间拷贝到后面去,并且不能覆盖和重复
当被拷贝函数在前面的时候,我们分析两种图解
第一种是从前向后拷贝(这里的从前向后拷贝是第一个拷贝到最后一个)(不满足)
第二种是从后向前拷贝(这里的从后向前拷贝是被拷贝函数的从最后一个元素拷贝到第一个元素)(满足)
2当被拷贝函数在后面的时候,我们分析两种图解
这里也是两种情况
第一种从前往后拷贝(满足)
第二种从后往前拷贝 不满足
画图总结
地址是从低地址到高地址
也就是
低----------------------------------------->高
拷贝重复地址的模拟实现
这个不像memcpy 拷贝不重复地址 memcpy就算完成任务 也是超额完成
这里的完成任务不算超额完成
5*sizeof(int) 这里是20个字节大小
这里是 sizeof(int)四个字节 这里传递的是字节
模拟函数
图解
把1 2 3 4 5 拷贝到 3 4 5 6 7
版本1(不是最优解)
版本2(最优解)
忘记知识是一个客观事实 但是思考方式是不会忘记的
地址从低地址到高地址
第二个是从前到后
第一个是从后到前
所以
代码总结
#define _CRT_SECURE_NO_WARNINGS 1
#include<assert.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
void* my_memmove(void* dest,const void* src, size_t num)
{
assert(dest && src);
if (src > dest)
{
while (num--)
{
*(char*)dest = *(char*)src;
src = (char*)src + 1;
dest = (char*)dest + 1;
}
}
else
{
while (num--)
{
//这里可以这样理解 也就是 本质还是每次许循环都是相等的
//但是因为是从后向前拷贝的 所以此时是
// 当 scr指向8 的时候 他的最后数值的时候
// dest刚好指向的也是最后一个数值
// 因为是首元素地址+num 这个num是20个字节 也就是武哥元素 也就是同时他们指向的都是各自需要交换的最后一个字节
//最后再每次进行-- 直到循环结束 此时结束循环
*((char*)dest + num) = *((char*)src + num);
}
}
}
void* MY_memmove(void* dest, const void* scr, size_t num)
{
assert(dest && scr);
//首先我们需要知道 地址是从低地址到高地址进行的
// 也就是我们需要知道 什么时候scr 小于dest 什么时候大于dest 根据这个计算 就可以得到想要的结果
// 我们需要知道 scr 是被拷贝的空间 dest 是拷贝的空间
if (scr > dest)
{
while (num--) //这里还是每次减少一个字节 这里是被拷贝空间在拷贝空间后面 所以其实也发现并没有什么变化 那么也是可以把memcpy的函数直接拿过来
{
*(char*)dest = *(char*)scr;
scr = (char*)scr + 1;
dest = (char*)dest + 1;
}
}
else//这里包含小于等于 也可以不包含等于 这里是scr<dest也是就需要从后往前进行打印
{
while (num--)
{
*((char*)dest + num) = *((char*)scr + num);//这里的意思是
}
}
}
int main()
{
char arr1[] = "dfskbjh ";
char arr2[100] = { 0 };
memmove(arr2, arr1, 7);
printf("%s \n", arr2);
char arr3[] = "dfskbjh ";
char arr4[100] = { 0 };
memmove(arr4, arr3, 5 * sizeof(char));
printf("%s \n", arr4);
int arr5[] = { 1,2,3,4,5,6,7,8,9,1 };
int arr6[100] = { 0 };
memmove(arr6, arr5, 20);//这里是把不重复地址复制到arr6里面 20个字节也就四个数值
for (int i = 0; i < 20; i++)
{
printf("%d ", arr6[i]);
}
printf("\n");
int arr7[] = { 1,2,3,4,5,6,7,8,9,1 };
memmove(arr7, arr7 + 5, 5 * sizeof(int));
for (int i = 0; i < 10; i++)
{
printf("%d ", arr7[i]);//这里是直接打印数组
}
printf("\n");
int arr8[] = { 1,2,3,4,5,6,7,8,9,1 };
memmove(arr8 + 2, arr8, 5 * sizeof(int));
for (int i = 0; i < 10; i++)
{
printf("%d ", arr7[i]);//这里是直接打印数组
}
printf("\n\n\n");
int arr9[] = { 1,2,3,4,5,6,7,8,9,1 };
my_memmove(arr9 + 2, arr9, 12);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr9[i]);//这里是直接打印数组
}
printf("\n\n\n");
int str1[] = { 1,2,3,4,5,6,7,8,9,1 };
MY_memmove(str1, str1+2, 20);
for (int i = 0; i < 10; i++)
{
printf("%d ", str1[i]);
}
printf("\n\n\n");
return 0;
}
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
memset(改变数值函数)
函数的语法
(第几个元素,改变成什么元素,几个字节)
`memset`函数是C语言标准库函数之一,用于将内存中的某一块区域全部设置为某个特定的值。它定义在`<string.h>`头文件中。`memset`函数通常用于初始化内存或者将一块内存区域的内容全部置为某个特定的数字,比如置零或置某个特定值。
函数原型如下:
```c
void *memset(void *s, int c, size_t n);
```
参数说明:
- `s`:指向要填充的内存块的指针。
- `c`:要设置的值,该值以`int`类型传递,但是函数会将该值转换为`unsigned char`类型,并把内存中的每个字节均设置为此值。
- `n`:要填充的字节数。
返回值:
- 函数返回指向`s`的指针。
举例说明:
```c
#include <stdio.h>
#include <string.h>
int main() {
int arr[10];
// 初始化数组
memset(arr, 0, sizeof(arr));
// 打印数组元素,应该全部为0
for (int i = 0; i < 10; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
在上面的例子中,我们定义了一个大小为10的整型数组`arr`,使用`memset`函数将数组的每个字节都设置为0。在大多数情况下,这会初始化整个数组为零。
需要注意的是,`memset`是按字节操作的,所以当需要将数组的每个元素都设置为同一个值时,需要确保该值在一个字节中可以表示。例如,如果想要将一个整型数组的每个元素都设置为同一个整数值,就需要考虑该值占用的位数,以及如何将其映射到一个字节的值。
此外,对于需要设置为特定值大小的数组,使用`memset`进行初始化是一种简单有效的方法。然而,在某些情况下,特别是涉及到多字节数据类型(如`char`、`short`、`int`等)时,可能需要结合其他函数如`memcpy`来确保数据的正确初始化。
函数的使用
字符方面
从第三个字符开始进行改变
举例2
所以这里是20个字节全是1
这里需要知道的是,这存储的时候是按照字节为单位进行存储的,所以这里的改变20个字节
其实是不准确的
但是要全部改成0 ,那是没有问题的,也不容易出错
代码
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = "hello word";//
memset(arr, 'a', 7);//这里是 arr数组 第一个元素开始,修改成a,往后七个字节
printf("%s\n", arr);//打印这个函数 因为是对内存直接进行操作所以 打印这个arr也就是直接打印修改过的数值
return 0;
}
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
memcmp(内存块的比较)
语法
`memcmp`函数是C语言标准库中的一个函数,用于比较两个内存块的内容是否相等。它定义在`<string.h>`头文件中。`memcmp`函数在比较两个字符串或者任何内存数据时非常有用,它不会检查字符串的长度,只是逐字节比较直到找到一个不同的字符或者比较完所有的字节。
函数原型如下:
```c
int memcmp(const void *s1, const void *s2, size_t n);
```
参数说明:
- `s1`:指向第一个内存块的指针。
- `s2`:指向第二个内存块的指针。
- `n`:要比较的字节数。
返回值:
- 如果`s1`和`s2`所指向的内存内容完全相同,则返回`0`。
- 如果内存内容不同,`memcmp`会返回第一个不同的字节的位置,以`0`为基础。也就是说,如果`s1`中的某个字节与`s2`中的对应字节不同,`memcmp`会返回该字节在`s1`中的偏移量(以字节为单位)。
- 如果`n`为0,则返回`0`。
举例说明:
```c
#include <stdio.h>
#include <string.h>
int main() {
char str1[] = "Hello";
char str2[] = "Hello";
char str3[] = "hello";
// 比较str1和str2是否相等
int result1 = memcmp(str1, str2, strlen(str1));
printf("str1 and str2 are %s\n", result1 == 0 ? "equal" : "not equal");
// 比较str1和str3是否相等
int result2 = memcmp(str1, str3, strlen(str1));
printf("str1 and str3 are %s\n", result2 == 0 ? "equal" : "not equal");
return 0;
}
```
在上面的例子中,我们定义了三个字符串,分别比较`str1`和`str2`,以及`str1`和`str3`。由于`str1`和`str2`完全相同,`memcmp`返回`0`,表示它们相等。而`str1`和`str3`只有大小写不同,所以`memcmp`会返回第一个不同字节的偏移量,因此`str1`和`str3`不相等。
`memcmp`函数在处理二进制数据时也非常有用,例如,在比较两个文件或者两个数据块是否相同时。在这种情况下,不需要关心数据的长度,只需要比较它们的内容。
内存的比较 这里还是
(地址,地址,比较几个字节)
其实这里和 这个函数很类似(理解不深入的可以看看这个代码 其实都一样)C语言-strncmp strncat strncpy长度受限制的字符串函数-CSDN博客https://blog.csdn.net/Jason_from_China/article/details/136724412
具体举例
这里比较到低17个字节的时候也就是不一样的 上面的 小 所以 返回的数值是-1
num的意思是最多比较那麽多的大小 要是在num之前就已经比较出来 那就会返回数值
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "sdfhnjk";
char arr2[] = "sdfjikh";
int ret = memcmp(arr1, arr2, 20);
if (ret == 0)
{
printf("相等");
}
else
{
printf("bu相等");
}
return 0;
}