模拟实现memmove,memcpy,memset
目录
前言
一、模拟实现memmove
代码演示:
二、模拟实现memcpy
代码演示:
三、模拟实现memset
代码演示:
总结
前言
这篇文章主要讲解了库函数的模拟实现,包含memmove,memcpy,memset
一、模拟实现memmove
memmove函数是C语言标准库中的一个函数,用于从一个内存位置复制数据到另一个内存位置。这个函数在处理重叠内存区域时特别有用,因为它能够在源区域被覆盖之前将重叠区域的字节拷贝到目标区域中。如果没有重叠,memmove的行为与memcpy相同。
函数声明和参数
memmove函数的声明如下:
void *memmove(void *str1, const void *str2, size_t n)
其中,参数str1是指向目标数组的指针,str2是指向数据源的指针,而n是要复制的字节数。
返回值
函数返回一个指向目标存储区str1的指针。
memmove函数的实现关键在于如何处理内存重叠的情况。如果源地址和目标地址重叠,memmove会从后向前复制数据,以避免覆盖还未复制的数据。如果源地址在目标地址之后,它会从前向后复制数据。这种策略确保了数据的正确复制,即使在内存区域重叠的情况下。
代码演示:
#include <stdio.h>void* my_memmove(void* dest, const void* src, size_t n) {unsigned char* d = dest;const unsigned char* s = src;if (d > s) {for (size_t i = n; i > 0; --i) {d[i - 1] = s[i - 1];}} else {for (size_t i = 0; i < n; ++i) {d[i] = s[i];}}return dest;
}int main() {char str1[20] = "Hello, World!";char str2[20] = {0};printf("Before memmove: %s\n", str2);my_memmove(str2, str1, 13);printf("After memmove: %s\n", str2);return 0;
}
my_memmove 函数的返回值是 void* 类型,通常用于指向目标内存区域的指针。返回 ret 使得调用者可以获得目标内存的起始地址。这在某些情况下是有用的,尤其是在链式调用或需要确认目标地址的情况下。
#include <stdio.h>
#include <string.h>
#include <assert.h>void* my_memmove(void* dest, const void* src, size_t num) {assert(dest && src);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;
}int main() {int a[] = { 5,5,88,5,22,33,8,8,9,9,33,83,9,8,98 };my_memmove(a+2,a,20);for (int i = 0; i < 15; i++)printf("%d ", a[i]);return 0;
}
二、模拟实现memcpy
memcpy 是一个用于内存拷贝的函数,其功能是从源内存地址的起始位置开始拷贝若干个字节到目标内存地址中。
其函数原型为:
void *memcpy(void *dest, const void *src, size_t n);
该函数的作用是将源地址 src 指向的内存区域的 n 个字节复制到目标地址 dest 指向的内存区域。
下面是一个基本的 memcpy 实现,按字节进行拷贝:
代码演示:
void *my_memcpy(void *dst, const void *src, size_t n) {if (dst == NULL || src == NULL) {return NULL;}char *pdst = (char *)dst;char *psrc = (char *)src;while (n--) {*pdst++ = *psrc++;}return dst;}
这个实现简单且直接,但没有考虑内存重叠的情况12。
考虑内存重叠
为了处理内存重叠的情况,我们需要判断源地址和目标地址的相对位置。如果目标地址在源地址之后,我们需要从高地址向低地址拷贝;否则,从低地址向高地址拷贝:
void *my_memcpy(void *dst, const void *src, size_t n) {if (dst == NULL || src == NULL) {return NULL;}char *pdst = (char *)dst;char *psrc = (char *)src;if (pdst > psrc && pdst < psrc + n) {pdst += n - 1;psrc += n - 1;while (n--) {*pdst-- = *psrc--;}} else {while (n--) {*pdst++ = *psrc++;}}return dst;}
这个实现考虑了内存重叠的情况,确保数据不会被覆盖13。
优化实现
为了提高拷贝速度,可以按 4 字节(或更大)为单位进行拷贝:
void *my_memcpy(void *dst, const void *src, size_t n) {if (dst == NULL || src == NULL) {return NULL;}int *pdst = (int *)dst;int *psrc = (int *)src;size_t num_ints = n / sizeof(int);size_t num_bytes = n % sizeof(int);if (pdst > psrc && pdst < (int *)((char *)psrc + n)) {pdst += num_ints - 1;psrc += num_ints - 1;while (num_ints--) {*pdst-- = *psrc--;}char *cdst = (char *)pdst;char *csrc = (char *)psrc;cdst += sizeof(int) - 1;csrc += sizeof(int) - 1;while (num_bytes--) {*cdst-- = *csrc--;}} else {while (num_ints--) {*pdst++ = *psrc++;}char *cdst = (char *)pdst;char *csrc = (char *)psrc;while (num_bytes--) {*cdst++ = *csrc++;}}return dst;}
这个实现通过按 4 字节为单位进行拷贝,提高了拷贝速度
#include <stdio.h>
#include <string.h>
#include <assert.h>void* my_memcpy(void* dest, const void* src, size_t num) {if (dest == NULL || src == NULL) {return NULL;}char* a = (char*)dest;char* b = (char*)src;while (num--) {*a++ = *b++;}return dest;
}int main() {int a[] = { 5,5,88,5,22,33,8,8,9,9,33,83,9,8,98 };my_memcpy(a + 2, a, 20);for (int i = 0; i < 15; i++)printf("%d ", a[i]);return 0;
}
三、模拟实现memset
memset函数是C语言中的一个标准库函数,主要用于给内存块进行初始化操作。它的作用是将某个给定的值填充或复制到指定内存的前n个字节中。这个函数非常适合用于初始化大型结构体或数组。
memset的函数原型如下:
void *memset(void *str, int c, size_t n)
其中,str是指向要填充的内存块的指针,c是要设置的值,通常是一个无符号字符,n是要被设置为该值的字节数。
一个常见的使用场景是初始化一个字符数组或者整型数组。例如,如果想要将一个整型数组的所有元素初始化为-1,可以使用以下代码:
int A[2];memset(A, -1, sizeof A);
这段代码会将数组A的每个元素都设置为-1。
注意事项
使用memset时需要注意,它按字节对内存块进行初始化。因此,如果你想用它来初始化一个整型数组,除了0和-1之外的其他值可能会导致不符合预期的结果。这是因为memset在赋值时是按字节赋值的,它会将c参数化成二进制之后填入一个字节。例如,如果你尝试使用memset给整型数组赋值100,你可能会得到一个意想不到的结果,因为100的二进制表示会被复制到整型的每个字节中,导致实际的数值并不是100。
memset初始化为无穷大
在某些算法中,我们可能需要将数组初始化为一个非常大的数,这时可以使用memset来实现。例如:
memset(a, 0x3f, sizeof a);
这段代码会将数组a的每个元素赋值为0x3f3f3f3f,这是一个非常大的数,接近于整型的最大值。
memset是一个非常有用的函数,它可以快速地初始化内存区域。但是,使用时需要注意它的赋值特性,尤其是在处理非字符类型的数据时。正确理解和使用memset可以避免一些常见的错误,并提高代码的效率和可读性。
代码演示:
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stddef.h> void* my_memset(void* dest, int value, size_t n) {/* unsigned char* p = (unsigned char*)dest;unsigned char c = (unsigned char)value;size_t align = 4; unsigned int block = c | (c << 8) | (c << 16) | (c << 24);unsigned int* wp = (unsigned int*)p;while (n >= align) {*wp++ = block;n -= align;}p = (unsigned char*)wp;while (n--) {*p++ = c;}return dest;*/unsigned char* p = (unsigned char*)dest;for (size_t i = 0; i < n; i++) {p[i] = (unsigned char)value;}return p;
}
int main() {int a[] = { 5,5,88,5,22,33,8,8,9,9,33,83,9,8,98 };char b[] = "djsjfksjfjsdijfoisjosij";my_memset(b, 'a', sizeof(b));for (int i = 0; i < 15; i++)printf("%c ", b[i]);return 0;
}
在这个例子中,我们定义了一个自定义的myMemset
函数来模拟标准库中的memset
函数。它接受一个指针ptr
,一个要填充的值value
,以及要填充的字节数num
。函数将指针ptr
指向的内存地址开始的连续num
字节设置为value
。
然后,就会发现出现了一堆
原因是什么呢,传入的val是 2。对于 int 类型的数组,实际上是在每个 int 的 4 个字节中都写入了 2,这会导致 int 的值变成 0x02020202(在小端系统中),而不是简单的 2。
解决办法
#include <stdio.h>
#include <string.h>
#include <assert.h>void* my_memset(void* ptr, int val, size_t num) {// 计算要设置的元素数量size_t count = num / sizeof(int);int* int_ptr = (int*)ptr;// 设置 int 数组for (size_t i = 0; i < count; i++) {int_ptr[i] = val; // 直接设置为 int 值}// 处理剩余的字节(如果有的话)unsigned char* byte_ptr = (unsigned char*)(int_ptr + count);for (size_t i = 0; i < num % sizeof(int); i++) {byte_ptr[i] = (unsigned char)val; // 设置剩余的字节}return ptr;
}int main() {int a[5] = { 5, 5, 88, 5, 22 }; // 只定义 5 个元素my_memset(a, 2, sizeof(a)); // 设置整个数组为 2for (int i = 0; i < 5; i++) {printf("%d ", a[i]); // 输出应该是 2 2 2 2 2}printf("\n");return 0;
}
但是好像不行
拼尽全力,无法战胜qwq
总结
关于本篇的内容就结束了,对你有帮助的可以点个赞