【问题标题】:Assignment <pointer to array of constants> = <pointer to array>: incompatible pointers赋值 <指向常量数组的指针> = <指向数组的指针>:不兼容的指针
【发布时间】:2013-06-15 10:40:54
【问题描述】:

当我编译这样的东西时

double da[ 3 ] = { 2., 3., 4. };
double (* pda)[ 3 ] = &da;
double const (* cpda)[ 3 ] = pda; // gcc: warning; MSVC: ok

gcc 警告我

warning: initialization from incompatible pointer type [enabled by default]

问题:这个作业有什么问题?是的,从技术上讲,它们是不同的类型,但我认为这里没有任何危险,double const (*)[ 3 ] 对我来说看起来比 double (*)[ 3 ] 更安全。

我做了一些测试,结果让我更加困惑:

1) MSVC 对double const (* cpda)[ 3 ] = pda; 分配非常满意,没有错误,没有警告。

2) gcc 和 MSVC 都对此感到满意

double d = 1.;
double * pd = &d;
double const * cpd = pd;  // gcc: ok; MSVC: ok

虽然这些也是不同的类型。

3) 在这个例子中

double d = 1.;
double * pd = &d;
double * * ppd = &pd;
double const * * cppd = ppd;  // gcc: warning; MSVC: error

gcc 给出相同的警告,但 MSVC 给出错误(!)。

谁在这里? gcc 还是 MSVC?


测试结果。

编译器:

1) gcc 4.7.2 版:http://www.compileonline.com/compile_c_online.php

2) MSVC(作为 C++ 代码)版本 'VS2012CTP' 17.00.51025 for x86:http://rise4fun.com/vcpp

3) MSVC (as C code) VS2010: 离线测试

int main()
{
    double d = 1.;

    double * pd = &d;
    double const * cpd = pd;
    // gcc: ok
    // MSVC C++: ok
    // MSVC C: ok

    double * * ppd = &pd;
    double const * * cppd = ppd;
    // gcc: warning: initialization from incompatible pointer type [enabled by default]
    // MSVC C++: error C2440: 'initializing' : cannot convert from 'double **' to 'const double **'
    // MSVC C: ok

    double da[ 3 ] = { 2., 3., 4. };

    double (* pda)[ 3 ] = &da;
    double const (* cpda)[ 3 ] = pda;
    // gcc: warning: initialization from incompatible pointer type [enabled by default]
    // MSVC C++: ok
    // MSVC C: ok

    cpd, cpda;
    return 0;
}

编辑:

我刚刚在我的 Visual Studio 上将它编译为 C 代码(不是 C++),它没有给出任何错误,也没有任何警告。我编辑了上述代码的注释

【问题讨论】:

标签: c pointers constants compiler-warnings gcc-warning


【解决方案1】:

这是对标准的解释不同,gcc认为类型不兼容,而MSVC和clang则认为。

6.7.6.1(2):

为了使两种指针类型兼容,两者都应具有相同的限定,并且都应 是指向兼容类型的指针。

pdacpda 的类型是相同限定的[根本没有限定],所以问题是它们是否指向兼容类型,即 double[3]const double[3] 兼容类型?

6.7.6.2(6):

为了使两个数组类型兼容,两者都应具有兼容的元素类型,并且如果 两个大小说明符都存在,并且是整数常量表达式,那么两个大小 说明符应具有相同的常数值。如果在上下文中使用这两种数组类型 这要求它们兼容,如果两个大小说明符是未定义的行为 评估为不相等的值。

所以问题是doubleconst double 是否是兼容的类型。

6.7.3 (10):

为了使两个合格的类型兼容,两者都应具有相同的合格版本 兼容类型;说明符或限定符列表中类型限定符的顺序 不影响指定类型。

我会说这使得doubleconst double 不兼容,所以 gcc 是正确的。

初始化

double const * cpd = pd;

没关系,因为 6.5.16.1 列表中的赋值约束(与初始化相关)

左操作数具有原子的、合格的或不合格的指针类型,并且(考虑到 左操作数在左值转换后的类型)两个操作数都是 指向兼容类型的合格或非合格版本的指针,以及指向的类型 to by left 具有所有由 right 指向的类型的限定符;

作为可接受的情况之一。 cpdpd 都指向 double 的限定版本,并且左侧操作数的目标具有右侧具有的所有限定符(还有一个,const)。

但是,double*const double* 类型不兼容,因此

double const * * cppd = ppd;

再次无效,需要诊断消息。

【讨论】:

  • 它没有解释这个double const * cpd = pd; // gcc: ok。根据您的研究,double const *double * 不兼容,因为它们指向不兼容的类型。但 gcc 不这么认为。
  • 不,虽然指针类型不兼容,但 6.5.16.1 明确允许将 double* 分配给 const double*,请参阅补充。
【解决方案2】:

gcc 就在此处,需要在 C 中进行诊断。

double da[ 3 ] = { 2., 3., 4. };
double (* pda)[ 3 ] = &da;
double const (* cpda)[ 3 ] = pda;  // diagnostic here

基本上,您正在尝试将 T1 类型的对象分配给 T2 类型的对象(简单分配的约束适用于初始化)。

其中T1 是指向数组NT 的指针。

T2 是一个指向数组Nconst T 的指针。

在简单赋值的约束中,C 表示对于指针,以下内容应成立(在 C99 中,6.5.16.1p1):

两个操作数都是指向兼容类型的合格或不合格版本的指针,左边指向的类型具有右边指向的类型的所有限定符

这将允许例如:

int a = 0;
const int *p = &a;  // p type is a qualified version of &a type

但在您的示例中,指向const T 的数组N 的指针不是指向T 的数组N 的指针的限定版本。在 C 中,数组不能是常量:没有 const 数组,而只有 const 元素的数组。

【讨论】:

  • 有错字:“其中 T1 是指向 const T 的数组 N 的指针。而 T2 是指向 T 的数组 N 的指针。” - 必须完全相反。
【解决方案3】:

这是 C 和 C++ 之间的区别。在 C++ 中进行这种类型的 const 转换非常好,但在 C 中则不然。

【讨论】:

  • 是的,Microsoft C++ 在这里确实是不好的比较。我试图在 VS2010 中编译为 C 代码(见编辑)。但这也是不好的比较,因为微软不支持 C99。
  • 不仅仅是 C 和 C++ 之间的区别。编译 C 代码时的 clang 就像这里的 MSVC。
猜你喜欢
  • 2018-10-14
  • 1970-01-01
  • 2011-12-16
  • 1970-01-01
  • 2020-10-11
  • 2021-02-07
  • 1970-01-01
  • 1970-01-01
  • 2016-07-30
相关资源
最近更新 更多