Eli*_*gan 18 c c++ const visual-studio visual-c++
当C程序尝试将指针转换为指向数据(如或)的指针时,Microsoft Visual Studio中的C/C++编译器会发出警告C4090(即使这样的类型实际上不是指针).更奇怪的是,同一个编译器默默接受编译为C++的相同代码.const
const void **
const char **
void *
const
什么是这种不一致的原因,以及为什么Visual Studio的(不像其他编译器)有隐含转换的指针指向一个问题const
成void *
?
我有一个C程序,其中在变量参数列表中传递的C字符串被读入一个数组(通过va_arg
调用的循环).由于C字符串是类型const char *
,跟踪它们的数组是类型const char **
.这个带有const
内容的字符串指针数组本身是动态分配的(带calloc
)和I free
它在函数返回之前(在处理完C字符串之后).
当我使用cl.exe
(在Microsoft Visual C++中)编译此代码时,即使警告级别较低,该free
调用也会触发警告C4090.因为free
需要一个void *
,这告诉我编译器不喜欢我已经转换const char **
为a void *
.我创建了一个简单的例子来证实这一点,我尝试将a转换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;
}
Run Code Online (Sandbox Code Playgroud)
然后我按如下方式编译它,确认这是触发警告的原因:
>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
Run Code Online (Sandbox Code Playgroud)
此警告是针对C程序发出的.在C++程序中,编译器发出错误:C2440.
这是有道理的,因为C++是一种比C更强类型的语言,并且在C++中不允许使用C中允许的潜在危险的隐式转换.微软的文档似乎警告C2440在C中触发相同的代码或代码的子集,这将触发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
Run Code Online (Sandbox Code Playgroud)
当相同的代码编译为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
Run Code Online (Sandbox Code Playgroud)
它默默地成功.
这些版本是cl.exe
在Windows 7计算机上使用Microsoft Visual C++ 2010 Express Edition ,但在Windows XP计算机上,在Visual Studio .NET 2003 cl.exe
和Visual C++ 2005 Express Edition 中都会出现相同的错误cl.exe
.所以这似乎发生在所有版本上(尽管我没有在每个可能的版本上进行测试)并且在我的机器上设置Visual Studio的方式不是问题.
在UCCntu 11.10系统(版本字符串gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
)上,相同的代码在GCC 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]
Run Code Online (Sandbox Code Playgroud)
它确实警告说q
,在分配之后永远不会从中读取,但该警告是有意义的并且是无关的.
除了在GCC不触发警报启用所有警告,并在任何GCC或MSVC不触发警告在C++中,在我看来,从指针转换为指向一个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;
}
Run Code Online (Sandbox Code Playgroud)
>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
Run Code Online (Sandbox Code Playgroud)
然而,如果这是有意的,那么:
为什么Microsoft文档表明在C中产生此警告的代码在C++中产生错误?
除了忽略或抑制警告之外,还有一个合理的替代方案,当一个人必须free
非const
指针const
指向const
数据的非指针时(如在我的实际情况中)?如果在C++中发生类似这样的事情,我可以将在变量参数列表中传递的字符串存储在某个高级STL容器中而不是数组中.对于没有访问C++ STL且不使用高级集合的C程序,这种事情不是一个合理的选择.
一些程序员在公司/组织政策下工作,将警告视为错误.C4090即使启用也启用/W1
.人们一定以前遇到过这种情况.那些程序员做了什么?
650*_*502 11
显然这只是VC++中的一个错误.
如果你声明const char **x;
结果是一个指向chars的"只读"指针的指针,它本身不是一个"只读"指针(我使用术语"只读",因为const
-ness term推错了概念,即被指向的字符是常量,而一般来说这是假的... const
引用和指针是引用或指针的属性,并且不会告诉指向或引用数据的常量).
任何读/写指针都可以转换为a void *
,VC++没有任何理由在编译代码时发出警告,无论C
是在C++
模式还是在模式下.
请注意,这不是正式问题,因为标准没有强制要求或不应该发出哪些警告,因此编译器可以自由地发出警告,以保证完全有效的代码仍然符合要求.VC++实际上发出了大量有效C++代码的警告......