为什么的Visual C的隐式转换++警告从常量无效**到void *在C,但不是在C ++? [英] Why does Visual C++ warn on implicit cast from const void ** to void * in C, but not in C++?

查看:447
本文介绍了为什么的Visual C的隐式转换++警告从常量无效**到void *在C,但不是在C ++?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

摘要

在Microsoft Visual Studio中的C / C ++编译器在警告 C4090 当C程序试图的指针指针转换常量数据(如常量无效** 为const char ** )以无效* (尽管这种类型实际上不是一个指向常量)。更奇怪的是,相同的编译器默默接受相同的code编译为C ++。

什么是这种不一致的原因,为什么Visual Studio的(不像其他编译器)与隐式转换指针问题的指针的常量无效*

详细信息

我在这一个变量参数列表传递C字符串读入一个数组(由被调用的va_arg 循环)的C程序。由于C-字符串类型为const char * ,即跟踪他们的数组类型的为const char ** 。这与常量内容指向字符串数组本身是动态分配的(与释放calloc )和I 免费在函数返回(在C字符串后已被处理)之前。

当我编译这个code与 cl.exe时(在Microsoft Visual C ++),即使是很低的警戒线,在免费调用触发警告 C4090 。由于免费需要无效* ,这告诉我,编译器不喜欢,我已经转换成一个为const char ** 无效* 。我创建了一个简单的例子来证实这一点,我在其中尝试将常量无效** 转换为无效*

  / * cast.c  - 可一个const无效**隐式转换为void *? * /INT主要(无效)
{
    常量无效** P = 0;
    无效* Q;
    Q =磷;    返回0;
}

然后我如下,证实这是什么触发警告编译它:

 > CL cast.c
微软(R)32位C / C ++优化编译器版本为16.00.40219.01的80x86
版权所有(C)微软公司。版权所有。cast.c
cast.c(7):警告C4090:'=':不同的常量预选赛
微软(R)增量链接器版本10.00.40219.01
版权所有(C)微软公司。版权所有。/out:cast.exe
cast.obj

上警告C4090微软文档说:


  

此发出警告C程序。在C ++程序,编译器会发出错误:C2440


这是有道理的,因为C ++是一种比C强类型语言,并允许使用C潜在危险的隐式转换用C是不允许++。微软的文档使它看起来像警告 C2440 在C触发同一code,或code的一个子集,这将引发错误 C2440在C ++中的。

我这样想着,直到我试图编译我的测试程序作为C ++(在 / TP 标志执行此操作):

 > CL / TP cast.c
微软(R)32位C / C ++优化编译器版本为16.00.40219.01的80x86
版权所有(C)微软公司。版权所有。cast.c
微软(R)增量链接器版本10.00.40219.01
版权所有(C)微软公司。版权所有。/out:cast.exe
cast.obj

在同一code编译为C ++,没有错误或警告。可以肯定,我重建,告诉编译器为积极警告尽可能:

 > CL / TP /华尔街cast.c
微软(R)32位C / C ++优化编译器版本为16.00.40219.01的80x86
版权所有(C)微软公司。版权所有。cast.c
微软(R)增量链接器版本10.00.40219.01
版权所有(C)微软公司。版权所有。/out:cast.exe
cast.obj

它成功默默的。

那些构建均与微软的Visual C ++的Windows XP计算机于2010年防爆preSS版的 cl.exe时 Windows 7的机器上,但同样的错误出现,在这两个的Visual Studio .NET 2003的 cl.exe时和Visual C ++ 2005例preSS版的 cl.exe时 。因此,似乎这发生在所有版本(虽然我没有在一切可能的版本测试),而不是与Visual Studio是建立在我的机器的方式有问题。

同样的code编译没有一个Ubuntu 11.10系统(版本字符串海湾合作委员会(Ubuntu的/ Linaro的4.6.1-9ubuntu3)4.6.1 ),设置为警告尽最大可能地,为C89,C99和C ++:

  $ GCC -ansi -pedantic -Wall -Wextra -o投cast.c
