Reputation: 952
I am going through this code for learning purposes and have a question about this line:
return (char*)desc + sizeof *desc;
Why is desc
cast to char*
?
I tried to mimic it with my own code:
#include <stdio.h>
#include <stdlib.h>
struct Test {
int value;
};
int main() {
struct Test* test = malloc(sizeof test);
struct Test* test1 = (void*)test + sizeof *test;
test1->value = 1;
printf("%d\n", test1->value);
struct Test* test2 = (void*)test1 + sizeof *test;
test2->value = 10;
printf("%d\n", test2->value);
}
This also works. But, what is the difference? Why is char*
used?
Note: I used void*
just to see if that works. As char*
has nothing to do with the struct
in question, I simply thought, "What if I use void*
over there?". A more specific question could be, why not int*
or float*
& why char*
is used?
Upvotes: 2
Views: 1658
Reputation: 223264
Why is desc cast to char*? … As
char*
has nothing to do with thestruct
in question…
In C, every object except a bit-field is composed of a sequence of bytes.1 Converting the address of an object to char *
yields a pointer to the first byte of the object, and you can access the individual bytes of the object using that pointer.
In standard C, pointer arithmetic uses units of the pointed-to type. For a pointer p
of type struct Test
, p+1
points to the next structure after p
, p+2
points to the structure after that, and so on. For a pointer q
of type char *
, q+1
points to the next char
after q, q+2
points to char
after that, and so on.
Thus, to access the individual bytes of an object, you can convert its address to a char *
and use that.
A more specific question could be, why not
int*
orfloat*
& whychar*
is used?
char *
is used because all objects in C, except bit-fields, are defined to be represented as sequences of bytes. They are not necessarily sequences of int
or float
. unsigned char *
and signed char *
may also be used, and unsigned char *
may be preferable due to complications from sign issues.
The C standard has special rules about accessing objects using character pointers, so it guarantees that accessing the bytes of an object this way will work. In contrast, accessing objects using int *
or float *
may not work. The compiler is allowed to expect that a pointer to an int
will not be used to access a float
object, and, when it is generating machine instructions for a program, it may write those instructions based on that expectation. Using a character pointer prevents the compiler from assuming that a char *
does not point to the same place as another kind of pointer.
Note: I used
void*
just to see if that works.
For pointer arithmetic to work, the compiler needs to know the size of the pointed-to object. When 1 is added to a pointer to a struct Test
, the compiler needs to know how many bytes to adjust the internal address by.
void
is an incomplete type. There are no void
objects, and void
has no size. (The size is not zero. There is no size.) Because of this, the C standard does not define any meaning for p+1
when p
is a void *
.
However, GCC defines arithmetic on void *
as an extension. It works as if void
had a size of 1 byte. Clang supports this too.
Because of this extension, doing arithmetic with void *
pointers is essentially the same as doing arithmetic with char *
pointers.
This extension is unnecessary; any code doing arithmetic on void *
could be rewritten to use char *
instead. Sometimes this requires extra casts to convert pointer types, and that could be the reason the extension was added to GCC (to reduce the amount of code required and make it look better).
You can disable this extension with the switches -Werror -Wpointer-arith
, or you can generally request closer conformance to standard C with -Werror -std=c18 -pedantic
. I used -Werror -std=c18 -pedantic
whenever possible, and I recommend it.
1 Bit-fields are sequences of bits that are held in some larger container of bytes and may happen to coincide with bytes.
Upvotes: 5