While looking at:
Can a C compiler add padding before the first element in a structure?
I came up with the following code:
(Ignore the fact that memory isn’t freed in this example.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char *cstr;
size_t len;
} str_t;
void setStr(str_t* dest, const char* src)
{
size_t len = strlen(src);
dest->cstr = malloc(len + 1);
dest->len = len;
memcpy(dest->cstr, src, len + 1);
}
int main(void)
{
str_t str;
setStr(&str, "woot!");
printf("%s\n", str);
return 0;
}
Amazingly, this actually works. This call:
printf("%s\n", str);
seems to be equivalent to this one:
printf("%s\n", str.cstr);
So one would think that the following is also possible:
char* plainstr = malloc(str.len + 1);
strcpy(plainstr, str);
But no go. In contrast to printf, strcpy is not variadic, so there’s type checking. The compiler rightfully complains:
passing 'str_t' to parameter of incompatible type 'const char *'
But trying to tell the compiler “I really mean it” by casting it:
strcpy(plainstr, (const char*)str);
Won’t work either:
operand of type 'str_t' where arithmetic or pointer type is required
Note that the following can’t work:
strcpy(plainstr, (const char*)&str);
Since str.cstr != &str. For example, the output of this:
printf("%p %p\n", str.cstr, &str);
Is the following:
0xbdb010 0x7fff788f6ab8
And indeed, garbage data is being copied to plainstr.
So the questions are:
- Why isn’t it allowed to cast a struct to a pointer type?
- How come that
printfdeals with this correctly if casting isn’t allowed?
Because it makes no sense. How would you reinterpret a whole bunch of possibly unrelated information of distinct types as a concise memory address? However, in the previous question you asked, all of the people who answered, cited the C standard, and one particular statement in the standard stated that
So (as @Mat already pointed it out), you can indeed write
and that “will work” for the reasons I just enumerated.
Because in C, typecasting is often just for fooling the compiler (except when it isn’t). By passing the structure, the structure will be copied, and your stack will be something like (I’m intentionally omitting any padding from the structure for the sake of simplicity):
So, now what
printf()will do is:%sformat specifier in the format string, it will pop off another char pointer – in reality, it’s the pointer to the structure, and the pointer to the first element, which is the string to be printed.Also, this is still undefined behavior, despite the fact that it works – if you don’t specify a format string for
printf()that actually corresponds to the types you pass in as its variadic arguments, that’s not conformant and you can expect anything to happen.