【C语言】内存函数


前言

在C语言编程中,内存操作是绕不开的核心环节,而memcpymemmovememsetmemcmp这几个内存函数更是高频使用的工具。它们直接作用于内存字节,不受数据类型限制,灵活度极高,但也因细节繁多容易踩坑。本文将从使用方法、核心特点、模拟实现三个维度,把这四个函数讲透,帮你彻底掌握内存操作的精髓。


一、memcpy

1. 函数核心说明

memcpy的核心作用是从源内存地址开始,向后复制指定字节数的数据到目标内存地址,函数原型如下:

c 复制代码
void * memcpy ( void * destination, const void * source, size_t num );
  • 关键特点
    • 不识别'\0':即便拷贝过程中遇到字符串结束符,也会继续拷贝,直到完成num个字节。
    • 不处理重叠内存:若源地址(source)和目标地址(destination)的内存区域重叠,拷贝结果未定义,这是它和memmove的核心区别。

2. 实用示例

比如将数组arr1的前5个int元素(共20字节)拷贝到arr2中:

c 复制代码
#include <stdio.h>
#include <string.h>

int main()
{
    int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
    int arr2[10] = { 0 };
    // 拷贝20字节(5个int,每个int占4字节)
    memcpy(arr2, arr1, 20);
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        // 输出:1 2 3 4 5 0 0 0 0 0
        printf("%d ", arr2[i]);
    }
    return 0;
}

3. 模拟实现

模拟实现的核心思路是按字节拷贝(因为要适配所有数据类型),同时校验指针合法性:

c 复制代码
#include <assert.h>

void * memcpy ( void * dst, const void * src, size_t count)
{
    // 断言防止空指针
    assert(dst && src);
    void * ret = dst;
    // 按字节逐个拷贝
    while (count--) {
        *(char *)dst = *(char *)src;
        dst = (char *)dst + 1;
        src = (char *)src + 1;
    }
    // 返回目标地址,支持链式调用
    return ret;
}

二、memmove

1. 函数核心说明

memmovememcpy的增强版,原型和memcpy一致,但支持源/目标内存重叠的场景:

c 复制代码
void * memmove ( void * destination, const void * source, size_t num );

当源内存和目标内存重叠时,必须使用memmove,否则会出现数据覆盖错误。

2. 实用示例

将数组arr1的前5个元素拷贝到从arr1+2开始的位置(内存重叠):

c 复制代码
#include <stdio.h>
#include <string.h>

int main()
{
    int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
    // 拷贝20字节到arr1+2的位置,内存重叠
    memmove(arr1+2, arr1, 20);
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        // 输出:1 2 1 2 3 4 5 8 9 10
        printf("%d ", arr1[i]);
    }
    return 0;
}

3. 模拟实现

核心逻辑是分两种情况处理内存重叠

  • 若目标地址 ≤ 源地址,或目标地址 ≥ 源地址+拷贝字节数(无重叠):从低地址到高地址拷贝(和memcpy一致)。
  • 若内存重叠:从高地址到低地址拷贝,避免数据覆盖。
c 复制代码
#include <assert.h>

void * memmove ( void * dst, const void * src, size_t count)
{
    assert(dst && src);
    void * ret = dst;
    // 情况1:无重叠,低地址→高地址拷贝
    if (dst <= src || (char *)dst >= ((char *)src + count)) {
        while (count--) {
            *(char *)dst = *(char *)src;
            dst = (char *)dst + 1;
            src = (char *)src + 1;
        }
    }
    // 情况2:有重叠,高地址→低地址拷贝
    else {
        dst = (char *)dst + count - 1;
        src = (char *)src + count - 1;
        while (count--) {
            *(char *)dst = *(char *)src;
            dst = (char *)dst - 1;
            src = (char *)src - 1;
        }
    }
    return ret;
}

三、memset

1. 函数核心说明

