Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's wrong with casting like (void**)&device_array?

Tags:

c

cuda

There is this answer on another question about the use of cudaMalloc((void**)&device_array, num_bytes), which uses void** as output argument instead of passing a void* as return value like the standard malloc.

It criticizes NVIDIA's API and states :

Casting, as in (void**)&device_array, is invalid C and results in undefined behavior.

and has been upvoted several times (8 as of now), so I assume there is some truth in it.

I don't understand what's wrong with casting there.

  • What is invalid C here?
  • In what case would this lead to undefined behavior?

All I know is that it compiles without warning and runs with the intended behavior for me. But I am not knowledgeable with C up to standard specification level.

like image 277
bct Avatar asked Jun 07 '16 05:06

bct


People also ask

What does casting to void mean?

Casting to void is used to suppress compiler warnings. The Standard says in §5.2. 9/4 says, Any expression can be explicitly converted to type “cv void.” The expression value is discarded. Follow this answer to receive notifications.

Do I need to cast a void pointer?

A void pointer is a pointer that can point to any type of object, but does not know what type of object it points to. A void pointer must be explicitly cast into another type of pointer to perform indirection. A null pointer is a pointer that does not point to an address. A void pointer can be a null pointer.

Can you cast any pointer to a void pointer?

Any pointer to an object, optionally type-qualified, can be converted to void* , keeping the same const or volatile qualifications.

Why is type casting used to access the value of any variable using a void pointer?

Why we use void pointers? We use void pointers because of its reusability. Void pointers can store the object of any type, and we can retrieve the object of any type by using the indirection operator with proper typecasting.


1 Answers

The problem is that void* has a special meaning in C, with special rules (1). It is the only pointer type to/from which you can safely convert any other pointer type. However, these special rules do not apply recursively to void**.

Meaning that code like int* ptr = malloc(x); is perfectly fine, but

int* ptr; 
cudaMalloc(&ptr, x); // bad

is not fine! A pointer conversion from int** to void** is not well-defined. In theory this could cause undefined behavior and misalignment (2).

In addition, there might also be problems with pointer aliasing. The compiler is free to assume that the contents of a void* is never accessed through a int** and could therefore optimize the code in unexpected ways, leading to undefined behavior for violation of the strict aliasing rule (6.5).

Which means you will have to write code like this in order to safely use the function:

void* vptr; 
int*  iptr;

cudaMalloc(&vptr, x);
iptr = vptr;

(1) C11 6.3.2.3/1:

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

(2) C11 6.3.2.3/7:

A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.

like image 111
Lundin Avatar answered Sep 21 '22 21:09

Lundin