Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assignment <pointer to array of constants> = <pointer to array>: incompatible pointers

When I compile something like this

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

gcc warns me

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

Question: What's the problem with this assignment? Yes, technically, these are different types, but I don't see any danger here, double const (*)[ 3 ] looks even safer for me than double (*)[ 3 ].

I did some tests and results confuse me even more:

1) MSVC is quite happy with double const (* cpda)[ 3 ] = pda; assignment, no errors, no warnings.

2) Both gcc and MSVC are happy with this

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

while these are different types too.

3) In this example

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

gcc gives the same warning but MSVC gives error(!).

Who is right here? gcc or MSVC?


Test results.

Compilers:

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

2) MSVC (as C++ code) version 'VS2012CTP' 17.00.51025 for x86: http://rise4fun.com/vcpp

3) MSVC (as C code) VS2010: tested offline

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;
}

Edit:

I just compiled this on my Visual Studio as C code (not C++) and it gives no errors, no warnings at all. I edited commentaries to above code

like image 883
kotlomoy Avatar asked Jun 15 '13 10:06

kotlomoy


3 Answers

It's a difference in interpretation of the standard, gcc considers the types not compatible, while MSVC and clang do.

6.7.6.1 (2):

For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.

The types of pda and cpda are identically qualified [not qualified at all], so the question is whether they point to compatible types, i.e. are double[3] and const double[3] compatible types?

6.7.6.2 (6):

For two array types to be compatible, both shall have compatible element types, and if both size specifiers are present, and are integer constant expressions, then both size specifiers shall have the same constant value. If the two array types are used in a context which requires them to be compatible, it is undefined behavior if the two size specifiers evaluate to unequal values.

So the question is whether double and const double are compatible types.

6.7.3 (10):

For two qualified types to be compatible, both shall have the identically qualified version of a compatible type; the order of type qualifiers within a list of specifiers or qualifiers does not affect the specified type.

I would say that makes double and const double not compatible, so gcc is right.

The initialisation

double const * cpd = pd;

is okay because the constraints of assignment (which are relevant for initialisation) in 6.5.16.1 list

the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;

as one of the admissible situations. cpd and pd both point to qualified versions of double, and the left operand's target has all qualifiers that the right has (and one more, const).

However, the types double* and const double* are not compatible, hence

double const * * cppd = ppd;

is again invalid, and requires a diagnostic message.

like image 167
Daniel Fischer Avatar answered Nov 04 '22 11:11

Daniel Fischer


gcc is right here and the diagnostic is required in C.

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

Basically you are trying to assign an object of type T1 to an object of type T2 (constraints of simple assignment apply for initializations).

Where T1 is a pointer to an array N of T.

And T2 is a pointer to an array N of const T.

In the constraints of the simple assignment, C says that for pointers the following shall hold (in C99, 6.5.16.1p1):

both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right

This would allow for example something like:

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

But in your example, a pointer to an array N of const T is not a qualified version of a pointer to an array N of T. In C an array cannot be constant: there is not const arrays, but only arrays of const elements.

like image 34
ouah Avatar answered Nov 04 '22 11:11

ouah


This is a difference between C and C++. Doing that type of const conversion is perfectly fine in C++, but not in C.

like image 1
Vaughn Cato Avatar answered Nov 04 '22 10:11

Vaughn Cato