Hao
Hao

Reputation: 13

Why use char* to handle a union's address?

union a {
    struct a_header a_hdr;
    struct b        b_hdr;
};

bzero((char*)(&(&a->a_hdr)[1]), sizeof(struct b) - sizeof(struct a_header));

What does this call do? Where does (char*)(&(&a->a_hdr)[1]) point to?

Upvotes: 1

Views: 121

Answers (2)

dbush
dbush

Reputation: 224457

Working under the assumption that struct b is larger than struct a_header (otherwise sizeof(struct b) - sizeof(struct a_header) would be zero or very large since size_t cannot be negative), this statement clears any bytes in union a that are used by b_hdr but not a_hdr.

Breaking down the expression:

&a->a_hdr

Given than a is a pointer to union a, this gets the address of the a_hdr field

(&a->a_hdr)[1]

This treats the above address as an array and gets the element at offset 1. There is none, so this would be undefined behavior, but then we have:

&(&a->a_hdr)[1]

Since a[b] is exactly equivalent to *(a + b), the above is the same as:

&(*(&a->a_hdr + 1))

The & and * adjacent to each other cancel each other out, so now you have:

&a->a_hdr + 1

So this points one element past the array of length 1 that is a->a_hdr. It is this address that is passed to bzero. The cast to char * is unnecessary since bzero takes a void * as its first parameter.

The second parameter:

sizeof(struct b) - sizeof(struct a_header)

Again, assuming struct b is bigger than struct a_header, this gives us the number of bytes that struct b is bigger by. So the bytes used by b_hdr but not a_hdr are cleared.

To illustrate, suppose the struct b is 8 bytes and struct a_header is 4 bytes. Then a union a would look like this:

---------------------------------
| X | X | X | X | X | X | X | X |
---------------------------------
|           struct b            |
---------------------------------
|struct a_header|
-----------------

Where X is some unknown byte value. After the above call to bzero, it looks like this:

---------------------------------
| X | X | X | X | 0 | 0 | 0 | 0 |
---------------------------------
|           struct b            |
---------------------------------
|struct a_header|
-----------------

Upvotes: 1

lod
lod

Reputation: 1100

I'm going to take a guess and say that struct b has a definition like,

struct b {
  struct a_header header;
  ... [other stuff]
}

The union allows you to access the member elements of the header more easily.

The line you have gets the address of the start of the [other stuff] portion of the struct b, and sets it to zero.


Breaking it down, starting from the inside

&a->a_hdr a is a pointer to the union of type union a (kinda confusing). This line gets the memory at the a_hdr portion of the union, this changes the type to struct a_header, then we get the address of it. The address here doesn't actually change, rather it is a safe way of casting the pointer to be of type struct a_header.

&(&a->a_hdr)[1] we treat the pointer to struct a_header as an array and get memory of the next element, then get the address of that memory. Essentially we have done a + sizeof(struct a_header). This only makes sense if you know what is in the memory after struct a_header, either because you have an array or a struct like I placed in the above section.

(char*)(&(&a->a_hdr)[1]) the cast changes the pointer type, it probably isn't necessary as most bzero implementations take a void pointer, but it doesn't hurt either.

bzero((char*)(&(&a->a_hdr)[1]), sizeof(struct b) - sizeof(struct a_header))

bzero is a function which sets a chunk of memory to zero, an alternative to memset. The second parameter sets the size, the subtraction strongly suggests that struct b contains as it's first member a variable of type struct a_header. The pointer manipulations in the first parameter skip over that element, the second parameter sets the size to be the remainder of struct b, bzero then clears this portion.

Upvotes: 0

Related Questions