简体   繁体   English

为什么Visual C ++会在C中警告从const void **到void *的隐式转换,而不是在C ++中?

[英]Why does Visual C++ warn on implicit cast from const void ** to void * in C, but not in C++?

Summary 摘要

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 ). 当C程序试图将指向const数据(如const void **const char ** )的指针转换为void *时,Microsoft Visual Studio中的C / C ++编译器会发出警告C4090 (即使这样的类型实际上不是指向const的指针。 Even more strangely, the same compiler silently accepts identical code compiled as C++. 更奇怪的是,同一个编译器默默接受编译为C ++的相同代码。

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 * ? 这种不一致的原因是什么,为什么Visual Studio(与其他编译器不同)有一个问题,即将指向const的指针隐式转换为void *

Details 细节

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). 我有一个C程序,其中在变量参数列表中传递的C字符串被读入一个数组(通过调用va_arg的循环)。 Since the C-strings are of type const char * , the array that keeps track of them is of type const char ** . 由于C字符串的类型为const char * ,因此跟踪它们的数组的类型为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). 这个带有const内容的字符串指针数组本身是动态分配的(使用calloc ),我在函数返回之前free它(在处理C字符串之后)。

When I compiled this code with cl.exe (in Microsoft Visual C++), even with a low warning level, the free call triggered warning C4090 . 当我用cl.exe (在Microsoft Visual C ++中)编译此代码时,即使警告级别较低, free调用也会触发警告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 * . 由于free取一个void * ,这告诉我编译器不喜欢我将const char **转换为void * I created a simple example to confirm this, in which I try to convert a const void ** to a 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;
}

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's documentation on warning C4090 says: 微软关于C4090警告文档说:

This warning is issued for C programs. 此警告是针对C程序发出的。 In a C++ program, the compiler issues an error: C2440. 在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++. 这是有道理的,因为C ++是一种比C更强类型的语言,并且在C ++中不允许使用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++. 微软的文档似乎警告C2440在C中触发相同的代码或代码的子集,这将触发C ++中的错误C2440

Or so I thought, until I tried compiling my test program as C++ (the /TP flag does this): 或者我想,直到我尝试将我的测试程序编译为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

When the same code is compiled as C++, no error or warning occurs. 当相同的代码编译为C ++时,不会发生错误或警告。 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

It succeeds silently. 它默默地成功。

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 . 这些版本是在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中都会出现相同的错误。 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. 所以这似乎发生在所有版本上(尽管我没有在每个可能的版本上进行测试)并且在我的机器上设置Visual Studio的方式不是问题。

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 (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1 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]

It does warn that q is never read from after being assigned, but that warning makes sense and is unrelated. 它确实警告q在被分配之后永远不会从中读取,但是该警告是有意义的并且是无关的。

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 . 除了在GCC中没有启用所有警告的情况下触发警告,并且在GCC或MSVC中没有在C ++中触发警告之外,在我看来,从指针到指针转换为const到void *不应该被认为是一个问题,因为void *是指向非const的指针,指向const的指针也是指向非const的指针。

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. 在我的真实世界代码(不是示例)中,我可以使用#pragma指令或显式强制转换,或者编译为C ++(嘿嘿)来沉默,或者我可以忽略它。 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 ++中发生!)

One possible, partial explanation occurs to me: Unlike C++, C allows implicit casting from void * to any pointer-to-data type. 一个可能的,部分解释发生在我身上:与C ++不同,C允许从void *隐式转换为任何指向数据的指针类型。 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. 所以我可以将一个指针从const char **隐式转换为void * ,然后从void *隐式转换为char ** ,从而可以修改指向指针的常量数据,而无需强制转换。 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. 但我不知道这比C的弱类型安全所允许的各种其他事情更糟糕。

I guess maybe this warning makes sense given the choice not to warn when a non- void pointer type is converted to void * : 我想也许这个警告是有意义的,因为当非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

And yet, if that is intentional, then: 然而,如果这是有意的,那么:

  1. Why does the Microsoft documentation indicate that code producing this warning in C produces an error in C++? 为什么Microsoft文档表明在C中产生此警告的代码在C ++中产生错误?

  2. 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)? 除了忽视或抑制该警告,没有任何合理的替代方法,当一个人必须free一个非const指针非const指针const数据(如在我的现实世界的情况下)? 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. 如果在C ++中发生类似这样的事情,我可以将在变量参数列表中传递的字符串存储在某个高级STL容器中而不是数组中。 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. 对于无法访问C ++ STL且不使用高级集合的C程序,这种事情不是一个合理的选择。

  3. Some programmers work under a corporate/organizational policy of treating warnings as errors. 一些程序员在公司/组织政策下工作,将警告视为错误。 C4090 is enabled even with /W1 . 即使使用/W1也启用C4090 People must have encountered this before. 人们一定以前遇到过这种情况。 What do those programmers do? 那些程序员做了什么?

Apparently this is simply a bug in VC++. 显然这只是VC ++中的一个错误。

If you declare const char **x; 如果你声明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). 结果是一个指向chars的“只读”指针的指针,它本身不是一个“只读”指针(我使用术语“只读”,因为const -ness术语推出了错误的概念,即该字符是指向是常量而一般来说这是假的...带引用和指针的const是引用或指针的属性,并且不会告诉指向或引用数据的const )。

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. 任何读/写指针都可以转换为void *并且在编译该代码时VC ++没有真正的理由发出警告,无论是在C还是在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... VC ++实际上发出了大量有效C ++代码的警告......

Like 6502 says this seems to be a bug in the compiler. 像6502说这似乎是编译器中的一个错误。 However, you also ask what you should do about it. 但是,你也问你应该怎么做。

My answer is that you should add an explicit cast to the free call, and then a comment explaining why it is needed. 我的答案是你应该为免费电话添加一个明确的演员,然后是一个解释为什么需要它的评论。 Bugs in the compiler do happen, use the easiest workaround and add a note such that it can be tested if the bug has been resolved later. 编译器中的错误确实发生,使用最简单的解决方法并添加一个注释,以便在以后解决该错误时可以对其进行测试。

Extra points for also reporting the bug to the compiler vendor. 还向编译器供应商报告错误的额外点。

As for 1. It seems to be referring to implicitly casting a const T * to a void * , which should be a warning in C and an error in C++. 至于1.它似乎是指将一个const T *隐式地转换为void * ,它应该是C中的警告和C ++中的错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM