Casting between pointers is risky: here's a concrete example of how it can blow up on ARM:
Code:
void fooi(int * i)
{
*i = 5;
}
void badcast()
{
char str[] = "hello there";
char *c = &str[3];
fooi((void*)c);
}
The ARM ABI says that ints must be 4 byte aligned and chars can be 1 byte aligned. In this example I construct a valid char pointer to an unaligned address then try to access it through an int*. We've told the compiler that the address is word aligned with the type.
An ARM compiler will generate a store like:
Code:
mov r2, #5
str r2, [r3]
STR is a word-sized store to an unaligned address. What this does depends on the configuration of the core and version of the architecture:
- On later ARM cores (v6,v7), if the core is configured to allow unaligned accesses, it'll succeed (like it does on x86)
- On later ARM cores if the core is configured to check alignment, it'll generate a data abort. I.e. it will crash your program.
- On earlier ARM cores unaligned accesses aren't supported at all. I think some still have the ability to check alignment and generate an abort if you set it up that way, but by default they'll treat the address as if it were aligned: i.e. ignore the bottom 2 bits of a word access. That'd store 0x0000005 to &str[0]. Bad!! Horrible to debug!
Here's a table in all its horror. ARM Information Center
If you did:
it would error, because it knows that the pointer types are incompatible.
If GCC had a keyword to mark this as an unaligned pointer (it doesn't) then it'd have to generate 4 stores of 1 byte.
Functions like memcpy that take void*s have to assume the pointer is unaligned, so will check and if necessary do byte copies. I expect this is still true of ARM cores with unaligned access support, as it's always more efficient to do aligned accesses.
I went over yuklair's last bit of code a bit myself too and scratched my head a bit.
Code:
#include<stdio.h>
char (*(*foo)[])[];
int
main(int argc,char **argv){
foo=(void *)argv;
puts(*(*foo)[1]);
return(0);
}
If I am understanding this correctly....
foo is a pointer to an array of pointers to arrays of chars.
Deferencing foo gives an array of pointers to an array of chars. Then that's converted back into a pointer to the first elenent for the indexing operation. Then the pointer to char is dereferenced, but that's ok because when we get there we've been honest with the compiler about getting a char array. Which is then converted back into a pointer to the first char for the call.
Pointers to arrays of pointers and pointers to pointers must be the same size, right?
So if I have understod this right, which I may not have done, this is safe. But I wouldn't say it's an accurate representation of argv. I think the most helpful way to write it in terms of indicating the two arrays would be char argv[][]. Of course this isn't allowed, and even if it was I don't really approve -- I think that whether a pointer points to a single object or to an array of objects should be clear from the context. I think it usually is!