C语⾔内存函数

发布于:2025-09-12 ⋅ 阅读:(20) ⋅ 点赞:(0)

前言

本篇文章将讲解C语⾔内存函数知识的相关内容,为本章节知识的内容。

本篇文章涉及的C语⾔内存函数有:

1.memcpy函数

2.memmove函数

3.memset函数

4.memcmp函数

为本次讲解的知识。

一、 memcpy函数

1.介绍

void * memcpy ( void * destination, const void * source, size_t num );

  memcpy函数strcpy函数功能相似,不同的是memcpy 是完成内存块拷⻉的,不关注内存中存放的数据是什么,即将函数 memcpy 从source 的位置开始向后复制 num 个字节的数据到 destination 指向的内存位置。

注意:source 和 destination 有任何的重叠,复制的结果都是未定义的,更易懂的角度来说:

例:

int arr[5] = {1, 2, 3, 4, 5};
memcpy(arr+1, arr, 3*sizeof(int)); // 错误:目标区域 (arr+1) 与源区域 (arr) 重叠

memcpy不允许同名数组的复制。

destination :指针,指向⽬标空间,将拷⻉的数据存放在这⾥(归宿)。

source :指针,指向源空间,要拷⻉的数据从这⾥来(来源)。

num :从source要拷⻉的数据占据的字节数。

返回值:memcpy 函数返回的⽬标空间的起始地址。

使用例:

#include <stdio.h>
#include <string.h>
int main()
{
    int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
    int arr2[10] = { 0 };
    memcpy(arr2, arr1, 20);
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr2[i]);
    }
    return 0;
}

例子讲解:

本代码中,是将 arr1 的前20字节数据复制到 arr2 中,然后打印 arr2 的所有元素。

  • memcpy(arr2, arr1, 20) 表示复制 20字节 数据。由于每个 int 占4字节,因此实际复制 20 / 4 = 5 个元素。
  • 从 arr1 的起始地址复制5个元素:1, 2, 3, 4, 5
  • arr2 初始化为全0,因此未被复制的后5个元素保持为0。

所以可知打印时后5个值为0.

2.模拟实现:

#include <stdio.h>
#include <string.h>
void * mymemcpy ( void * dst, const void * src, int n )
{  void *ret=dst;
     while(n--)
{
    *(char*)dst=*(char*)src;
    dst=(char*)dst+1;
    src=(char*)src+1;
}
    return ret;
}
int main()
{
    int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
    int arr2[10] = { 0 };
    mymemcpy(arr2, arr1, 20);
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr2[i]);
    }
    return 0;
}

对该实现的讲解:

首先模拟实现要满足原函数的参数,返回值等,所以要满足void * memcpy ( void * destination, const void * source, size_t num );的型状。

因为返回值:memcpy 函数返回的⽬标空间的起始地址。

所以用void *ret=dst;语句来接⽬标空间的起始地址。

memcpy 是完成内存块拷⻉的,不关注内存中存放的数据是什么,拷贝的是num个字节,但不同类对应着不同的字节数,所以存在拷贝不完全的情况,而char类型1个字节数,可以完成1个1个复制拷贝的情况,所以   *(char*)dst=*(char*)src; 拷贝完一个字符,就该向下拷贝,但二者均为void *类,所以应该通过强转之后移动地址,所以会有  dst=(char*)dst+1;
    src=(char*)src+1; 语句,在结尾,完成返回值:memcpy 函数返回的⽬标空间的起始地址。 return ret;

  运行结果一样的,看实现代码,我们可以发现,memcpy不能实现同名数组的复制,如果我们想实现,则可以靠二、memmove函数

  二、memmove函数

1.介绍

void * memmove ( void * destination, const void * source, size_t num );

memmove函数也是完成内存块拷⻉的。

他与 memcpy函数功能一样,是完成内存块拷⻉的,不关注内存中存放的数据是什么,即将函数 memcpy 从source 的位置开始向后复制 num 个字节的数据到 destination 指向的内存位置。不过mommove可以正确处理目标空间与源空间重叠的情况。

源内存块和⽬标内存块是可以重叠的。

二者的参数相同,意义也一样的:

destination :指针,指向⽬标空间,将拷⻉的数据存放在这⾥(归宿)。

