1

So I came across this situation today. I had a code like this

void some_handler(some_stuct_t * p_evt_type)
{   
            uint16_t a = ((uint16_t )p_evt_type->p_instance->p_instance_handler);
            (a)++;
}

It was givinig me warnings, but when I changed code to cast to pointer it looks like perfectly correct.

void some_handler(some_stuct_t * p_evt_type)
{   
            uint16_t * a = ((uint16_t *)p_evt_type->p_instance->p_instance_handler);
            (*a)++;
}

The

p_instance_handler

is of type

(void *)

What is wrong with casting directly to value ?

4
  • 1
    Means? You want to cast a pointer to an uint16_t? Commented May 10, 2016 at 7:06
  • Noo, I wanted to cast memory it pointing to to uint16_t. I already see that this could not work, thanks! Commented May 10, 2016 at 7:08
  • 1
    Note that in C you don't need to cast when assigning to/from void* from/to other data pointer types. uint16_t * a = p_evt_type->p_instance->p_instance_handler; should work fine. Commented May 10, 2016 at 7:10
  • Right, but it looks more clear to me. Still, thanks ! Commented May 10, 2016 at 7:15

2 Answers 2

2

INT36-C. Converting a pointer to integer or integer to pointer

Conversions between integers and pointers can have undesired consequences depending on the implementation. According to the C Standard, subclause 6.3.2.3 [ISO/IEC 9899:2011],

An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.

Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.

In short, conversion between pointers and integers is possible but the result is implementation-defined, so in implementations where sizeof(integer) < sizeof(pointer) the conversion will not work.

Noncompliant Code Example

The size of a pointer can be greater than the size of an integer, such as in an implementation where pointers are 64 bits and unsigned integers are 32 bits.

void f(void) {
  char *ptr;
  /* ... */
  unsigned int number = (unsigned int)ptr;
  /* ... */
}
Sign up to request clarification or add additional context in comments.

6 Comments

Downvoted: Pointers are not necessarily the same size on the same machine (N1570 6.2.5 p28). Of pointer to basic types only void* and any char* have that property. Second example works because there is a conversion from void* to uint16_t* inserted by the compiler if it's needed on that particular platform.
@user694733 I agree, fixed it now.
Unfortunately "since they're both pointers, and all pointers have the same size in the program." is still incorrect. Size of the pointers is irrelevant. void* to uint16_t* assignment works, because conversion is well defined (N1570 6.3.2.3 p1).
@user694733 what about the size in case of casting to an integer like here ?
Conversions between pointer and integer are allowed (6.3.2.3 p5-6), but the result is implementation defined, or in the worst case, undefined. So size does come into play here. If contents of the pointer can be represented in integer type, then conversion can be legal on some systems.
|
0

While there is no need to cast a pointer from type to void or void to type (with some limited exceptions not relevant here), the cast becomes of critical importance when you need to derefernce a void pointer. As you know a pointer stores as its value the address of something else. When dealing with pointers of the same type as the value, there are no need for any additional casts as the type has already been fixed. e.g.

int a[] = { 1, 3, 5, 7, 9 };
int *ap = a;
ap++;
printif (" ap points to : %d\n", *ap);

All good, no mystery. Now lets take a function that will iterate over the values in the array taking the array and number of elements as its parameters. But there is still another parameter needed to be able to iterate over the array passed as void *. What is it? The sizeof each element that make sup a.

Otherwise, since the array was passed as a void pointer, what would happen to pointer arithmetic? In the function, what would happen if we attempted ap++?

Therein lies the issue. When handling pointers as void pointers, they are very convenient to use, but when the values they hold must be manipulated, they must be cast so that the compiler knows size of the object pointed to, so it can know how many bytes exist between ap and the next value in your array located at ap++.

There is another caveat in type casting pointers. They must be cast in such a way that rules of strict aliasing are not violated and that type punning does not occur. See C11 N1570 Committee Draft — April 12, 2011 For that your will want to take a look at sections 6.5 (6) and (7) paying close attention to the notes included on the draft as well. Without going into the loopholes and qualifications, you can safely cast a void pointer to its effective type or to char. Any other combination is asking for an aliasing violation resulting in a type punned pointer.

So continuing with our example, if we were to pass our array a to a function as void * instead of int *, what would be required in our function to allow pointer arithmetic to work properly? We know from rule 6.5 (6) that we can cast to char and not run afoul of the strict-aliasing rules, so we may be able to handle it like this, where a is our array, n the number of elements, and sz the size of each element in the array:

void prnarray (void *a, size_t n, size_t sz)
{
    char *ap = a;
    printf ("\npointers printed from prnarray:\n\n");
    while (n--) {
        printf (" ap points to : %d\n", *ap);
        ap += sz;
    }
}

We can then step through the array without problem while adding the appropriate size/offset from each prior element.

What if we know we will be taking an array of int through the void pointer? That allows a cast directly to the effective type of the array and we no longer need to worry about passing the size. Pointer arithmetic will take care of the offset to/from each value within the array, e.g.

 void prnarrayint (void *a, size_t n)
{
    int *ap = a;
    printf ("\npointers printed from prnarrayint:\n\n");
    while (n--)
        printf (" ap points to : %d\n", *ap++);
}

While not as flexible as the previous function, where we were not limited to the type, there are many circumstances where you will know the type of pointer you are getting passed as void, and in those instances, casting to the effective type will simplify your code.

Now putting all the pieces together you could create a short illustrative example something as follows:

#include <stdio.h>

void prnarray (void *a, size_t n, size_t sz)
{
    char *ap = a;
    printf ("\npointers printed from prnarray:\n\n");
    while (n--) {
        printf (" ap points to : %d\n", *ap);
        ap += sz;
    }
}

void prnarrayint (void *a, size_t n)
{
    int *ap = a;
    printf ("\npointers printed from prnarrayint:\n\n");
    while (n--)
        printf (" ap points to : %d\n", *ap++);
}

int main (void) {

    int a[] = { 1, 3, 5, 7, 9 };
    int *ap = a;
    size_t i, n = sizeof a/ sizeof *a;
    i = n;

    printf ("\npointers printed in main:\n\n");
    while (i--)
        printf (" ap points to : %d\n", *ap++);

    prnarray (a, n, sizeof *a);  /* pass/print based on sz with cast to char */

    prnarrayint (a, n);  /* pass/print based on effective type of array */

    return 0;
}

Use/Output

$ ./bin/voidpcast

pointers printed in main:

 ap points to : 1
 ap points to : 3
 ap points to : 5
 ap points to : 7
 ap points to : 9

pointers printed from prnarray:

 ap points to : 1
 ap points to : 3
 ap points to : 5
 ap points to : 7
 ap points to : 9

pointers printed from prnarrayint:

 ap points to : 1
 ap points to : 3
 ap points to : 5
 ap points to : 7
 ap points to : 9

So in summary, the universal pointer void can be assigned to/from any type pointer and back without the need of a cast, however when any operation requires the pointer to be dereferenced to obtain the value pointed to, then an appropriate cast must be made to either its effective type or char in order to comply with the rules of strict-aliasing.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.