简体   繁体   中英

Is casting to and from char pointer well defined in standard C?

I've just been reading up on type punning, strict aliasing and alignment and was having trouble understanding pointer alignment issues. I know that there are exceptions when using char* and was wondering if the following was safe to do:

char* test = malloc(sizeof(struct foo));
((struct foo*)test)->member = 10;

Apparently the safest way to do something like this would be to use memcpy :

char* test = malloc(sizeof(struct foo));
struct foo A;
A.member = 10;
memcpy(test, &A, sizeof A);

But then what if you wanted to access/change those values:

((struct foo*)test)->member = 10;

If this wasn't safe to do due to pointer alignment then you would have to do 2 memcpy s involving a tmp variable?

struct foo tmp;
memcpy(&tmp, test, sizeof tmp);
tmp.member = 20;
memcpy(test, &tmp, sizeof tmp);

Surely this is more inefficient than a pointer cast? I'm not sure if all compilers would optimize out the memcpy s.

For those wondering as to why you would want to do this, I was thinking that maybe you might want to store a char* to a value of any type (yes I know you can use a union for this, but what if this was abstracted away so you would not be able to place your own types in said union).

And what about in the case that the char* referred to an array of struct foo .

for (size_t i = 0; i < size; ++i)
{
  ((struct foo*)test)[i].member = 10;
}

as opposed to:

for (size_t i = 0, j = 0; i < size; ++i, j += sizeof (struct foo))
{
  struct foo tmp;
  memcpy(&tmp, test + j, sizeof tmp);
  tmp.member = 20;
  memcpy(test + j, &tmp, sizeof tmp);
}

malloc is intended to be used to reserve memory and create new objects in that memory. This code is fully defined by the C standard (supposing the appropriate definitions and #include lines precede it, of course):

void *test = malloc(sizeof (struct foo));
((struct foo *) test)->member = 10;

There is no aliasing problem here because the reserved memory initially has no object type associated with it. Once a value is stored into it using any type other than a character type, the type of that object becomes the effective type for the memory. The address returned by malloc is suitable for all the basic types ( char , integer types, floating-point types), enumerator types, pointer types, arrays of these types, structures or unions of these types without an overriding alignment specifier, and all the complete object types in the standard library (like struct tm ).

Pedantically, there are some inadequacies in the standard. The assignment above only writes to a member of a structure, not the whole structure, so is the effective type of the allocated memory set to be that of the whole structure? The language in the standard does not formally cover this and similar issues. However, it is entirely clear this code is intended to work, and all compilers support it.

Similarly, there are some inadequacies in the standard regarding pointer conversions, which is why I changed your code to use void *test instead of char *test . The standard does not explicitly said that converting the void * result of malloc to some other type, like struct foo * , actually produces a pointer to the same memory. (It just says that, if we convert the pointer back, we will get something equal to the original.) But it is clear memory reserved by malloc is intended to be used in this way. Inserting another conversion, first to char * and then to struct foo * complicates the matter further. Again, it should work, and it will work in all compilers, but the standard does not say it explicitly.

You can use memcpy to read or write objects in the allocated memory, but it is not necessary, unless you actually want to do aliasing where you have written an object of one type and want to reinterpret its bytes as another type. (This can be done with memcpy or with a union.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM