Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you ever assume typecasting pointers is safe?

I've heard from many people that you cannot guarantee typecasting will be performed lossless. Is that only true if you don't know your processor, that is, you haven't verified the number of bytes used for your data types? Let me give an example:

If you execute the following:

typedef struct
{
    int i;
    char c;
    float f;
    double d;
} structure;

size_t voidPtrSz = sizeof(void *);
size_t charPtrSz = sizeof(char *);
size_t intPtrSz = sizeof(char *);
size_t floatPtrSz = sizeof(float *);
size_t doublePtrSz = sizeof(double *);
size_t structPtrSz = sizeof(structure *);
size_t funcPtrSz = sizeof(int (*)(float, char));

printf("%lu\n", voidPtrSz);
printf("%lu\n", charPtrSz);
printf("%lu\n", intPtrSz);
printf("%lu\n", floatPtrSz);
printf("%lu\n", doublePtrSz);
printf("%lu\n", structPtrSz);
printf("%lu\n", funcPtrSz);

…and the output is the following…

4
4
4
4
4
4
4

Can you assume that in all cases you can typecast a specific data type pointer to another data type pointer safely? For example, if you execute this:

int foo(float, char)
{
}

void *bar(void)
{
    return (void *)foo;
}

int (*pFunc)(float, char) = bar();

Can you assume with certitude that pFunc has the address of foo?

like image 621
Fiddling Bits Avatar asked Jan 12 '23 18:01

Fiddling Bits


2 Answers

Regarding your specific code example, let's refer to section 6.3.2.3 of the C99 language standard:

A pointer to void may be converted to or from a pointer to any incomplete or object type. A pointer to any incomplete or object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

Note that a pointer-to-function is not the same as pointer-to-object. The only mention of pointer-to-function conversions is:

A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined.

So your code example invokes undefined behaviour.

If we avoid function-pointer conversions, the following paragraph explains everything:

A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned for the pointed-to type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer.

Note: Converting between pointer types is a separate issue from converting and then dereferencing (in general, that's only valid if you're converting to char * and then dereferencing.)

like image 141
Oliver Charlesworth Avatar answered Jan 17 '23 14:01

Oliver Charlesworth


Can you assume that in all cases you can typecast a specific data type pointer to another data type pointer safely?

Any data pointer can be safely cast to char* or void*. Any char* or void* thus created can be cast back to its original type. Any other data pointer cast leads to undefined behavior when indirection is performed on the pointer.

Any function pointer type can be cast to any other function pointer type, although you should not call a function through the wrong type. Casting a function pointer to void* or any other data pointer type results in undefined behavior.

Is that only true if you don't know your processor, that is, you haven't verified the number of bytes used for your data types?

Even then, you're not safe. When the C standard says a construct has undefined behavior, compiler writers are free to handle the construct as they wish. The result is that even though you think you know a construct with UB will be handled because you know the target CPU, optimizing compilers may cut corners and generate very different code than you expect.

like image 36
Fred Foo Avatar answered Jan 17 '23 14:01

Fred Foo