source :指针,指向源空间,要拷⻉的数据从这⾥来(来源)。

num :从source要拷⻉的数据占据的字节数。

返回值:memmove 函数返回的⽬标空间的起始地址。

  使用例:

#include <stdio.h>
#include <string.h>
int main()
{
    int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
    memmove(arr1 + 2, arr1, 20);
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr1[i]);
    }
    return 0;
}

讲解:

与在memcpy中的使用方法一样,不同的是该例为同名数组的拷贝,即将该数组的前5个值拷贝从(arr1 + 2)即arr[2]开始。

2.模拟实现:

#include <stdio.h>
#include <string.h>
void * mymemmove ( void * dst, const void * src, int n )
{
    void *ret=dst;
    if(dst<src)
    {
        while(n--)
        {
            *(char*)dst=*(char*)src;
            dst=(char*)dst+1;
            src=(char*)src+1;
        }
    }
    else
    {
        while(n--)
            *((char*)dst+n)=*((char*)src+n);
    }
    return ret;
}
int main()
{
    int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
    mymemmove(arr1 + 2, arr1, 20);
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr1[i]);
    }
    return 0;
}

  运行结果一样,接下来对该实现讲解一下:

核心逻辑分析

mymemmove 通过判断目标地址(dst)和源地址(src)的位置关系,选择不同复制方向:

  • 当 dst < src(目标在源左侧):从低地址到高地址复制(避免覆盖未复制的源数据)。(对应图中的一情况)
  • 当 dst >= src(目标在源右侧或重叠):从高地址到低地址复制(从 n-1 反向遍历)。(对应图中的二情况)

