当C程序尝试将指针转换为指向const
数据的指针(如const void **
或const char **
时,Microsoft Visual Studio中的C / C ++编译器会发出警告C4090 )到void *
(即使这样的类型实际上不是指向const
的指针)。更奇怪的是,同一个编译器默默接受编译为C ++的相同代码。
这种不一致的原因是什么,为什么Visual Studio(与其他编译器不同)在将指向const
的指针隐式转换为void *
时会出现问题?
我有一个C程序,其中在变量参数列表中传递的C字符串被读入一个数组(通过调用va_arg
的循环)。由于C字符串的类型为const char *
,因此跟踪它们的数组的类型为const char **
。指向具有const
内容的字符串的指针数组本身在函数返回之前(在处理C字符串之后)动态分配(calloc
)和I free
。
当我使用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
微软documentation on warning C4090说:
此警告是针对C程序发出的。在C ++程序中,编译器发出错误:C2440。
这是有道理的,因为C ++是一种比C更强类型的语言,并且在C ++中不允许使用C中允许的潜在危险的隐式转换。微软的文档似乎在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
它默默地成功。
这些版本是在Windows 7计算机上使用Microsoft Visual C ++ 2010 Express Edition的cl.exe
,但在Windows XP计算机上,在Visual Studio .NET 2003和#39;中都会出现相同的错误。 s 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]
它确实警告q
在被分配后永远不会被读取,但该警告是有意义的并且是无关的。
除了在GCC中没有启用所有警告的情况下触发警告,并且在GCC或MSVC中没有在C ++中触发警告之外,在我看来,不应该考虑从指针转换为const到void *
的指针一个问题,因为虽然void *
是指向非const
的指针,但指向const的指针也是指向非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 ++ STL并且不使用高级集合的C程序,这种事情不是一个合理的选择。
一些程序员根据将警告视为错误的公司/组织政策工作。使用/W1
即可启用C4090。人们一定以前遇到过这种情况。这些程序员做了什么?
答案 0 :(得分:11)
显然这只是VC ++中的一个错误。
如果你声明const char **x;
结果是一个指向chars的“只读”指针的指针,它本身不是一个“只读”指针(我使用术语“只读”,因为{ {1}} - ness term推出错误的概念,指向的字符是常量,而一般来说这是假的... const
带引用和指针是引用或指针的属性,并且什么都不告诉关于指向或引用数据的常量。)
任何读/写指针都可以转换为const
,VC ++没有理由在编译该代码时发出警告,无论是void *
还是C
模式。< / p>
请注意,这不是正式问题,因为标准没有强制要求或不应该发出哪些警告,因此编译器可以自由地发出警告,以保证完全有效的代码仍然符合要求。 VC ++实际上会发出大量有效C ++代码的警告......
答案 1 :(得分:1)
就像6502所说这似乎是编译器中的一个错误。但是,你也会问你应该怎么做。
我的回答是你应该为免费电话添加一个明确的演员表,然后是一个解释为什么需要它的评论。编译器中的错误确实发生了,使用最简单的解决方法并添加一个注释,以便在以后解决该错误时对其进行测试。
还向编译器供应商报告错误的额外点。
至于1.它似乎是指隐式地将const T *
转换为void *
,这应该是C中的警告和C ++中的错误。