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

Eli*_*gan 18 c c++ const visual-studio visual-c++

摘要

当C程序尝试将指针转换为指向数据(如或)的指针时,Microsoft Visual Studio中的C/C++编译器会发出警告C4090(即使这样的类型实际上不是指针).更奇怪的是,同一个编译器默默接受编译为C++的相同代码.constconst void **const char **void *const

什么是这种不一致的原因,以及为什么Visual Studio的(不像其他编译器)有隐含转换的指针指向一个问题constvoid *

细节

我有一个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)

微软关于C4090警告文档说:

此警告是针对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)

然而,如果这是有意的,那么:

  1. 为什么Microsoft文档表明在C中产生此警告的代码在C++中产生错误?

  2. 除了忽略或抑制警告之外,还有一个合理的替代方案,当一个人必须freeconst指针const指向const数据的非指针时(如在我的实际情况中)?如果在C++中发生类似这样的事情,我可以将在变量参数列表中传递的字符串存储在某个高级STL容器中而不是数组中.对于没有访问C++ STL且不使用高级集合的C程序,这种事情不是一个合理的选择.

  3. 一些程序员在公司/组织政策下工作,将警告视为错误.C4090即使启用也启用/W1.人们一定以前遇到过这种情况.那些程序员做了什么?

650*_*502 11

显然这只是VC++中的一个错误.

如果你声明const char **x;结果是一个指向chars的"只读"指针的指针,它本身不是一个"只读"指针(我使用术语"只读",因为const-ness term推错了概念,即被指向的字符是常量,而一般来说这是假的... const引用和指针是引用或指针的属性,并且不会告诉指向或引用数据的常量).

任何读/写指针都可以转换为a void *,VC++没有任何理由在编译代码时发出警告,无论C是在C++模式还是在模式下.

请注意,这不是正式问题,因为标准没有强制要求或不应该发出哪些警告,因此编译器可以自由地发出警告,以保证完全有效的代码仍然符合要求.VC++实际上发出了大量有效C++代码的警告......