IE盒子

搜索
查看: 130|回复: 0

【C语言】<string.h>中四大内存操作函数(用法+模拟实现)

[复制链接]

1

主题

7

帖子

8

积分

新手上路

Rank: 1

积分
8
发表于 2023-1-15 11:57:20 | 显示全部楼层 |阅读模式
头文件中常用内存操作函数共有四大,学习完本篇文章,各种类型数组的常见处理轻松拿下。
零、前言

对字符串(字符数组)的操作函数有很多,但是我们想要操作整型数组等呢:
这就需要内存操作函数了,memory在计算机科学中是内存的意思,这也是四大内存操作函数都有mem头的原因。
与void*类型指针重要的知识:
void*类型指针可以指向任何类型的数据,但是void*类型指针无法访问地址数据。这是因为指针压根就不知道它要访问多大空间,那么即使能访问得到的数据也毫无意义。因此不能对void*类型指针解引用操作,也不能做地址偏移+-操作,这是语法型错误。
本文提及的字符串相关函数不熟悉的可以查看文章【C语言】<string.h>中十大字符串函数(用法+模拟实现)。
一、memcpy 内存拷贝函数

我们知道strcpy(字符串拷贝函数)可以将一个字符串内容拷贝到另一个字符串,如果我们想拷贝整型数组,我们可以使用memcpy内存拷贝函数。
memcpy功能:逐字节地拷贝num个字节内存,并会覆盖原来内容。
memcpy函数声明:
void * memcpy ( void * dst, const void * src, size_t num );src(source)源内存首字节地址,dst(destination)目标内存首字节地址。
num是需要拷贝的字节数。
memcpy返回的是dst首字节地址,并且是void*型,接收返回值需要强制类型转换。
memcpy使用实例:
#include <stdio.h>
#include <string.h>
int main()
{
    int arr1[10] = { 0 };
    int arr2[10] = { 1,2,3,4,5,6,7,8,9,10 };
    memcpy(arr1, arr2, 4 * sizeof(arr2[0]));
    for (int i = 0; i < 10; i++)
        printf("%d ", arr1);
    printf("\n");
    memcpy(arr1+2,arr1,4*sizeof(arr1[0]));
    //理想拷贝后效果应该是1 2 1 2 3 4 0 0 0 0
    //实际输出是1 2 1 2 1 2 0 0 0 0 因为原来的3 4被提前覆盖了
    for (int i = 0; i < 10; i++)
        printf("%d ", arr1);
    return 0;
}注意:dst和src都要有超过num个字节的空间。
源内存块src和目标内存块dst有任何的重叠,复制的结果都是未定义的。



代码输出实例

my_memcpy

void *my_memcpy(void *dst, const void *src, int num)
{
    //将void*型强制转化成char*,能够逐字节访问
    char *tmp_dst = (char *)dst;
    const char *tmp_src = (const char *)src;
    //循环num次,拷贝num个字节
    for (int i = 0; i < num; i++)
    {
        *tmp_dst++ = *tmp_src++;
    }
    return dst;
}二、memmove 内存移动函数

memmove和memcpy的差别就是处理的源内存块和目标内存块是可以重叠的。
memmove功能:将源内存块num个字节移动到目标内存块,源内存块内容并不消失。
memmove函数声明:
void * memmove ( void * dst, const void * src, size_t num );src(source)源内存首字节地址,dst(destination)目标内存首字节地址。
num是需要拷贝的字节数。
memmove返回的是dst首字节地址,并且是void*型,接收返回值需要强制类型转换。
memmove使用实例:
#include <stdio.h>
#include <string.h>
int main()
{
    int arr1[10] = { 0 };
    int arr2[10] = { 1,2,3,4,5,6,7,8,9,10 };

    memmove(arr1, arr2, 4 * sizeof(arr2[0]));
    printf("arr1:");
    for (int i = 0; i < 10; i++)
        printf("%d ", arr1);

    printf("\narr2:");
    for (int i = 0; i < 10; i++)
        printf("%d ", arr2);

    //理想拷贝后效果应该是1 2 1 2 3 4 0 0 0 0
    printf("\narr1自己移动自己:");
    memmove(arr1 + 2, arr1, 4 * sizeof(arr1[0]));
    for (int i = 0; i < 10; i++)
        printf("%d ", arr1);

    return 0;
}注意:dst和src都要有超过num个字节的空间。



代码输出实例

my_memmove

memmove的模拟关键在于解决内存块重复部分移动问题。
首先要比较dst和src两个地址大小,原因如下:

  • 如果要将1 2 3 4 5 6 7 8的src 1 2 3 4移动到dst 3 4 5 6位置,就要从4开始倒着移动,才能避免重叠部分移动错误。
  • 如果要将src 3 4 5 6移动到dst 1 2 3 4位置,就要从3开始正着移动。
  • 如果不是重叠内存块拷贝,正着倒着移动都一样,地址位置判断并不影响。
void *my_memmove(void *dst, const void *src, int num)
{
    char *tmp_dst = (char *)dst;
    const char *tmp_src = (const char *)src;

    if (src > dst)
        for (int i = 0; i < num; i++)
            *tmp_dst++ = *tmp_src++;
    else
        for (int i = num-1; i >=0; i--)
            *(tmp_dst + i) = *(tmp_src + i);

    return dst;
}三、memset 内存赋值函数

memset功能:给一片内存num个字节赋上指定值
memset函数声明:
void * memset ( void * ptr, int value, size_t num );ptr是内存块首字节地址。
value是要赋的值,这个值应该不超过一个无符号字节大小即2^8^-1
num是需要赋值的字节数。
memset返回值是ptr首字节地址,并且是void*型,接收返回值需要强制类型转换。
memset使用实例:
#include <stdio.h>
#include <string.h>
int main()
{
    char carr[10] = { 0 };
    memset(carr, 65, sizeof(carr));
    printf("字符型:");
    for (int i = 0; i < 10; i++)
        printf("%c ",carr);

    int darr[4] = { 0 };
    printf("\n整型:");
    memset(darr, 1,sizeof(darr));
    for (int i = 0; i < 4; i++)
        printf("%d ", darr);
    return 0;
}注意:内存块要有num个字节大小。
给每个字节赋值,比如int型是4字节,赋值1,输出是16843009而不是1。



代码输出实例

my_memset

void *my_memset(void *ptr, int value, int num)
{
    //强制类型转换为无符号char*型
    unsigned char *tmp_ptr = (unsigned char *)ptr;
    //循环num次
    for (int i = 0; i < num; i++)
    {
        //因为char是特殊的整型,所以可以直接赋值
        *tmp_ptr++ = value;
    }

    return ptr;
}四、memcmp 内存比较函数

memcmp功能:逐字节地比较从ptr1和ptr2开始的num个字节,直至比出。
memcmp函数声明:
int memcmp ( const void * ptr1, const void * ptr2, size_t num );ptr1是一个内存块首字节地址,ptr2是另一个内存块首字节地址。
num是需要比较的字节数。
ptr1>ptr2返回值>0,ptr1<ptr2返回值<0,完全相等返回值0。
memcmp使用实例:
#include <stdio.h>
#include <string.h>
int main()
{
    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int arr2[10] = { 1,2,3,4,5,7,7,7,7,7 };
    if (memcmp(arr1, arr2, sizeof(arr1)) > 0)
        printf("arr1大");
    else if (memcmp(arr1, arr2, sizeof(arr1)) < 0)
        printf("arr2大");
    else
        printf("arr1和arr2一样大");
    return 0;
}注意:内存块要有num个字节大小。



代码输出实例

my_memcmp

int my_memcmp(const void *ptr1, const void *ptr2, int num)
{
    //先强制类型转换
    const char *tmp_ptr1 = (const char *)ptr1;
    const char *tmp_ptr2 = (const char *)ptr2;
    //循环num次,比较出就返回值
    for (int i = 0; i < num; i++)
    {
        if (*(tmp_ptr1+i) > *(tmp_ptr2+i))
            return 1;
        else if (*(tmp_ptr1+i) < *(tmp_ptr2+i))
            return -1;
    }
    //比较完num个字节了,仍没返回,完全相等返回值0。
    return 0;
}<hr/>熟练掌握库函数,敲码速度倍倍翻。
码字不容易,欢迎关注、点赞、收藏、评论、转发。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表