Компилятор 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 *
:
/* cast.c - Can a const void** be cast implicitly to void* ? */
int main(void)
{
const void **p = 0;
void *q;
q = p;
return 0;
}
. Затем я скомпилировал его следующим образом, подтвердив, что именно это вызвало предупреждение:
>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
Документация Microsoft по предупреждению C4090 говорит:
Это предупреждение выдается для программ на C. В программе на С++компилятор выдает ошибку :C2440.
Это имеет смысл, так как C++ является языком с более строгой типизацией, чем C, и потенциально опасные неявные приведения типов, разрешенные в C, запрещены в C++. В документации Microsoft создается впечатление, что предупреждение C2440 запускается в C для того же кода или подмножества кода, который вызывает ошибку C2440 в C++.
По крайней мере, я так думал, пока не попытался скомпилировать свою тестовую программу как C++ (флаг /TP
делает это):
>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
Когда тот же код компилируется как C++, не возникает ни ошибки, ни предупреждения. Чтобы быть уверенным, я перестроил, сказав компилятору предупреждать как можно агрессивнее.:
>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
Это удается молча.
Эти сборки были с Microsoft Visual C++ 2010 Express Edition cl.exe
на компьютере с Windows 7, но те же ошибки возникают на компьютере с Windows XP как в Visual Studio.NET 2003 cl.exe
, так и в Visual C++ 2005 Express Edition. cl.exe
. Похоже, это происходит во всех версиях (, хотя я не тестировал все возможные версии ), и это не проблема с тем, как Visual Studio настроена на моих машинах.
Тот же самый код компилируется без проблем в GCC 4.6.1 в системе Ubuntu 11.10 (строка версииgcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
), настроенная на максимально агрессивное предупреждение, как C89, C99 и C++:
$ 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]
Предупреждает что q
никогда не читается после назначения, но это предупреждение имеет смысл и не связано.
Помимо отсутствия срабатывания предупреждения в GCC со всеми включенными предупреждениями и отсутствия срабатывания предупреждения в C++ ни в GCC, ни в MSVC, мне кажется, что преобразование указателя в указатель в const в void *
не должно рассматриваться как проблема. вообще, потому что хотя void *
является указателем на non-const
, указатель на указатель на const также является указателем на non-const
.
В моем реальном -мировом коде (не в примере )я могу отключить это с помощью директивы #pragma
, или явного приведения, или путем компиляции как C++ (хе-хе ), или я могу просто игнорировать это.Но я бы предпочел не делать ничего из этого, по крайней мере, пока не пойму, почему это происходит. (И почему этого не происходит в C++!)
Мне приходит в голову одно возможное частичное объяснение. :В отличие от C++, C допускает неявное приведение от void *
к любому типу данных указателя -на -. Таким образом, я мог бы неявно преобразовать указатель из const char **
в void *
, а затем неявно преобразовать из void *
в char **
, тем самым сделав возможным изменение постоянных данных, на которые он указывает, без приведения. Это было бы плохо. Но я не вижу, чем это хуже, чем всевозможные другие вещи, разрешенные более слабой безопасностью типа -C.
Думаю, это предупреждение имеет смысл, учитывая выбор не предупреждать, когда тип указателя, отличного от -void
, преобразуется вvoid *
:
/* cast.c - Can a const void** be cast implicitly to void* ? */
int main(void)
{
const void **p = 0;
void *q;
q = p;
return 0;
}
>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
И все же, если это сделано намеренно, то:
Почему в документации Microsoft указан этот код создание этого предупреждения в C приводит к ошибке в C++?
Помимо игнорирования или подавления предупреждения, есть ли разумная альтернатива, когда нужно free
не-const
указатель на не-const
указатель на const
данные (, как в моем реальном -мире ситуация )? Если бы что-то подобное произошло в C++, я мог бы хранить строки, переданные в списке переменных аргументов, в каком-нибудь контейнере STL высокого -уровня вместо массива. Для программы на C, не имеющей доступа к C++ STL и не использующей коллекции высокого -уровня, такой вариант не является разумным.
Некоторые программисты работают в соответствии с корпоративной/организационной политикой обработки предупреждений как ошибок. C4090 активируется даже при /W1
. Люди наверняка уже сталкивались с этим. Чем занимаются эти программисты?