On Wed, 22 Jun 2016 18:37:32 +0100, Katolaz wrote:
[...]
> It is perfectly legal in C to do something like:
> 
> =====
> 
> int fun (int *v){
> 
>     v[4] = 17;
> }
> 
> int fun2(char *p){
> 
>     p[32] = 0;
> 
> }
> 
> int fun3(float *f){
> 
>     f[7] = 3.1415;
> }
> 
> 
> int main(){
> 
>     int *a;
> 
>     a = malloc(47 * sizeof(double)); // Yes, I know I wrote "sizeof(double)" 
> here, so what?
> 
>     fun(a);
>     fun(a+17);
>     fun(a+37);
> 
>     fun2((char*)a);
>     fun2((char*)(a + 21));
> 
>     fun3((float*)a);
> 
> }

After reassessing your sample code one more time, I think it is 
worth mentioning that you only got away with a black eye because 
the standard gives the following explicit guarantee for memory 
allocated by one of the *alloc() functions:

| The pointer returned if the allocation succeeds is suitably 
| aligned so that it may be assigned to a pointer to any type 
| of object and then used to access such an object or an array 
| of such objects in the space allocated […].      
(C99 7.20.3p1, similar wording in C11 7.22.3p1)

Otherwise at least the cast to a float pointer would have invoked 
undefined behavior; consider: 

| A pointer to void shall have the same representation and alignment 
| requirements as a pointer to a character type. 39) […]  Pointers 
| to other types need not have the same representation or alignment 
| requirements.
| 39) The same representation and alignment requirements are meant to 
| imply interchangeability as arguments to functions, return values 
| from functions, and members of unions.             
(C99 6.2.5p27, similar wording in C11 6.2.5p28)

Actually, I think it in fact /still/ invokes undefined behavior, 
as it is only valid to cast from a pointer-to-T to a pointer to a 
compatible type (and back), or from a pointer-to-T to void pointer
(or a pointer-to-unsigned-char[1]) and back to a type compatible 
with T.

So, your casts to char* are at least fishy (since we cannot tell 
if char is signed or unsigned, as that is implementation defined), 
and the cast to float* is downright wrong. You only avoided the 
compiler barfing[2] at you by hiding the error behind explicit 
type casts. If you remove the bogus casts, even gcc coughs up 
appropriate diagnostics[2] about the incompatibility of the 
pointer types involved.

Lesson:  Whenever you write down an explicit type cast in C, 
chances are you're doing something dubious and try to get away 
with it by lying to the compiler. There are exceptions to this 
rule, but they are few and far between. 

The following is not necessarily directed at you, KatolaZ, but 
is merely meant to preempt any snide remarks by others à la 
"works fine on my box", "all the world's a *NIXtel™", etc: 
Yes, there exist architectures where pointers to different types 
have different internal representation, so one may actually lose 
information by casting to an incompatible type. Not to mention 
possible alignment woes when trying to dereference such a 
twisted pointer. And don't get me started on those hacks that 
involve pointers being cast to integers, or vice versa!


[1] This is a special case, as originally pointer-to-unsigned-char 
    was the closest to a generic pointer type C had to offer — the 
    void pointer type is a later addition.  Plus it is explicitly 
    allowed to access the individual bytes of any object through
    a pointer-to-unsigned-char.

[2] Note that the compiler is not even required to warn you, as 
    we're already in the realm of undefined behavior, so all bets 
    are off anyway. 


Regards
Urban

_______________________________________________
Dng mailing list
[email protected]
https://mailinglists.dyne.org/cgi-bin/mailman/listinfo/dng

Reply via email to