概括
Microsoft Visual Studio 中的 C/C++ 编译器在C 程序尝试将指向数据的指针(如or )转换为(即使这种类型实际上不是指向 的指针)的指针时发出警告C4090。更奇怪的是,同一个编译器默默地接受编译为 C++ 的相同代码。const
const void **
const char **
void *
const
这种不一致的原因是什么,为什么 Visual Studio(与其他编译器不同)在将指向指针的指针隐式转换const
为void *
?
细节
我有一个 C 程序,其中将变量参数列表中传递的 C 字符串读入数组(通过va_arg
调用的循环)。由于 C-strings 是 type const char *
,因此跟踪它们的数组是 type const char **
。这个指向带有内容的字符串的指针数组const
本身是动态分配的(使用calloc
),并且free
在函数返回之前(在处理 C 字符串之后)分配。
当我使用cl.exe
(在 Microsoft Visual C++ 中)编译此代码时,即使警告级别较低,free
调用也会触发警告C4090。由于free
需要 a void *
,这告诉我编译器不喜欢我将 a 转换const char **
为 a void *
。我创建了一个简单的示例来确认这一点,在其中我尝试将 a 转换const void **
为 a 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
微软关于警告 C4090 的文档说:
此警告针对 C 程序发出。在 C++ 程序中,编译器发出错误:C2440。
这是有道理的,因为 C++ 是一种比 C 更强类型的语言,并且 C 中允许的潜在危险的隐式转换在 C++ 中是不允许的。Microsoft 的文档看起来像是在 C 中针对相同代码或代码子集触发了C2440警告,这将在 C++ 中触发错误C2440 。
或者我是这么想的,直到我尝试将我的测试程序编译为 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
它默默地成功了。
这些构建是cl.exe
在 Windows 7 机器上使用 Microsoft Visual C++ 2010 Express Edition 的,但在 Windows XP 机器上,Visual Studio .NET 2003cl.exe
和 Visual C++ 2005 Express Edition 的cl.exe
. 因此,这似乎发生在所有版本上(尽管我没有在每个可能的版本上进行测试),并且在我的机器上设置 Visual Studio 的方式不是问题。
相同的代码在 Ubuntu 11.10 系统(版本字符串)上的 GCC 4.6.1 中编译没有问题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 中触发警告,也不会在 GCC 或 MSVC 中触发 C++ 中的警告,在我看来,从指针转换为指向 const 的指针void *
根本不应该被视为问题,因为 whilevoid *
是指向 non-const
的指针,指向 const 的指针也是指向 non- 的指针const
。
在我的实际代码(不是示例)中,我可以使用#pragma
指令或显式强制转换,或编译为 C++(呵呵),或者我可以忽略它。但我宁愿不做任何这些事情,至少在我明白为什么会发生之前不做。(以及为什么它不会在 C++ 中发生!)
我想到了一种可能的部分解释:与 C++ 不同,C 允许隐式转换void *
为任何指向数据的指针类型。所以我可以有一个指针从 to 隐式转换const char **
,void *
然后从void *
to隐式转换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++ STL 并且不使用高级集合的 C 程序,这种事情不是一个合理的选择。一些程序员在将警告视为错误的公司/组织政策下工作。C4090即使使用
/W1
. 人们以前一定遇到过这种情况。那些程序员是做什么的?