C语言入门指南:内存操作函数详解

目录

 

引言

1. memcpy:内存复制的“快刀手”

1.1函数原型:

1.2 功能:

1.3 重要特性:

1.4示例:

1.5 模拟实现(理解原理)

2. memmove:处理内存重叠的“安全卫士”

2.1 函数原型:

2.2 功能:

什么时候会内存重叠?

举个栗子 🌰

2.3 模拟实现(理解其智能之处)

3. memset:内存批量“填充器”

3.1 函数原型:

3.2功能:

3.3重要特性:

3.4示例:

4 memcmp:内存“比较器”

4.1 函数原型

4.2 功能:

4.3 返回值

举个栗子 🌰

 

总结:


 

引言

大家好!在C语言的编程世界里,除了我们熟悉的字符串函数(如strcpy, strlen),还有一类非常强大且底层的函数——内存操作函数。它们不关心你操作的数据是什么类型(是字符、整数还是结构体),只把内存看作是一块连续的字节空间,直接对字节进行复制、设置或比较。掌握这些函数,能让你的程序更高效,也能让你更深入地理解计算机内存的工作原理。

今天,我们就来详细聊聊四个核心的内存函数:

memcpy, memmove,  memset  ,memcmp 

我会用最通俗的语言和清晰的例子,带你搞懂它们!

 

1. memcpy:内存复制的“快刀手”

1.1函数原型:

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

1.2 功能:

memcpy的作用非常直接:

从源地址source开始,向后复制num个字节的数据,到目标地址destination

1.3 重要特性:

  • 不认\0:和strcpy不同,memcpy不会因为遇到\0(空字符)就停下来。它严格按照你指定的字节数num进行复制。
  • 不能处理内存重叠:这是memcpy最大的“雷区”!如果源内存区域和目标内存区域有重叠部分,memcpy的行为是未定义的,结果可能完全错误。

1.4示例:

假设我们有一个整型数组arr1,想把它的前5个元素(即前20个字节,因为一个int通常是4字节)复制到另一个数组arr2中。

#include <stdio.h>
#include <string.h> // memcpy在string.h头文件中

int main() {
    int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int arr2[10] = { 0 }; // 初始化为0

    // 复制20个字节,即5个int
    memcpy(arr2, arr1, 20);

    // 打印arr2
    for (int i = 0; i < 10; i++) {
        printf("%d ", arr2[i]);
    }
    // 输出:1 2 3 4 5 0 0 0 0 0
    return 0;
}

1.5 模拟实现(理解原理)

理解memcpy最好的方式就是自己动手写一个!它的核心思想就是逐字节拷贝。

#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和src强制转换为char*,以便按字节访问
        dst = (char*)dst + 1;      // 指针向后移动一个字节
        src = (char*)src + 1;
    }
    return ret;
}

关键点:我们把void*指针强制转换为char*,因为char类型正好占1个字节,这样就能精确地按字节进行操作了。

2. memmove:处理内存重叠的“安全卫士”

2.1 函数原型:

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

2.2 功能:

memmove的功能和memcpy一模一样,都是复制num个字节。但它的核心优势在于:即使源内存和目标内存有重叠,它也能保证复制结果的正确性!

 

什么时候会内存重叠?

最常见的场景就是在同一个数组内进行元素移动。比如,你想把数组的前几个元素挪到后面去。

举个栗子 🌰

我们尝试用memmove把数组arr1的前5个元素,复制到从第3个元素开始的位置

#include <stdio.h>
#include <string.h>

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

    // 把前5个元素(20字节)复制到从第3个元素(下标为2)开始的位置
    // 注意:这里源区域[0-4]和目标区域[2-6]明显重叠了!
    memmove(arr1 + 2, arr1, 20);

    for (int i = 0; i < 10; i++) {
        printf("%d ", arr1[i]);
    }
    // 输出:1 2 1 2 3 4 5 8 9 10
    // 解释:前两个元素没动,从第三个开始被前五个元素覆盖。
    return 0;
}

如果你在这里用memcpy,结果将是不可预测的,可能是一堆乱码!

 

2.3 模拟实现(理解其智能之处)

memmove之所以能处理重叠,是因为它很“聪明”,会根据内存重叠的情况选择不同的拷贝方向。

#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;
}

核心思想

  • 如果目标区域在源区域“后面”或者完全不重叠,就从前往后拷贝。
  • 如果目标区域在源区域“前面”且有重叠,就从后往前拷贝,这样就不会把还没复制的源数据给覆盖掉。

3. memset:内存批量“填充器”

3.1 函数原型:

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

3.2功能:

memset的作用是将某一块内存的前num个字节,全部设置为指定的值value

3.3重要特性:

  • value虽然是int类型,但实际使用时,它会被转换为unsigned char(即只取其最低的8位,一个字节)。
  • 它是按字节设置的,所以最适合用于初始化为0,或者初始化字符数组。

3.4示例:

#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "hello world"; // 这是一个字符数组
    printf("Before: %s\n", str);

    // 将str的前6个字节设置为字符 'x'
    memset(str, 'x', 6);

    printf("After: %s\n", str);
    // 输出:
    // Before: hello world
    // After: xxxxxxworld

    return 0;
}

一个非常常见的用法:初始化数组为0

int arr[100];
// 将整个数组(100 * sizeof(int) 个字节)初始化为0
memset(arr, 0, sizeof(arr));

注意memset按字节赋值,所以用它给int数组赋非0值要小心!比如memset(arr, 1, sizeof(arr)),并不会让每个int变成1,而是让每个字节都变成0x01,最终每个int会变成0x01010101(即十进制的16843009),这通常不是你想要的结果。

memcmp:内存“比较器”

4.1 函数原型

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

4.2 功能:

memcmp用于比较从ptr1ptr2指针开始的num个字节

4.3 返回值

  • < 0: ptr1指向的内存区域 小于 ptr2指向的内存区域。
  • = 0: 两者相等。
  • > 0: ptr1指向的内存区域 大于 ptr2指向的内存区域。

比较规则是逐字节比较,和strcmp类似,遇到不同的字节就返回差值,如果都相同则返回0。

举个栗子 🌰

#include <stdio.h>
#include <string.h>

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

    // 比较两个数组的所有字节
    int 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'.
    // 原因:在第3个字节,'g' (ASCII 103) > 'G' (ASCII 71)

    return 0;
}

 

总结:

一句话建议:如果你不确定内存是否会重叠,无脑使用memmove!它在功能上完全兼容memcpy,只是在有重叠时多做了一点判断,安全性满分。

希望这篇博客能帮你彻底掌握这四个强大的内存函数!它们是C语言底层操作的基石,理解它们,你的编程功力会更上一层楼!

            

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值