将struct转换为数组?

时间:2015-04-15 21:46:52

标签: c arrays pointers struct

我目前正在学习C,但我无法理解以下代码:

struct dns_header
{
    unsigned char ra : 1;
    unsigned char z : 1;
    unsigned char ad : 1;
    unsigned char cd : 1;
    unsigned char rcode : 4;
    unsigned short q_count : 16;

};

int main(void)
{
    struct dns_header *ptr;
    unsigned char buffer[256];

    ptr = (struct dns_header *) &buffer;

    ptr->ra = 0;
    ptr->z = 0;
    ptr->ad = 0;
    ptr->cd = 0;
    ptr->rcode = 0;
    ptr->q_count = htons(1);

}

我不理解的是ptr = (struct dns_header *) &buffer;

任何人都可以详细解释这个吗?

6 个答案:

答案 0 :(得分:4)

您的buffer只是一个连续的原始字节数组。他们没有buffer观点的语义:你不能做buffer->ra = 1之类的事情。

然而,从struct dns_header *的角度来看,这些字节会变得有意义。您使用ptr = (struct dns_header *) &buffer;执行的操作是将指针映射到数据。

ptr现在将指向数据数组的开头。这意味着当您写入值(ptr->ra = 0)时,实际上是在修改buffer中的字节0。

您正在投射struct dns_header数组buffer指针的视图。

答案 1 :(得分:2)

缓冲区只是作为一个内存区域 - 它的字符数组对于这段代码并不重要;它可以是任何其他类型的数组,只要它的大小正确

结构定义了你如何使用那个记忆 - 作为一个位域,它具有极高的特异性。

也就是说,大概是你通过网络发送这个结构 - 网络IO的代码可能希望传递一个字符数组形式的缓冲区,因为那个& #39;本质上是最好的选择 - 网络IO在发送字节方面完成。

答案 2 :(得分:2)

假设您要为结构分配空间,以便

ptr = malloc(sizeof(struct dns_header)); 

将返回指向已分配内存的指针

ptr = (struct dns_header *) &buffer; 

几乎是相同的,除了在这种情况下它在堆栈中分配,并且它不需要获取数组的地址,它可以是

ptr = (struct dns_header *) &buffer[0];

或只是

ptr = (struct dns_header *) buffer;

虽然没有问题,因为地址是相同的。

答案 3 :(得分:1)

  

我不明白的是ptr =(struct dns_header *)& buffer;

您获取数组的地址并假装它是指向dns_header的指针。它基本上是原始内存访问,这是不安全的,但如果你知道你在做什么就行。这样做将授予您在数组开头写入dns_header的权限。

理想情况下,它应该是dns_header的数组而不是字节数组。您必须谨慎对待dns_header包含位字段的事实,标准不强制执行该字段,这完全取决于编译器供应商。尽管位字段实现相当“理智”,但无法保证,因此字节数组的大小实际上可能与您的意图不匹配。

答案 4 :(得分:0)

添加其他答案:

此代码是非法的,因为ANSI C. ptr->q_count = htons(1);违反了严格的别名规则。

只允许使用unsigned short左值(即表达式ptr->q_count)来访问没有声明类型的内存(例如malloc'd空格),或已声明类型shortunsigned short或兼容。

要按原样使用此代码,您应该将-fno-strict-aliasing传递给gcc或clang。其他编译器可能有也可能没有类似的标志。

相同代码的改进版本(对结构大小的改变也有一些向前兼容性)是:

struct dns_header d = { 0 };
d.q_count = htons(1);

unsigned char *buffer = (unsigned char *)&d;

这是合法的,因为严格的别名规则允许unsigned char别名。

请注意,此代码中当前未使用buffer。如果您的代码实际上是较小代码的较小代码段,则可能必须以不同方式定义buffer。无论如何,它可能与d结合在一起。

答案 5 :(得分:0)

结构直接引用内存的连续块,并且结构中的每个字段都从开始处位于某个固定的偏移量处。然后可以通过结构指针或返回相同地址的结构声明名称访问变量。

在这里,我们声明一个 packed 结构,该结构引用一个连续的内存块

#pragma pack(push, 1)
struct my_struct
{
    unsigned char b0;
    unsigned char b1;
    unsigned char b2;
    unsigned char b3;
    unsigned char b4;
};
#pragma pack(pop)

然后可以使用指针通过其地址引用该结构。参见以下示例:

int main(void)
{
    struct my_struct *ptr;
    unsigned char buffer[5];

    ptr = (struct my_struct *) buffer;

    ptr->b0 = 'h';
    ptr->b1 = 'e';
    ptr->b2 = 'l';
    ptr->b3 = 'l';
    ptr->b4 = 'o';

    for (int i = 0; i < 5; i++)
    {
        putchar(buffer[i]); // Print "hello"
    }

    return 0;
}

在这里,我们将结构1:1的连续存储块显式映射到buffer所指向的连续存储块(使用第一个元素的地址)。

数组地址和地址名称在数字上相同,但类型不同。因此,这两行是等效的:

ptr = (struct my_struct *) buffer;
ptr = (struct my_struct *) &buffer;

如果我们按原样使用地址 并将其正确转换,则通常不会出现问题。将 pointer指针类型的数组地址解引用为array-of-type 会产生相同的指针,但具有不同的 array-of-type 类型。

尽管以这种方式操作内存似乎很方便,但强烈建议不要强烈,因为生成的代码很难理解。如果您真的别无选择,我建议使用联合来指定以特定方式使用该结构。