第18讲:C语言内存函数

📚 第18讲:C语言内存函数

深入理解底层内存操作,掌握高效、安全的数据拷贝与比较技术!


📂 目录

  1. memcpy 的使用与模拟实现 🧩
  2. memmove 的使用与模拟实现 🔄
  3. memset 函数的使用 🎨
  4. memcmp 函数的使用 ⚖️

📖 正文开始

在C语言中,除了字符串操作函数外,还有一组内存函数 ,它们不关心数据类型,而是以字节为单位 直接操作内存。这些函数定义在 <string.h> 头文件中,适用于任意类型的数据块操作。


memcpy 的使用与模拟实现 🧩

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

🔹 特性说明

  • 从 source 指向的内存位置开始,复制 num 个字节 到 destination。
  • 不会 因遇到 '\0' 而停止。
  • 如果 source 和 destination 指向的内存区域有重叠 ,行为是未定义的(undefined behavior)。

✅ 适用场景 :源和目标内存不重叠时的高效拷贝。

✅ 示例:整型数组拷贝
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)
    memcpy(arr2, arr1, 20);

    for (int i = 0; i < 10; i++) {
        printf("%d ", arr2[i]);
    }
    printf("\n");

    return 0;
}

📌 输出:1 2 3 4 5 0 0 0 0 0


🔧 memcpy 模拟实现

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

void *my_memcpy(void *dst, const void *src, size_t count) {
    void *ret = dst;
    assert(dst != NULL);
    assert(src != NULL);

    // 逐字节拷贝:从低地址向高地址
    while (count--) {
        *(char *)dst = *(char *)src;
        dst = (char *)dst + 1;
        src = (char *)src + 1;
    }

    return ret;
}

int main() {
    int arr1[] = {10, 20, 30, 40};
    int arr2[4] = {0};

    my_memcpy(arr2, arr1, sizeof(arr1));

    for (int i = 0; i < 4; i++) {
        printf("%d ", arr2[i]);
    }
    printf("\n");

    return 0;
}

📌 输出:10 20 30 40
⚠️ 注意 :此函数不能处理内存重叠!重叠场景请使用 memmove。


memmove 的使用与模拟实现 🔄

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

🔹 特性说明

  • 功能与 memcpy 类似,但可以正确处理源和目标内存区域重叠的情况。
  • 是处理重叠内存拷贝的安全选择。
✅ 示例:数组内部移动(内存重叠)
c 复制代码
#include <stdio.h>
#include <string.h>

int main() {
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    // 将前5个元素(20字节)向前移动2个位置
    memmove(arr + 2, arr, 20);

    for (int i = 0; i < 10; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    return 0;
}

📌 输出:1 2 1 2 3 4 5 8 9 10
🔍 解析:

  • 原数组:[1,2,3,4,5,6,7,8,9,10]
  • 将 arr[0]~arr[4] 拷贝到 arr[2]~arr[6]
  • 使用 memmove 可避免因重叠导致的数据覆盖错误。

🔧 memmove 模拟实现

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

void *my_memmove(void *dst, const void *src, size_t count) {
    void *ret = dst;
    assert(dst != NULL);
    assert(src != NULL);

    if (dst <= src || (char *)dst >= ((char *)src + count)) {
        // ✅ 情况1:无重叠 或 重叠但目标在源之后
        // 从低地址向高地址拷贝
        while (count--) {
            *(char *)dst = *(char *)src;
            dst = (char *)dst + 1;
            src = (char *)src + 1;
        }
    } else {
        // ✅ 情况2:重叠且目标在源之前
        // 从高地址向低地址拷贝,避免覆盖
        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;
}

int main() {
    char str[] = "hellohello";
    my_memmove(str + 5, str, 5);  // 重叠拷贝
    printf("Result: %s\n", str);  // 输出: hellohello

    return 0;
}

✅ 关键点 :通过判断内存地址关系,选择正向或反向拷贝,确保数据安全。


memset 函数的使用 🎨

c 复制代码
void *memset(void *ptr, int value, size_t num);

🔹 特性说明

  • 将 ptr 指向的内存区域的前 num 个字节 ,设置为 value(按字节填充)。
  • 常用于初始化数组、结构体等。
  • value 是 int 类型,但只使用其低8位(一个字节)。
✅ 示例:字符数组填充
c 复制代码
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "hello world";
    memset(str, 'x', 6);  // 前6个字符设为 'x'
    printf("Result: %s\n", str);  // 输出: xxxxxxworld

    return 0;
}
✅ 示例:整型数组初始化为0
c 复制代码
#include <stdio.h>
#include <string.h>

int main() {
    int arr[5];
    memset(arr, 0, sizeof(arr));  // 全部设为0

    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    return 0;
}

📌 输出:0 0 0 0 0
⚠️ 注意 :memset 是按字节设置的。若想将 int 数组设为 -1,可以:

c 复制代码
memset(arr, -1, sizeof(arr)); // 每个字节都是 0xFF,int 变为 -1

memcmp 函数的使用 ⚖️

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

🔹 特性说明

  • 比较从 ptr1 和 ptr2 开始的 num 个字节。
  • 按字节进行无符号比较。
  • 返回值:
    • > 0:ptr1 大于 ptr2
    • == 0:前 num 字节完全相同
    • < 0:ptr1 小于 ptr2
✅ 示例:比较两个缓冲区
c 复制代码
#include <stdio.h>
#include <string.h>

int main() {
    char buffer1[] = "DWgaOtP12df0";
    char buffer2[] = "DWGAOTP12DF0";
    int n;

    // 比较整个字符串(包含 '\0')
    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);
    }

    return 0;
}