memset用于将指定内存区域的每个字节设置为指定值,原型如下:

c 复制代码
void * memset ( void * ptr, int value, size_t num );
  • 注意:memset按字节赋值,因此仅适合单字节数据(如char)或批量设置0/0xff等场景,不能直接用于int数组赋值(除非赋值0)。

2. 实用示例

将字符串前6个字节设置为字符'x':

c 复制代码
#include <stdio.h>
#include <string.h>

int main ()
{
    char str[] = "hello world";
    // 将前6个字节设为'x'
    memset (str,'x',6);
    // 输出:xxxxxxworld
    printf(str);
    return 0;
}

四、memcmp

1. 函数核心说明

memcmp用于比较两个内存区域的前num个字节,原型如下:

c 复制代码
int memcmp ( const void * ptr1, const void * ptr2, size_t num );

返回值规则(按无符号字符比较):

返回值 含义
< 0 ptr1首个不同字节 < ptr2对应字节
= 0 前num个字节完全相同
> 0 ptr1首个不同字节 > ptr2对应字节

2. 实用示例

比较两个字符数组的全部字节内容:

c 复制代码
#include <stdio.h>
#include <string.h>

int main()
{
    char buffer1[] = "DWgaOtP12df0";
    char buffer2[] = "DWGAOTP12DF0";
    int n;
    // 比较整个数组的字节内容
    n = memcmp(buffer1, buffer2, sizeof(buffer1));

    if (n > 0) 
        printf("'%s' is greater than '%s'.\n", buffer1, buffer2);
    else if (n < 0) 
        printf("'%s' is less than '%s'.\n", buffer1, buffer2);
    else
        printf("'%s' is the same as '%s'.\n", buffer1, buffer2);
    // 输出:'DWgaOtP12df0' is greater than 'DWGAOTP12DF0'(小写字母ASCII码更大)
    return 0;
}

总结

C语言的这四个内存函数各有侧重:

  • memcpy:基础内存拷贝,不处理重叠;
  • memmove:增强版拷贝,支持内存重叠;
  • memset:按字节设置内存值,注意赋值粒度;
  • memcmp:按字节比较内存,区分无符号字符大小。

掌握它们的使用场景和底层实现逻辑,既能避免内存操作的坑,也能更深刻理解C语言"直面内存"的特性。实际开发中,优先使用库函数(效率更高、经过严格测试),理解模拟实现则能帮你夯实内存操作的基础。

至此,我们已梳理完"字符函数和字符串函数"的全部内容了。最后我们在文末来进行一个投个票,告诉我你对哪部分内容最感兴趣、收获最大,也欢迎在评论区聊聊你的学习感受。

以上就是本期博客的全部内容了,感谢各位的阅读以及关注。如有内容存在疏漏或不足之处,恳请各位技术大佬不吝赐教、多多指正。


相关推荐
前端程序猿i1 小时前
彻底搞懂防抖(Debounce)与节流(Throttle):源码实现与应用场景
开发语言·前端·javascript·vue.js·ecmascript
纵有疾風起1 小时前
【C++—STL】红黑树底层封装与set/map模拟实现
开发语言·c++·经验分享·面试·开源·stl
执笔论英雄1 小时前
【RL】async_engine 远离
java·开发语言·网络
不会c嘎嘎1 小时前
【数据结构】红黑树详解:从原理到C++实现
开发语言·数据结构
pandarking1 小时前
[CTF]攻防世界:ics-05
开发语言·javascript·web安全·网络安全·ecmascript
执笔论英雄1 小时前
【RL]expand_requests干啥的
服务器·开发语言·python
kesifan1 小时前
JAVA线程的建立方法
java·开发语言·python
周杰伦fans1 小时前
C#中ValueTask
开发语言·c#
菠菠萝宝1 小时前
【Java手搓OpenManus】-5- 工具系统设计
java·开发语言·人工智能·openai·agent·manus