使用void*作缓冲区存储uint32_t模拟RAM的读写问题与实现方法
问题根因
你的代码存在3个核心错误,直接导致内存写入位置完全偏离预期:
- 错误取了指针变量本身的地址:
mem是栈上存储的void*类型变量,保存的是malloc分配的堆内存块首地址。你写&mem时,拿到的是栈上mem变量自身的地址,不是你分配的4096字节模拟RAM的首地址,相当于直接在栈上乱写数据,破坏了栈内存的原有内容。 - 指针算术步长完全错误:
&mem的类型是void**,对指针做加减运算时,步长等于指针指向类型的大小(64位系统下void*占8字节)。你写&mem+sizeof(uint32_t)等价于&mem + 4,实际偏移的地址是(uint8_t*)&mem + 4*8 = (uint8_t*)&mem + 32字节,根本不是你预期的模拟RAM首地址+4字节的位置。 - 对
void*直接做指针算术属于C标准未定义行为:C标准不允许对不完整类型(void是不完整类型)做指针运算,虽然GCC等编译器做了扩展把void*步长设为1字节,但这是非标准写法,可移植性极差,你的打印函数里buffer+i的写法本身就不规范。
另外你拷贝测试值时传了&test1,这是test1指针变量自身的地址,不是test1指向的存储12344444的堆地址,属于额外的逻辑错误。
正确读写方法
项目要求用void*持有模拟内存块的前提下,只要遵守字节级偏移计算规则即可,不需要把内存块本身定义为uint32_t*或char*类型:
- 所有偏移计算前,先把
void*基址强转为uint8_t*(字节类型指针),该类型指针步长固定为1字节,计算偏移时不会出现步长错误,也符合C标准对指针算术的要求。 - 读写第n字节位置的变量时,用
(uint8_t*)mem + n算出目标地址,再通过memcpy或强转类型指针的方式读写,优先用memcpy避免内存对齐导致的异常。 - 永远不要对持有基址的指针变量取地址做写入操作,所有写入目标都应该是基址加偏移算出的模拟RAM内部地址。
修正后的可运行代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> // 打印模拟RAM内容,按4字节为单位遍历 void print(void* buffer){ uint8_t* base = (uint8_t*)buffer; // 转字节指针做偏移计算 for(int i = 0; i < 4096; i += sizeof(uint32_t)){ printf ("%u ", *(uint32_t*)(base + i)); } printf("\n"); } int main(void) { void* mem = malloc(4096); // 用void*持有分配的模拟RAM,符合项目要求 if (!mem) { perror("malloc failed"); return 1; } memset(mem, 0, 4096); // 初始化内存为0,避免脏值干扰 uint32_t val1 = 12344444; uint32_t val2 = 56788888; // 写值到0偏移位置(模拟RAM起始处) memcpy((uint8_t*)mem + 0, &val1, sizeof(uint32_t)); // 写值到4偏移位置(接在第一个32位值后面) memcpy((uint8_t*)mem + 4, &val2, sizeof(uint32_t)); // 示例:读取第4字节偏移处存储的32位值 uint32_t read_res; memcpy(&read_res, (uint8_t*)mem + 4, sizeof(uint32_t)); printf("Offset 4 value: %u\n", read_res); print(mem); free(mem); return 0; }
运行后输出开头为12344444 56788888 0 0 0 ...,符合预期。
通用访问模板
如果要访问模拟RAM中第n字节处存储的类型为T的变量,写法固定为:
// 写操作 T write_val = 目标值; memcpy((uint8_t*)mem + n, &write_val, sizeof(T)); // 读操作 T read_val; memcpy(&read_val, (uint8_t*)mem + n, sizeof(T));
内容的提问来源于stack exchange,提问作者pmdea
相关产品推荐
相关产品推荐

