Castrate a structure into an array?

I am currently studying C, and it is difficult for me to understand the following code:

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); } 

The line I don’t understand is ptr = (struct dns_header *) &buffer;

Can anyone explain this in detail?

+6
source share
5 answers

Your buffer is just a continuous array of raw bytes. They have no semantics in terms of buffer : you cannot do something like buffer->ra = 1 .

However, from the point of view of struct dns_header * these bytes will become significant. What you do with ptr = (struct dns_header *) &buffer; displays your pointer to your data.

ptr will now point to the beginning of your data array. This means that when you write the value ( ptr->ra = 0 ), you actually change byte 0 from buffer .

You create a pointer representation of the struct dns_header your buffer array.

+3
source

The buffer simply serves as a memory area - that an array of characters does not matter for this code; it can be an array of any other type if it is the right size.

The structure determines how you use this memory - as a bit field, it represents it with particular specificity.

However, presumably you are sending this structure over the network - the code that makes the IO of the network is likely to be sent in a buffer that is in the form of an array of characters, because this is essentially the most reliable option - the IO network is executed in terms of sending bytes.

+2
source

Suppose you want to allocate space for a structure so that

 ptr = malloc(sizeof(struct dns_header)); 

which will return a pointer to the allocated memory,

 ptr = (struct dns_header *) &buffer; 

almost the same, except that in this case it is allocated on the stack, and there is no need to take the address of the array, it can be

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

or simply

 ptr = (struct dns_header *) buffer; 

there is no problem, because the addresses will be the same.

+2
source

The line I don’t understand is ptr = (struct dns_header *) & buffer;

You take the address of the array and pretend it's a pointer to dns_header . This is basically raw memory access, which is unsafe, but good if you know what you are doing. This will give you access to the dns_header at the beginning of the array.

Ideally, it should be a dns_header array of a non-byte array. You should be careful that dns_header contains bit fields that are not implemented by the standard, and is completely dependent on the compiler providers. Although bitfield implementations are pretty β€œnormal,” there is no guarantee, so the size of the byte array may not be compatible with your intentions.

+1
source

Adding to other answers:

This code is illegal because ANSI C. ptr->q_count = htons(1); violates the rule of strict smoothing.

It is allowed to use unsigned short lvalue (i.e. the expression ptr->q_count ) to access memory that either does not have a declared type (for example, malloc 'd space), or declared a type of short or unsigned short or compatible.

To use this code as is, you must pass -fno-strict-aliasing to gcc or clang. Other compilers may or may not have a similar flag.

An improved version of the same code (which also has some forward compatibility with resizing the structure):

 struct dns_header d = { 0 }; d.q_count = htons(1); unsigned char *buffer = (unsigned char *)&d; 

This is legal because a strict alias rule allows an unsigned char alias to do nothing.

Note that buffer is not used in this code. If your code is actually a smaller piece of larger code, then buffer can be defined differently. In any case, it can be combined with d .

0
source

Source: https://habr.com/ru/post/985220/


All Articles