📌 输出:'DWgaOtP12df0' is greater than 'DWGAOTP12DF0'.
🔍 解析 :比较是逐字节进行的。第一个不同字符是 'g' vs 'G',由于 'g' > 'G'(ASCII值),所以返回正值。

✅ 示例:比较结构体或任意数据块
c 复制代码
#include <stdio.h>
#include <string.h>

struct Data {
    int id;
    float score;
};

int main() {
    struct Data d1 = {1001, 89.5f};
    struct Data d2 = {1001, 89.5f};

    if (memcmp(&d1, &d2, sizeof(struct Data)) == 0) {
        printf("Two data structs are identical.\n");
    } else {
        printf("Data structs differ.\n");
    }

    return 0;
}

📌 输出:Two data structs are identical.


✅ 核心知识点总结

函数 功能 是否支持重叠 关键点
memcpy 内存拷贝 ❌ 不支持 高效,但重叠时行为未定义
memmove 内存移动 ✅ 支持 安全处理重叠,稍慢
memset 内存设置 --- 按字节填充,常用于初始化
memcmp 内存比较 --- 按字节比较,返回差值

🎯 掌握这些内存函数,你就能像操作系统一样直接操控内存,写出更高效、底层的C代码!

下节我们将学习动态内存管理,敬请期待!💪

相关推荐
程序猿编码15 分钟前
不用 PyTorch!纯 C 实现 N 维张量 + 反向传播,完成手写数字识别训练
c语言·人工智能·pytorch·神经网络·卷积神经网络·大模型推理
tangguofeng1 小时前
基于分治策略的循环赛日程表问题的思路讲解
算法
hnjzsyjyj1 小时前
洛谷 P1339:[USACO09OCT] Heat Wave G ← Bellman-Ford 算法
数据结构·bellman-ford 算法
起个名字费劲死了1 小时前
VisionMaster集成深度学习算法(基础狗)
人工智能·深度学习·算法
lengxuenong1 小时前
枚举例题分析
c++·算法·青少年编程
Navigator_Z2 小时前
LeetCode //C - 1269. Number of Ways to Stay in the Same Place After Some Steps
c语言·算法·leetcode
2601_965742222 小时前
短视频脚本创作方法分享:本地生活类账号的起步思路
大数据·人工智能·算法·ai·新媒体运营·生活
55873生态系统手记2 小时前
读完 55873 资产钱包的技术栈,我重新理解了 "合规钱包" 应该长什么样
算法·架构
旋生万物2 小时前
素数螺旋映射 $z_n=n^{1+i}$ 的角分布统计检验与零模型对比
大数据·前端·人工智能·算法·云原生·螺旋生成论·螺旋相位
HRTOS2 小时前
HRTOS Shell继续完善:优化printf缓冲区与字符串越界保护,国庆后随新版本更新
运维·单片机·嵌入式硬件·算法·51单片机