第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' 而停止。
  • 如果 sourcedestination 指向的内存区域有重叠 ,行为是未定义的(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(按字节填充)。
  • 常用于初始化数组、结构体等。
  • valueint 类型,但只使用其低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);

🔹 特性说明

  • 比较从 ptr1ptr2 开始的 num 个字节
  • 按字节进行无符号比较
  • 返回值:
    • > 0ptr1 大于 ptr2
    • == 0:前 num 字节完全相同
    • < 0ptr1 小于 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代码!

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

相关推荐
Han.miracle3 小时前
数据结构——二叉树的从前序与中序遍历序列构造二叉树
java·数据结构·学习·算法·leetcode
mit6.8245 小时前
前后缀分解
算法
独自破碎E6 小时前
判断链表是否为回文
数据结构·链表
你好,我叫C小白6 小时前
C语言 循环结构(1)
c语言·开发语言·算法·while·do...while
朱嘉鼎8 小时前
状态机的介绍
c语言·单片机
寂静山林8 小时前
UVa 10228 A Star not a Tree?
算法
Neverfadeaway9 小时前
【C语言】深入理解函数指针数组应用(4)
c语言·开发语言·算法·回调函数·转移表·c语言实现计算器
一碗绿豆汤9 小时前
c语言-流程控制语句
c语言
子牙老师9 小时前
从零手写gdb调试器
c语言·linux内核·gdb·调试器
Madison-No79 小时前
【C++】探秘vector的底层实现
java·c++·算法