:: Re: [DNG] Studying C as told. (For …
Αρχική Σελίδα
Delete this message
Reply to this message
Συντάκτης: Irrwahn
Ημερομηνία:  
Προς: dng
Αντικείμενο: Re: [DNG] Studying C as told. (For help)
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