cast.c:在函数'主':
cast.c:6:11:警告:变量'Q'设置,但不使用[-Wunused,但是设置变量]$ GCC -std = C99 -pedantic -Wall -Wextra -o投cast.c
cast.c:在函数'主':
cast.c:6:11:警告:变量'Q'设置,但不使用[-Wunused,但是设置变量]$ G ++ -x C ++ -ansi -pedantic -Wall -Wextra -o投cast.c
cast.c:在函数'廉政的main():
cast.c:6:11:警告:变量'Q'设置,但不使用[-Wunused,但是设置变量]

它警告说,永远不会被分配后读取,但这一警告是有道理的,是不相关的。

除了没有引发海湾合作委员会的警告启用所有警告,并在任何海合会或MSVC不会触发警报在C ++中,在我看来,从指针转换为指针为const到无效* 不应该被认为在所有问题,因为在无效* 是一个指向非 - 常量,指针的指针常量也是一个指向非 - 常量

在我的现实世界的code(不是例子),我可以用的#pragma 指令,或有明确的转换这沉默,或作为编译C ++(嘿嘿),或者我可以忽略它。但我宁愿没有做任何的那些事情,至少没有之前我明白为什么会这样。 (为什么不使用C ++发生!)

一个可能,部分原因发生对我说:不像C ++,C允许从隐式转换的void * 来的指针到的数据类型。所以我可以有一个指针隐式地从为const char ** 转换为无效* ,然后隐式地从<$转换C $ C>无效* 的char ** ,从而可以修改它指向的指针不断的数据,没有一个演员。这将是坏的。但我看不出这是任何比这都有C的弱类型安全允许的其他各种事情变得更糟。

我想也许这样的警告使得有选择不警告当非感 - 无效指针类型转换为无效*

  / * cast.c  - 可一个const无效**隐式转换为void *? * /INT主要(无效)
{
    常量无效** P = 0;
    无效* Q;
    Q =磷;    返回0;
}

 &GT; CL /华尔街voidcast.c
微软(R)32位C / C ++优化编译器版本为16.00.40219.01的80x86
版权所有(C)微软公司。版权所有。voidcast.c
微软(R)增量链接器版本10.00.40219.01
版权所有(C)微软公司。版权所有。/out:voidcast.exe
voidcast.obj

然而,如果这是故意的,则:


  1. 为什么微软的文档显示,code生产这种警告用C C ++中产生错误?


  2. 此外忽视或燮pressing警告,没有任何合理的替代方法,当一个人必须免费非 - 常量指针非 - 常量指针常量数据(如在我的现实世界的情况)?如果这样的事情在C ++中发生的事情,我可以存储在变量参数列表中的一些高层次的STL容器,而不是一个数组传递的字符串。对于没有进入C ++ STL和不以其他方式使用高层次的集合C程序,诸如此类的事情是不是一个合理的选择。


  3. 一些程序员在警告作为错误的企业/组织的政策工作。 C4090 / W1 启用连。人们一定遇到过这一点。什么是那些程序员呢?



解决方案

显然,这简直是在VC ++中的错误。

如果您声明为const char ** X; 的结果是指向了只读指针字符,而且它本身不是一个只读指针(我使用术语只读,因为常量 -ness长期推动了被指向的字符错误的概念,是恒定的,而这是一般假.. 常量与引用和指针是参考或指针的财产,不能说明所指向的或引用的数据的数量不同)。

任何读/写指针可被转换为无效* 和VC ++有没有真正的理由在编译code时发出警告,无论是在<$ ç$ C> C 也不 C ++ 模式。

请注意,这不是一个正式的问题,因为标准并不强制其警告应该或不应该发行,因此编译器可以自由地发出警告,完全有效的code仍保持兼容。 VC ++居然发出这些警告的有效的C过多++ code ...

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). Even more strangely, the same compiler silently accepts identical code compiled as 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 *?

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). 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).

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's documentation on warning C4090 says:

This warning is issued for C programs. In a C++ program, the compiler issues an error: 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++.

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

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

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. 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.

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]

It does warn that q is never read from after being assigned, but that warning makes sense and is unrelated.

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.

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++!)

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.

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:

  1. Why does the Microsoft documentation indicate that code producing this warning in C produces an error in 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)? 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.

  3. 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?

解决方案

Apparently this is simply a bug in VC++.

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).

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.

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...

这篇关于为什么的Visual C的隐式转换++警告从常量无效**到void *在C,但不是在C ++?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