首页 > 解决方案 > 通过将 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被正确存储而不会被彼此覆盖?

当转换和赋值操作完成时,编译器是否知道特定的内存只能由特定的结构成员访问?

标签: cmemorystructurevoid-pointers

解决方案


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:

  1. first there are only zeros. We can count zeros and we see that size = 16 and sizeof(int) = 4.
  2. 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 to 0x00001122.
  3. 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 that offsetof(struct Struct_1, data2) = 4
  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.
  5. Well, 4433 is overwritten by 8877 when writing to the data4 member of Struct_2

推荐阅读