You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C语言中对void*内存块进行结构化数据读写?

在预分配void*缓冲区读写不同类型数据的实用方案

核心逻辑

为了避开频繁malloc/free的性能坑,我们预分配一块连续内存作为命令缓冲区,靠偏移计算和类型强转直接在缓冲区对应位置读写结构体即可——重点是要保证内存对齐和偏移计算的准确性。

关键函数实现

先补全你代码里的buffer_alloc和buffer_get_ptr,这两个是操作缓冲区的核心:

buffer_alloc 实现

这个函数负责从指定偏移位置返回对应类型的指针,同时加入边界检查防止缓冲区溢出:

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>

// 给缓冲区加个头部,记录总大小和已用空间,方便管理
typedef struct {
    size_t total_size;
    size_t used_size;
    char data[]; // 柔性数组存储实际命令数据
} CmdBufferHeader;

void* buffer_alloc(void* buffer, size_t offset, size_t size) {
    CmdBufferHeader* header = (CmdBufferHeader*)buffer;
    // 检查偏移+数据大小是否超出缓冲区总容量
    if (offset + size > header->total_size - sizeof(CmdBufferHeader)) {
        return NULL; // 内存不足
    }
    // 更新已用空间(循环复用缓冲区时有用)
    if (offset + size > header->used_size) {
        header->used_size = offset + size;
    }
    // 转成char*计算偏移,因为void*不能直接进行算术运算
    return (void*)(header->data + offset);
}

buffer_get_ptr 实现

这个函数更简单,就是根据偏移直接返回对应位置的指针:

void* buffer_get_ptr(void* buffer, size_t offset) {
    CmdBufferHeader* header = (CmdBufferHeader*)buffer;
    // 防越界检查
    if (offset >= header->total_size - sizeof(CmdBufferHeader)) {
        return NULL;
    }
    return (void*)(header->data + offset);
}

修正后的主函数示例

注意原malloc需要调整,因为我们给缓冲区加了头部,要分配头部+数据区的总大小:

// 命令结构体定义
struct cmd_viewport {
    int x, y, w, h;
};

struct cmd_line {
    int x0, y0, x1, y1;
    unsigned int width;
};

int main(int argc, char* argv[]) {
    // 预分配1024字节的缓冲区(头部+数据)
    size_t buffer_total_size = 1024;
    CmdBufferHeader* cmd_buffer = (CmdBufferHeader*)malloc(buffer_total_size);
    if (!cmd_buffer) {
        return EXIT_FAILURE;
    }
    // 初始化缓冲区头部
    cmd_buffer->total_size = buffer_total_size;
    cmd_buffer->used_size = 0;

    // 分配viewport命令,偏移从0开始
    struct cmd_viewport* viewport = (struct cmd_viewport*)buffer_alloc(cmd_buffer, 0, sizeof(struct cmd_viewport));
    // 分配line命令,偏移为viewport结构体的大小
    struct cmd_line* line = (struct cmd_line*)buffer_alloc(cmd_buffer, sizeof(struct cmd_viewport), sizeof(struct cmd_line));

    if (!viewport || !line) {
        free(cmd_buffer);
        return EXIT_FAILURE;
    }

    // 直接往缓冲区对应位置写入数据
    viewport->x = 0;
    viewport->y = 0;
    viewport->w = 800;
    viewport->h = 600;

    line->x0 = 100;
    line->y0 = 100;
    line->x1 = 300;
    line->y1 = 400;
    line->width = 2;

    // 从指定偏移读取数据
    struct cmd_line* line2 = (struct cmd_line*)buffer_get_ptr(cmd_buffer, sizeof(struct cmd_viewport));
    if (line2) {
        printf("line->width = %u\n", line2->width);
    }

    // 循环复用缓冲区时,只需重置used_size,无需重复malloc/free
    cmd_buffer->used_size = 0;

    free(cmd_buffer);
    return EXIT_SUCCESS;
}

必注意的细节

  • 内存对齐:C语言中结构体成员会自动对齐,malloc分配的内存本身就是对齐的,因此直接转换指针是安全的,无需额外处理对齐问题。
  • 偏移计算:必须用char*来计算偏移,因为char占1字节,偏移值就是准确的字节数,而void*不能直接进行加减运算。
  • 边界检查:一定要添加边界检查,避免缓冲区溢出,尤其是循环复用缓冲区时,要确保写入的数据不超过预分配的大小。
  • 循环复用技巧:每秒60次的循环场景中,每次循环开始时将used_size设为0,直接重新写入命令即可,完全不需要重复执行malloc/free,能大幅提升性能。

类型转换的本质

从void*缓冲区的指定偏移转换为任意数据类型,本质就是三步:

  1. 将void*转换为char*(char是最小内存单位,偏移计算精准)
  2. 加上指定的偏移量,得到目标位置的char*指针
  3. 将该char*指针转换为目标数据类型的指针

比如你代码里的buffer_get_ptr,核心逻辑就是(void*)((char*)buffer_data + offset),添加头部只是为了更方便地管理缓冲区。

内容的提问来源于stack exchange,提问作者flcl8193

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 19:15:38