为什么 Visual C++ 在 C 中对从 const void ** 到 void * 的隐式转换发出警告,但在 C++ 中却没有?
Microsoft Visual Studio 中的 C/C++ 编译器在 C 程序尝试时发出警告 C4090将指向 const
数据的指针(如 const void **
或 const char **
)的指针转换为 void *
(即使这样的类型实际上不是指向 const
的指针).更奇怪的是,同一个编译器默默地接受编译为 C++ 的相同代码.
The C/C++ compiler in Microsoft Visual Studio gives warning C4090 when a C program tries to convert a pointer to pointer to const
data (like const void **
or const char **
) to void *
(even though such a type is not actually a pointer to const
). Even more strangely, the same compiler silently accepts identical code compiled as C++.
这种不一致的原因是什么,为什么 Visual Studio(与其他编译器不同)在将指向 const
的指针隐式转换为 void *
?
What is the reason for this inconsistency, and why does Visual Studio (unlike other compilers) have a problem with implicitly converting a pointer to pointer to const
into a void *
?
我有一个 C 程序,其中将变量参数列表中传递的 C 字符串读入数组(通过调用 va_arg
的循环).由于 C 字符串是 const char *
类型,因此跟踪它们的数组是 const char **
类型.这个指向具有 const
内容的字符串的指针数组本身是动态分配的(使用 calloc
)并且我在函数返回之前 free
它(在 C- 字符串已被处理).
I have a C program in which C-strings passed in a variable argument list are read into an array (by a loop in which va_arg
is invoked). Since the C-strings are of type const char *
, the array that keeps track of them is of type const char **
. This array of pointers to strings with const
content is itself allocated dynamically (with calloc
) and I free
it before the function returns (after the C-strings have been processed).
当我使用 cl.exe
(在 Microsoft Visual C++ 中)编译此代码时,即使警告级别较低,free
调用也会触发警告 C4090.由于 free
采用 void *
,这告诉我编译器不喜欢我将 const char **
转换为 <代码>无效*代码>.我创建了一个简单的示例来确认这一点,其中我尝试将 const void **
转换为 void *
:
When I compiled this code with cl.exe
(in Microsoft Visual C++), even with a low warning level, the free
call triggered warning C4090. Since free
takes a void *
, this told me that the compiler didn't like that I had converted a const char **
to a void *
. I created a simple example to confirm this, in which I try to convert a const void **
to 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;
}
然后我将其编译如下,确认这是触发警告的原因:
I then compiled it as follows, confirming that this was what triggered the warning:
>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 的文档 说:
Microsoft's documentation on warning C4090 says:
此警告针对 C 程序发出.在 C++ 程序中,编译器发出错误:C2440.
This warning is issued for C programs. In a C++ program, the compiler issues an error: C2440.
这是有道理的,因为 C++ 是一种比 C 更强类型的语言,并且 C 中允许的潜在危险的隐式转换在 C++ 中是不允许的.微软的文档使它看起来像警告 C2440 在 C 中针对相同的代码或子集触发的代码,这将在 C++ 中触发错误 C2440.
That makes sense, since C++ is a more strongly typed language than C, and potentially dangerous implicit casts allowed in C are disallowed in C++. Microsoft's documentation makes it seem like warning C2440 is triggered in C for the same code, or a subset of the code, that would trigger error C2440 in C++.
我是这么想的,直到我尝试将我的测试程序编译为 C++(/TP
标志执行此操作):
Or so I thought, until I tried compiling my test program as C++ (the /TP
flag does this):
>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++ 时,不会出现错误或警告.可以肯定的是,我重建了,告诉编译器尽可能积极地发出警告:
When the same code is compiled as C++, no error or warning occurs. To be sure, I rebuilt, telling the compiler to warn as aggressively as possible:
>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 的 cl.exe
和 Visual C++ 2005 Express Edition 的 cl.exe
.因此,似乎所有版本都会发生这种情况(尽管我没有对每个可能的版本进行测试),并且在我的机器上设置 Visual Studio 的方式没有问题.
Those builds were with the Microsoft Visual C++ 2010 Express Edition's cl.exe
on a Windows 7 machine, but the same errors occur on a Windows XP machine, in both Visual Studio .NET 2003's cl.exe
and Visual C++ 2005 Express Edition's cl.exe
. So it seems this happens on all versions (though I have not tested on every possible version) and is not a problem with the way Visual Studio is set up on my machines.
相同的代码在 Ubuntu 11.10 系统上的 GCC 4.6.1 中编译没有问题(版本字符串 gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
),设置为警告为尽可能积极,如 C89、C99 和 C++:
The same code compiles without a problem in GCC 4.6.1 on an Ubuntu 11.10 system (version string gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1
), set to warn as aggressively as possible, as C89, C99, and 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
在被分配后永远不会被读取,但该警告是有意义的并且是不相关的.
It does warn that q
is never read from after being assigned, but that warning makes sense and is unrelated.
除了在启用所有警告的情况下不会在 GCC 中触发警告,也不会在 GCC 或 MSVC 中触发 C++ 中的警告,在我看来,从指向 const 的指针转换为 void *
根本不应该被认为是一个问题,因为虽然 void *
是指向非 const
的指针,但指向 const 的指针也是指向非 <代码>常量.
Besides not triggering a warning in GCC with all warnings enabled, and not triggering a warning in C++ in either GCC or MSVC, it seems to me that converting from pointer to pointer to const to void *
should not be considered a problem at all, because while void *
is a pointer to non-const
, a pointer to a pointer to const is also a pointer to non-const
.
在我的真实代码(不是示例)中,我可以使用 #pragma
指令或显式强制转换,或编译为 C++(呵呵),或者我可以忽略它.但我宁愿不做任何这些事情,至少在我明白为什么会发生之前不做.(以及为什么它不会在 C++ 中发生!)
In my real-world code (not the example), I can silence this with a #pragma
directive, or an explicit cast, or by compiling as C++ (heh heh), or I can just ignore it. But I'd rather not do any of those things, at least not before I understand why this is happening. (And why it doesn't happen in C++!)
我想到一个可能的部分解释:与 C++ 不同,C 允许从 void *
隐式转换为任何指向数据的指针类型.所以我可以将一个指针从 const char **
隐式转换为 void *
,然后从 void *
隐式转换为 char**
,从而可以修改它指向的指针的常量数据,而无需强制转换.那会很糟糕.但我不认为这比 C 较弱的类型安全所允许的所有其他事情更糟糕.
One possible, partial explanation occurs to me: Unlike C++, C allows implicit casting from void *
to any pointer-to-data type. So I could have a pointer implicitly converted from const char **
to void *
, and then implicitly converted from void *
to char **
, thereby making it possible to modify constant data it points to pointers to, without a cast. That would be bad. But I don't see how that is any worse than all sorts of other things that are allowed by C's weaker type-safety.
如果非void
指针类型转换为void *
时选择不发出警告,我想这个警告可能是有意义的:
I guess maybe this warning makes sense given the choice not to warn when a non-void
pointer type is converted to 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
然而,如果这是故意的,那么:
And yet, if that is intentional, then:
为什么 Microsoft 文档指出在 C 中产生此警告的代码会在 C++ 中产生错误?
Why does the Microsoft documentation indicate that code producing this warning in C produces an error in C++?
除了忽略或抑制警告之外,是否有任何合理的选择,当必须 free
一个非 const
指向非 const<的指针时/code> 指向
const
数据的指针(在我的实际情况中)?如果在 C++ 中发生这样的事情,我可以将变量参数列表中传递的字符串存储在一些高级 STL 容器中,而不是数组中.对于无法访问 C++ STL 且不使用高级集合的 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.
一些程序员在将警告视为错误的公司/组织政策下工作.C4090 即使使用 /W1
也已启用.人们以前一定遇到过这种情况.那些程序员是做什么的?
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?
推荐答案
显然这只是VC++中的一个bug.
Apparently this is simply a bug in VC++.
如果你声明 const char **x;
结果是一个指向 chars 的只读"指针的指针,它本身不是一个只读"指针(我使用术语只读",因为 const
-ness 术语提出了错误的概念,即所指向的字符是常量,而这通常是错误的...... const
带有引用和指针是引用或指针的一个属性,它不说明指向或引用数据的常量性.
If you declare const char **x;
the result is a pointer to a "read-only" pointer to chars, and it's not itself a "read-only" pointer (I use the term "read-only" because const
-ness term pushes the wrong concept that the character being pointed to is constant while this is false in general... const
with references and pointers is a property of the reference or of the pointer and tells nothing about constness of the pointed-to or referenced data).
任何读/写指针都可以转换为 void *
并且 VC++ 在编译该代码时没有真正的理由发出警告,无论是在 C
还是在 C++
模式.
Any read/write pointer can be converted to a void *
and VC++ has no real reason to emit a warning when compiling that code, neither in C
nor in C++
mode.
请注意,这不是正式的问题,因为该标准没有规定应该或不应该发出哪些警告,因此编译器可以自由地为仍然保持合规的完全有效的代码发出警告.VC++ 实际上会针对有效的 C++ 代码发出大量警告...
Note that this is not formally a problem because the standard doesn't mandate which warnings should or should not be issued and therefore a compiler is free to emit warnings for perfectly valid code still remaining compliant. VC++ actually emits a plethora of those warnings for valid C++ code...
相关文章