将数组转换为struct

时间:2014-11-13 13:03:52

标签: c arrays data-structures

我的好奇心受到以下代码的影响:

struct tree
{
    unsigned char apple, leaf;
};

int main(void)
{
    void* arr[2] = {(int*)1, (int*)2};
    struct tree* myStruct = (struct tree*)arr;

    return 1;
}

..逻辑上尝试将数组转换为结构并且不会发出警告。

这是我将数组转换为结构的方式吗?

3 个答案:

答案 0 :(得分:4)

  

逻辑上尝试将数组转换为结构并且不会发出警告。

该代码背后没有任何逻辑。更准确地说,代码无意义尝试将数组转换为结构。但是,C允许很多废话,当你调用未定义的行为时,一切都会发生。

这就是线后面发生的事情:

  • void* arr[2]是一个由两个指针组成的数组,它们的大小与给定系统的地址总线大小相同。我们假设地址总线是32位。
  • {(int*)1, (int*)2};然后获取两个整数文字并将它们中的每一个转换为指针。这在C中很好。所以我们有两个指针分别地址为0x00000001和0x00000002。
  • 然后int指针存储在void指针数组中没有问题,因为在void指针和另一种类型的指针之间不需要显式转换。
  • 然后(struct tree*)arr疯狂地将数组转换为结构类型。这打破了所谓的"strict aliasing rule"这是未定义的行为。在这里你的程序可能会崩溃和烧毁,因为有几个潜在的问题。
  • 结构的数据成员的对齐不一定与指针变量的对齐兼容,并且结构中可能存在填充字节。 - 此外,没有任何迹象表明结构小于2指针。如果它更大,程序将在读取超出数组末尾时尝试访问内存越界。
  • 此外,char在给定系统上的表示可能是任何内容。它们可能是8位,可能是16位,它们可能带有或不带有符号位。
  • 运气好的话,给定机器上的未定义行为可能会变成这样:假设字符是8位无符号,并且指针再次是32位。让我们假设没有填充或对齐问题。让我们假设您执行代码时程序不会崩溃和刻录。在这种情况下,结构将采用指针数组的2个第一个遇到的字节并将它们视为数据。由于数组占用数据0x0000000100000002,因此前两个字节为0x00和0x00。然后appleleaf将分别包含值0和0。但这也是依赖于endianess的,指针地址中的哪个字节取决于你的机器是使用小端还是大端。
  

这是我将数组转换为结构的方式吗?

不,这是非常糟糕的代码。没有什么是正确的。永远不要写这样的代码。


那么将数组转换为结构的正确方法是什么?

简单地说:

char arr[2] = {1, 2};
myStruct.apple = arr[1];
myStruct.leaf  = arr[2];

这是唯一的100%防弹方式。如果你想使用memcpy()或类似的东西,为了减少手动分配的数量,你必须编写防御性编程以保护自己免受struct padding:

static_assert(sizeof(arr) == sizeof(myStruct), "ERR: struct padding detected");

memcpy(&myStruct, arr, sizeof(myStruct));

答案 1 :(得分:1)

您将变量arr定义为void *。在C中,void指针用于定义泛型类型。您可以将void *投射到您想要的任何内容。这就是为什么没有警告。

但请记住,现代32/64位系统上的指针大小为4或8字节。您的结构tree小于数组arr

我认为你想要它(适用于32位系统):

#include <stdio.h>

struct tree
{
    // use int instead of char because char is only 1 byte
    // if 32 bit system
    unsigned int apple, leaf;
    // if 64 bit system
    // unsigned long apple, leaf;
};

int main(void)
{
    void* arr[2] = {(int*)1, (int*)2};
    struct tree* myStruct = (struct tree*)arr;
    // if 32 bit system
    printf("%d\n", myStruct->apple);
    printf("%d\n", myStruct->leaf);
    // if 64 bit system
    //printf("%lu\n", myStruct->apple);
    //printf("%lu\n", myStruct->leaf);
    return 1;
}

此示例不适用于每个系统,并且由于struct成员对齐而具有未定义的行为。阅读Lundin的帖子了解更多信息。

答案 2 :(得分:0)

#include <stdio.h>

struct tree
{
    char apple, leaf;
};

int main(void)
{
    char arr[sizeof(struct tree)] = {'1', '2'}; //array of pointers it is ugly
    //use char or unsigned char, and size of array must be "sizeof(struct tree)"
    //cause struct may be alligned
    struct tree* myStruct = (struct tree*)arr;

    printf("%p\n", myStruct);
    printf("%c\n", myStruct->apple);
    printf("%c\n", myStruct->leaf);
    return 1;
}

工作正常

相关问题