c - 通过将 void 指针转换为不同的结构指针来访问它的内容
问题描述
在 C 语言中,假设我有一个用于存储所有数据的通用指针,在此示例中,为指针内容分配了四个整数的大小。
void * gen_purpose_ptr = SOME_MEMORY_ADDRESS;
gen_purpose_ptr = malloc(4*sizeof(int));
并且有两个结构使用这个指针来访问它们的内容:
struct Struct_1 {
int data1, data2;
};
struct Struct_2 {
int data3, data4;
};
现在如果我这样做
( (struct Struct_1 *)gen_purpose_ptr ) -> data1 = VALUE_A;
( (struct Struct_1 *)gen_purpose_ptr ) -> data2 = VALUE_B;
( (struct Struct_2 *)gen_purpose_ptr ) -> data3 = VALUE_C;
( (struct Struct_2 *)gen_purpose_ptr ) -> data4 = VALUE_D;
在这种情况下,VALUE_A
, VALUE_B
, VALUE_C
,是否会VALUE_D
被正确存储而不会被彼此覆盖?
当转换和赋值操作完成时,编译器是否知道特定的内存只能由特定的结构成员访问?
解决方案
Let's see for ourselves!
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
void printhex(const void *pnt0, size_t s)
{
const unsigned char *pnt = pnt0;
while(s--) {
printf("%02x", *pnt++);
}
}
void printhexln(const char *pre, const void *pnt, size_t s, const char *post)
{
printf("%s", pre);
printhex(pnt, s);
printf("%s", post);
printf("\n");
}
struct Struct_1 {
unsigned int data1, data2;
};
struct Struct_2 {
unsigned int data3, data4;
};
int main()
{
// let's grab memory for 4 ints
size_t size = 4 * sizeof(int);
void * ptr = malloc(size);
assert(ptr != NULL);
// let's zero that memory
memset(ptr, 0, size);
// this will print zeros
printhexln("1: ", ptr, size, "");
( (struct Struct_1 *)ptr ) -> data1 = 0x1122;
printhexln("2: ", ptr, size, "");
( (struct Struct_1 *)ptr ) -> data2 = 0x3344;
printhexln("3: ", ptr, size, "");
( (struct Struct_2 *)ptr ) -> data3 = 0x5566;
printhexln("4: ", ptr, size, "");
( (struct Struct_2 *)ptr ) -> data4 = 0x7788;
printhexln("5: ", ptr, size, "");
free(ptr);
return 0;
}
will output on https://www.onlinegdb.com :
1: 00000000000000000000000000000000
2: 22110000000000000000000000000000
3: 22110000443300000000000000000000
4: 66550000443300000000000000000000
5: 66550000887700000000000000000000
printhex
is a simple function that prints the memory behind a pointer in hexadecimal characters. We can see that:
- first there are only zeros. We can count zeros and we see that
size = 16
andsizeof(int) = 4
. - We then cast the pointer to struct Struct_1 and set data1 to 0x1122. The first 2 bytes of the pointer are overwritten and set to 0x2211, because the machine is little endian. The value of
((struct Struct_1)ptr)->data1
is now equal to0x00001122
. - We can see that writing 4433 to ((struct Struct_1*)pnt)->data2 sets byte 5 and 6 to 0x4433. Machine is little endian,
sizeof(int) = 4
and we can see thatoffsetof(struct Struct_1, data2) = 4
- Casting the struct to Struct_2 and writing to data3, overwrite first 2 bytes, not caring about the previous values. That's because
offsetof(struct Struct_2, data3) = 0
, so data3 starts at the beginning of the pointer. - Well, 4433 is overwritten by 8877 when writing to the data4 member of Struct_2
推荐阅读
- c++ - 带 * 的 C++ 布尔函数,你能帮帮我吗?我的代码有什么问题?
- powershell - 如何将 PowerShell 输出格式化为表格
- video - 未在 agora 会议中录制视频
- android-studio - IDE 致命错误 - 插件 Gradle 中的异常
- typescript - NestJS,尝试使用 ormconfig.ts,它破坏了一切
- postgresql - 如何为新的 GCP Cloud SQL PostgreSQL 用户授予 SELECT 权限?
- python-3.x - PySide2:QWidget 在装饰器中无法按预期工作
- directory - 织物纹理未加载
- powershell - powershell 导入 csv 并过滤名称以运行 enable-remotemailbox 并更新属性
- vue.js - 如何在 nuxtjs 中用作 vuetify 主题颜色