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

时间:2012-05-01 20:14:22

标签: c++ c visual-studio visual-c++ const

摘要

当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

然而,如果那是故意的话,那么:

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

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

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

2 个答案:

答案 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 ++中的错误。