Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conversion to void** on different compilers

Tags:

c++

c

cuda

I've been running the following code through different compilers:

int main()
{
    float **a;
    void **b;
    b = a;
}

From what I've been able to gather, void ** is not a generic pointer which means that any conversion from another pointer should not compile or at least throw a warning. However, here are my results (all done on Windows):

  • gcc - Throws a warning, as expected.
  • g++ - Throws an error, as expected (this is due to the less permissive typing of C++, right?)
  • MSVC (cl.exe) - Throws no warnings whatsoever, even with /Wall specified.

My question is: Am I missing something about the whole thing and is there any specific reason why MSVC does not produce a warning? MSVC does produce a warning when converting from void ** to float **.

Another thing of note: If I replace a = b with the explicit conversion a = (void **)b, none of the compilers throw a warning. I thought this should be an invalid cast, so why wouldn't there be any warnings?

The reason I am asking this question is because I was starting to learn CUDA and in the official Programming Guide (https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#device-memory) the following code can be found:

// Allocate vectors in device memory
float* d_A;
cudaMalloc(&d_A, size);

which should perform an implicit conversion to void ** for &d_A, as the first argument of cudaMalloc is of type void **. Similar code can be found all over the documentation. Is this just sloppy work on NVIDIA's end or am I, again, missing something? Since nvcc uses MSVC, the code compiles without warnings.

like image 711
CaptainProton42 Avatar asked Jan 21 '20 15:01

CaptainProton42


People also ask

What does casting to void * do?

Finally, a function call can be cast to void in order to explicitly discard a return value. For example, printf returns a value, but it is seldom used.

What is void* in Cpp?

When used as a function return type, the void keyword specifies that the function doesn't return a value. When used for a function's parameter list, void specifies that the function takes no parameters. When used in the declaration of a pointer, void specifies that the pointer is "universal."

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.


1 Answers

Am I missing something about the whole thing and is there any specific reason why MSVC does not produce a warning? MSVC does produce a warning when converting from void ** to float **

This assignment without a cast is a constraint violation, so a standard compliant compiler will print a warning or error. However, MSVC is not fully compliant C implementation.

Another thing of note: If I replace a = b with the explicit conversion a = (void **)b, none of the compilers throw a warning. I thought this should be an invalid cast, so why wouldn't there be any warnings?

Pointer conversions via a cast are allowed in some situations. The C standard says the following in section 6.3.2.3p7:

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. Otherwise, when converted back again, the result shall compare equal to the original pointer. When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.

So you can convert between pointer types provided there are no alignment issues and you only convert back (unless the target is a char *).

float* d_A;
cudaMalloc(&d_A, size);

...

Is this just sloppy work on NVIDIA's end or am I, again, missing something?

Presumably, this function is dereferencing the given pointer and writing the address of some allocated memory. That would mean it is trying to write to a float * as if it were a void *. This is not the same as the typical conversion to/from a void *. Strictly speaking this looks like undefined behavior, although it "works" because modern x86 processors (when not in real mode) use the same representation for all pointer types.

like image 98
dbush Avatar answered Sep 20 '22 13:09

dbush