Почему Visual C ++ предупреждает о неявном приведении из const void ** к void * в C, но не в C ++?

Summary

Компилятор C / C ++ в Microsoft Visual Studio выдает предупреждениеC4090 когда программа на C пытается преобразовать указатель в указатель наconst данные (какconst void ** или жеconst char **) чтобыvoid * (хотя такой тип на самом деле не является указателем наconst). Еще более странно, что тот же компилятор молча принимает идентичный код, скомпилированный как C ++.

В чем причина этого несоответствия и почему Visual Studio (в отличие от других компиляторов) имеет проблему с неявным преобразованием указателя на указатель наconst вvoid *?

Details

У меня есть 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?

Ответы на вопрос(2)

Ваш ответ на вопрос