Note that there are some explanatory texts on larger screens.

plurals
  1. POWhy does Visual C++ warn on implicit cast from const void ** to void * in C, but not in C++?
    primarykey
    data
    text
    <h2>Summary</h2> <p>The C/C++ compiler in Microsoft Visual Studio gives warning <a href="http://msdn.microsoft.com/en-us/library/k77bkb8d.aspx">C4090</a> when a C program tries to convert a pointer to pointer to <code>const</code> data (like <code>const void **</code> or <code>const char **</code>) to <code>void *</code> (even though such a type is not actually a pointer to <code>const</code>). Even more strangely, the same compiler silently accepts identical code compiled as C++.</p> <p>What is the reason for this inconsistency, and why does Visual Studio (unlike other compilers) have a problem with implicitly converting a pointer to pointer to <code>const</code> into a <code>void *</code>?</p> <h2>Details</h2> <p>I have a C program in which C-strings passed in a variable argument list are read into an array (by a loop in which <code>va_arg</code> is invoked). Since the C-strings are of type <code>const char *</code>, the array that keeps track of them is of type <code>const char **</code>. This array of pointers to strings with <code>const</code> content is itself allocated dynamically (with <code>calloc</code>) and I <code>free</code> it before the function returns (after the C-strings have been processed).</p> <p>When I compiled this code with <code>cl.exe</code> (in Microsoft Visual C++), even with a low warning level, the <code>free</code> call triggered warning <a href="http://msdn.microsoft.com/en-us/library/k77bkb8d.aspx">C4090</a>. Since <code>free</code> takes a <code>void *</code>, this told me that the compiler didn't like that I had converted a <code>const char **</code> to a <code>void *</code>. I created a simple example to confirm this, in which I try to convert a <code>const void **</code> to a <code>void *</code>:</p> <pre><code>/* cast.c - Can a const void** be cast implicitly to void* ? */ int main(void) { const void **p = 0; void *q; q = p; return 0; } </code></pre> <p>I then compiled it as follows, confirming that this was what triggered the warning:</p> <pre><code>&gt;cl cast.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. cast.c cast.c(7) : warning C4090: '=' : different 'const' qualifiers Microsoft (R) Incremental Linker Version 10.00.40219.01 Copyright (C) Microsoft Corporation. All rights reserved. /out:cast.exe cast.obj </code></pre> <p>Microsoft's <a href="http://msdn.microsoft.com/en-us/library/k77bkb8d.aspx">documentation on warning C4090</a> says:</p> <blockquote> <p>This warning is issued for C programs. In a C++ program, the compiler issues an error: C2440.</p> </blockquote> <p>That makes sense, since C++ is a more strongly typed language than C, and potentially dangerous implicit casts allowed in C are disallowed in C++. Microsoft's documentation makes it seem like warning <a href="http://msdn.microsoft.com/en-us/library/k77bkb8d.aspx">C2440</a> is triggered in C for the same code, or a subset of the code, that would trigger error <a href="http://msdn.microsoft.com/en-us/library/sy5tsf8z.aspx">C2440</a> in C++.</p> <p>Or so I thought, until I tried compiling my test program as C++ (the <code>/TP</code> flag does this):</p> <pre><code>&gt;cl /TP cast.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. cast.c Microsoft (R) Incremental Linker Version 10.00.40219.01 Copyright (C) Microsoft Corporation. All rights reserved. /out:cast.exe cast.obj </code></pre> <p>When the same code is compiled as C++, no error or warning occurs. To be sure, I rebuilt, telling the compiler to warn as aggressively as possible:</p> <pre><code>&gt;cl /TP /Wall cast.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. cast.c Microsoft (R) Incremental Linker Version 10.00.40219.01 Copyright (C) Microsoft Corporation. All rights reserved. /out:cast.exe cast.obj </code></pre> <p>It succeeds silently.</p> <p>Those builds were with the Microsoft Visual C++ 2010 Express Edition's <code>cl.exe</code> on a Windows 7 machine, but the same errors occur on a Windows XP machine, in both Visual Studio .NET 2003's <code>cl.exe</code> and Visual C++ 2005 Express Edition's <code>cl.exe</code>. So it seems this happens on all versions (though I have not tested on every possible version) and is not a problem with the way Visual Studio is set up on my machines.</p> <p>The same code compiles without a problem in GCC 4.6.1 on an Ubuntu 11.10 system (version string <code>gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1</code>), set to warn as aggressively as possible, as C89, C99, and C++:</p> <pre><code>$ gcc -ansi -pedantic -Wall -Wextra -o cast cast.c cast.c: In function ‘main’: cast.c:6:11: warning: variable ‘q’ set but not used [-Wunused-but-set-variable] $ gcc -std=c99 -pedantic -Wall -Wextra -o cast cast.c cast.c: In function ‘main’: cast.c:6:11: warning: variable ‘q’ set but not used [-Wunused-but-set-variable] $ g++ -x c++ -ansi -pedantic -Wall -Wextra -o cast cast.c cast.c: In function ‘int main()’: cast.c:6:11: warning: variable ‘q’ set but not used [-Wunused-but-set-variable] </code></pre> <p>It does warn that <code>q</code> is never read from after being assigned, but that warning makes sense and is unrelated.</p> <p>Besides not triggering a warning in GCC with all warnings enabled, and not triggering a warning in C++ in either GCC or MSVC, it seems to me that converting from pointer to pointer to const to <code>void *</code> should not be considered a problem at all, because while <code>void *</code> is a pointer to non-<code>const</code>, a pointer to a pointer to const is also a pointer to non-<code>const</code>.</p> <p>In my real-world code (not the example), I can silence this with a <code>#pragma</code> directive, or an explicit cast, or by compiling as C++ (heh heh), or I can just ignore it. But I'd rather not do any of those things, at least not before I understand why this is happening. (And why it doesn't happen in C++!)</p> <p>One possible, partial explanation occurs to me: Unlike C++, C allows implicit casting from <code>void *</code> to any pointer-to-data type. So I could have a pointer implicitly converted from <code>const char **</code> to <code>void *</code>, and then implicitly converted from <code>void *</code> to <code>char **</code>, thereby making it possible to modify constant data it points to pointers to, without a cast. That would be bad. But I don't see how that is any worse than all sorts of other things that are allowed by C's weaker type-safety.</p> <p>I guess maybe this warning makes sense given the choice not to warn when a non-<code>void</code> pointer type is converted to <code>void *</code>:</p> <pre><code>/* cast.c - Can a const void** be cast implicitly to void* ? */ int main(void) { const void **p = 0; void *q; q = p; return 0; }</code></pre> <pre><code>&gt;cl /Wall voidcast.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. voidcast.c Microsoft (R) Incremental Linker Version 10.00.40219.01 Copyright (C) Microsoft Corporation. All rights reserved. /out:voidcast.exe voidcast.obj </code></pre> <p>And yet, if that is intentional, then:</p> <ol> <li><p>Why does the Microsoft documentation indicate that code producing this warning in C produces an error in C++?</p></li> <li><p>Besides ignoring or suppressing the warning, is there any reasonable alternative, when one must <code>free</code> a non-<code>const</code> pointer to non-<code>const</code> pointer to <code>const</code> data (as in my real-world situation)? If something like this happened in C++, I could store the strings passed in the variable argument list in some high-level STL container instead of an array. For a C program without access to the C++ STL and which doesn't otherwise use high-level collections, that sort of thing is not a reasonable option.</p></li> <li><p>Some programmers work under a corporate/organizational policy of treating warnings as errors. <a href="http://msdn.microsoft.com/en-us/library/k77bkb8d.aspx">C4090</a> is enabled even with <code>/W1</code>. People must have encountered this before. What do those programmers do?</p></li> </ol>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload