Почему Visual C ++ предупреждает о неявном приведении из const void ** к void * в C, но не в C ++?
Компилятор C / C ++ в Microsoft Visual Studio выдает предупреждениеC4090 когда программа на C пытается преобразовать указатель в указатель наconst
данные (какconst void **
или жеconst char **
) чтобыvoid *
(хотя такой тип на самом деле не является указателем наconst
). Еще более странно, что тот же компилятор молча принимает идентичный код, скомпилированный как C ++.
В чем причина этого несоответствия и почему Visual Studio (в отличие от других компиляторов) имеет проблему с неявным преобразованием указателя на указатель наconst
вvoid *
?
У меня есть C-программа, в которой C-строки, переданные в списке переменных аргумент, считываются в массив (с помощью цикла, в которомva_arg
вызывается). Поскольку C-строки имеют типconst char *
массив, который отслеживает их, имеет типconst char **
, Этот массив указателей на строки сconst
сам контент распределяется динамически (сcalloc
) и яfree
это до возврата функции (после обработки C-строк).
Когда я скомпилировал этот код сcl.exe
(в Microsoft Visual C ++), даже с низким уровнем предупреждения,free
сработало предупреждениеC4090, посколькуfree
занимаетvoid *
это говорит мне, что компилятору не понравилось, что я преобразовалconst char **
кvoid *
, Я создал простой пример, чтобы подтвердить это, в котором я пытаюсь преобразоватьconst void **
кvoid *
:
<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>
Затем я скомпилировал его следующим образом, подтвердив, что именно это вызвало предупреждение:
<code>>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>
Microsoft & APOS; sдокументация по предупреждению C4090 говорит:
This warning is issued for C programs. In a C++ program, the compiler issues an error: C2440.
Это имеет смысл, поскольку C ++ является более строго типизированным языком, чем C, и потенциально опасные неявные преобразования, разрешенные в C, запрещены в C ++. В документации Microsoft это выглядит как предупреждениеC2440 запускается в C для того же кода или подмножества кода, что приведет к ошибкеC2440 в C ++.
Или так я думал, пока я не попытался скомпилировать мою тестовую программу как C ++ (/TP
флаг делает это):
<code>>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>
Когда тот же код скомпилирован как C ++, ошибки или предупреждения не появляются. Чтобы быть уверенным, я перестроил, сказав компилятору предупреждать как можно более агрессивно:
<code>>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>
Это происходит тихо.
Эти сборки были с использованием Microsoft Visual C ++ 2010 Express Edition.cl.exe
на компьютере с Windows 7, но на компьютере с Windows XP возникают те же ошибки, что и в Visual Studio .NET 2003cl.exe
и Visual C ++ 2005 Express Editioncl.exe
, Таким образом, похоже, что это происходит во всех версиях (хотя я не проверял на всех возможных версиях) и не является проблемой с настройкой Visual Studio на моих компьютерах.
Тот же код без проблем компилируется в GCC 4.6.1 в системе Ubuntu 11.10 (строка версииgcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
), установите для предупреждения как можно более агрессивно, как C89, C99 и C ++:
<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>
Это предупреждает, чтоq
никогда не читается после назначения, но это предупреждение имеет смысл и не связано.
Кроме того, чтобы не вызывать предупреждение в GCC со всеми включенными предупреждениями и не вызывать предупреждение в C ++ ни в GCC, ни в MSVC, мне кажется, что преобразование из указателя в указатель в const вvoid *
не следует считать проблемой вообще, потому что покаvoid *
это указатель наconst
указатель на указатель на const также является указателем наconst
.
В моем реальном коде (не в примере) я могу замолчать это с помощью#pragma
директива, или явное приведение, или путем компиляции как C ++ (хе-хе), или я могу просто проигнорировать это. Но я бы предпочел не делать ничего из этого, по крайней мере, прежде чем я пойму, почему это происходит. (И почему это не происходит в C ++!)
Одно возможное, частичное объяснение приходит ко мне: в отличие от C ++, C допускает неявное приведение изvoid *
к любому указателю на тип данных. Таким образом, я мог бы неявно преобразовать указатель изconst char **
вvoid *
, а затем неявно преобразуется изvoid *
вchar **
, тем самым позволяя изменять константные данные, на которые он указывает, без приведения. Это было бы плохо. Но я не вижу, как это хуже, чем все виды других вещей, которые допускаются более слабой типовой безопасностью C.
Я думаю, может быть, это предупреждение имеет смысл, учитывая выбор не предупреждать, когда неvoid
тип указателя преобразуется вvoid *
:
<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>
<code>>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>
И все же, если это умышленно, то:
Why does the Microsoft documentation indicate that code producing this warning in C produces an error in C++?
Besides ignoring or suppressing the warning, is there any reasonable alternative, when one must free
a non-const
pointer to non-const
pointer to const
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.
Some programmers work under a corporate/organizational policy of treating warnings as errors. C4090 is enabled even with /W1
. People must have encountered this before. What do those programmers do?