输入参数

  • arr1 初始值:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  • mymemmove(arr1 + 2, arr1, 20)
    • dst = arr1 + 2(目标起始地址:第3个元素)
    • src = arr1(源起始地址:第1个元素)
    • n = 20(复制20字节,int 占4字节 → 共复制 5个元素

关键判断

dst (arr1+2) > src (arr1) → 进入 else 分支(从高地址到低地址复制)。

在 else中拷贝。

    while(n--)
            *((char*)dst+n)=*((char*)src+n);

最后: return ret;完成返回值: memmove函数返回的⽬标空间的起始地址。

 三、memset函数

1.介绍

void * memset ( void * ptr, int value, size_t num );

1.memset 函数是设置内存块的内容的,将内存中指定⻓度的空间设置为特定的内容。 2.memset 的使⽤需要包含<string.h>。

参数讲解:

1.ptr :指针,指向要设置的内存空间,也就是存放了要设置的内存空间的起始地址。

2.value :要设置的值,函数将会把value值转换成unsigned char的数据进⾏设置的。也就是以字节为单位来设置内存块的。

3.num :要设置的内存⻓度,单位是字节。

4.返回值:返回的是要设置的内存空间的起始地址。

  使用例:

#include <stdio.h>
#include <string.h>
int main ()
{
    char str[] = "hello world";
    memset (str,'x',6);
    printf(str);
    return 0;
}

对于该例的解释:

  1. 初始字符串char str[] = "hello world" 定义了一个字符数组,存储字符串 "hello world"(共11个字符 + 1个结束符 \0,总长度12字节)。

  2. memset 操作

    • 作用:将 str 起始的 6个字节 全部设置为字符 'x'
    • 原字符串前6个字符是 "hello "(注意第6个字符是空格),替换后变为 "xxxxxx"
  3. 剩余字符不变

    • memset 仅修改前6字节,原字符串从第7字节开始的内容("world\0")保持不变。
    • 拼接后结果为 "xxxxxxrld\0"(第7个字符是 'r',因此输出 xxxxxxrld)。

所以是这样的结果。

注意:当有⼀块内存空间需要设置内容的时候,就可以使⽤memset函数,值得注意的是memset函数对内存 单元的设置是以字节为单位的。

2.模拟实现:

#include <stdio.h>
#include <string.h>
void * mymemset ( void * p, int value,int n)
{  void *ret=p;
    while(n--)
    {
        *(char*)p=value;
        p=(char*)p+1;
    }
return ret;
}
int main ()
{
    char str[] = "hello world";
    mymemset (str,'x',6);
    printf(str);
    return 0;
}

讲解:

  • mymemset执行流程
    • 保存原始指针ret = p,用于最终返回。
    • 循环6次(n--从6到0),每次将p指向的字节设为'x',并将p按字节递增((char*)p + 1)。
    • 原字符串前6字节("hello ")被替换为"xxxxxx",后续字符"world\0"保持不变,拼接后结果为"xxxxxxrld\0"
  • printf输出:打印字符串至结束符\0,结果为 xxxxxxrld

 四、memcmp函数

1.介绍

int memcmp ( const void * ptr1, const void * ptr2, size_t num );

功能:

1.⽐较指定的两块内存块的内容,⽐较从ptr1和ptr2指针指向的位置开始,向后的num个字节 2.memcmp 的使⽤需要包含

ptr1 :指针,指向⼀块待⽐较的内存块

ptr2 :指针,指向另外⼀块待⽐较的内存块

num :指定的⽐较⻓度,单位是字节

  使用例:

#include <stdio.h>
#include <string.h>
int main()
{
    char s1[] = "abcdefg";
    char s2[] = "abcdEFG";
    int n;
    n = memcmp(s1, s2, sizeof(s1));
    if (n > 0)
        printf("'%s'>'%s'.\n", s1, s2);
    else if (n < 0)
        printf("'%s' <'%s'.\n",s1,s2);
    else
        printf("'%s'  =='%s'  .\n", s1, s2);
    return 0;
}

讲解:

  • 输出内容'abcdefg' < 'abcdEFG'.
  • 关键原因memcmp字节逐一比较(区分大小写),在第5个字符处:
    • s1[4] = 'e'(ASCII值101)
    • s2[4] = 'E'(ASCII值69)
      因 101 > 69memcmp返回正数,n>0, 执行: if (n > 0)       printf("'%s'>'%s'.\n", s1, s2);

注意:1.memcmp 函数是通过返回值告知⼤⼩关系的。

           2.如果要⽐较2块内存单元的数据的⼤⼩,可以使⽤该函数,这个函数的特点就是可以指定⽐较长度。

2.模拟实现:

#include <stdio.h>
#include <string.h>
int mymemcmp ( const void * p1, const void * p2, int n )
{
    const char* s1 = (const char*)p1;
    const char* s2 = (const char*)p2;
    while (n-- > 0)
    {
        if (*s1 != *s2)
        {
            return *s1 - *s2;
        }
        s1++;
        s2++;
    }
    return 0;
}
int main()
{
    char s1[] = "abcdefg";
    char s2[] = "abcdEFG";
    int n;
    n = mymemcmp(s1, s2, sizeof(s1));
    if (n > 0)
        printf("'%s'>'%s'.\n", s1, s2);
    else if (n < 0)
        printf("'%s' <'%s'.\n",s1,s2);
    else
        printf("'%s'  =='%s'  .\n", s1, s2);
    return 0;
}
 

结果一样,接下来,我将为大家讲解一下:

因为参数为void*类型,如果使用,需要     *(char*)p1  这种的强转,代码中每次比较和移动指针时都进行(char*)强制转换,可通过临时变量优化,const char* s1 = (const char*)p1;
    const char* s2 = (const char*)p2;通过char*类型,便于比大小和向下移动。

    while (n-- > 0)
    {
        if (*s1 != *s2)
        {
            return *s1 - *s2;
        }
        s1++;
        s2++;
    }
    return 0;

  • 循环条件n-- > 0 表示从第 1 个字节开始,逐个比较 n 个字节(每轮循环后 n 减 1,直到 n=0 退出)。
  • 逻辑
    • 每次循环读取 s1 和 s2 指向的当前字节(*s1/*s2)。
    • 若字节不同,直接返回两字节的 ASCII 码差值(例如 'e' - 'E' = 101 - 69 = 32,结果为正数)。
    • 若字节相同,指针后移(s1++/s2++),继续比较下一个字节。
    • 若循环结束后所有 n 字节均相同(未触发 return *s1 - *s2),则显式返回 0。

总结

以上就是今天要讲的内容,

本篇文章涉及的C语⾔内存函数有:

1.memcpy函数

2.memmove函数

3.memset函数

4.memcmp函数

知识的相关内容,为本章节知识的内容,希望大家能喜欢我的文章,谢谢各位。


网站公告

今日签到

点亮在社区的每一天
去签到