28

Consider following program:

int main()
{
    int array[9];
    const int (*p2)[9] = &array;
}

It compiles fine in C++ (See live demo here) but fails in compilation in C. By default GCC gives following warnings. (See live demo here).

prog.c: In function 'main':
prog.c:4:26: warning: initialization from incompatible pointer type [enabled by default]
     const int (*p2)[9] = &array;

But If I use -pedantic-errors option:

gcc -Os -s -Wall -std=c11 -pedantic-errors -o constptr constptr.c

it gives me following compiler error

constptr.c:4:26: error: pointers to arrays with different qualifiers are incompatible in ISO C [-Wpedantic]

Why it fails in compilation in C but not in C++? What C & C++ standard says about this?

If I use const qualifier in array declaration statement it compiles fine in C also. So, what is happening here in above program?

Destructor
  • 14,123
  • 11
  • 61
  • 126
  • 10
    While I understand this is a good question, but simply asking `What C & C++ standard says about this?` is not good. Did you try to look into the standards? Which part you did not understand? Did you find any differences? Where is your research effort? Hope I'm understood. :) – Sourav Ghosh Dec 28 '15 at 06:21
  • For C see https://stackoverflow.com/questions/50164803/ and https://stackoverflow.com/questions/28062095/ – M.M Nov 21 '19 at 02:01

1 Answers1

32

GCC-gnu

In GNU C, pointers to arrays with qualifiers work similar to pointers to other qualified types. For example, a value of type int (*)[5] can be used to initialize a variable of type const int (*)[5]. These types are incompatible in ISO C because the const qualifier is formally attached to the element type of the array and not the array itself.

C standard says that (section: §6.7.3/9):

If the specification of an array type includes any type qualifiers, the element type is so- qualified, not the array type.[...]

Now look at the C++ standard (section § 3.9.3/5):

[...] Cv-qualifiers applied to an array type attach to the underlying element type, so the notation “cv T,” where T is an array type, refers to an array whose elements are so-qualified. An array type whose elements are cv-qualified is also considered to have the same cv-qualifications as its elements. [ Example:

 typedef char CA[5];
 typedef const char CC;
 CC arr1[5] = { 0 };
 const CA arr2 = { 0 };

The type of both arr1 and arr2 is “array of 5 const char,” and the array type is considered to be const- qualified. —endexample]

Therefore, the initialization

const int (*p2)[9] = &array;  

is assignment of type pointer to array[9] of int to pointer to array[9] of const int. This is not similar to assigning int * to a const int * where const is applied directly to the object type the pointer points to. This is not the case with const int(*)[9] where, in C, const is applied to the elements of the array object instead of the object the pointer points to. This makes the above initialization incompatible.

This rule is changed in C++. As const is applied to array object itself, the assignment is between same types pointer to const array[9] of int instead of type pointer to array[9] of int and pointer to array[9] of const int.

haccks
  • 104,019
  • 25
  • 176
  • 264
  • Good link. But the answer still doesn't say about what the C++ standard says about this? – Destructor Dec 28 '15 at 06:49
  • 5
    I'd call this a bug in ISO C. It makes no sense. You should always be able to add `const` to a pointer target, at any level. – Tom Karzes Dec 28 '15 at 07:20
  • 1
    @PravasiMeet; Added quote from C++ standard. – haccks Dec 28 '15 at 07:37
  • 7
    @TomKarzes; *You should always be able to add `const` to a pointer target, at any level*: [No. Not in C](http://c-faq.com/ansi/constmismatch.html). – haccks Dec 28 '15 at 08:54
  • `No. Not in C` yes, that's the point: it looks like a bug in C standard, as Tom said. – Ruslan Dec 28 '15 at 09:01
  • 4
    @TomKarzes no, you shouldn't, e.g. the well-known incompatibility between `int **` and `const int **` (which are also incompatible in C++). If that implicit conversion existed , it'd be possible to write to a const object without using a cast. – M.M Dec 28 '15 at 09:16
  • I don't see what the array type being const-qualified really has to do with the question. We are trying to make a pointer-to-array-of-const-int point to an array-of-int, which may or may not be illegal because of the qualifier mismatch of the component type (int); the array type itself is not in question. If anything, making that a pointer-to-const-array-of-const-int only makes it more different from the array-of-int type we are trying to make it point to. – Marc van Leeuwen Dec 28 '15 at 09:19
  • @M.M I don't see why an `int **` can't be assigned to a `const int **`. I agree that it can't, but I don't see the harm. They can be accessed the same way, but the `const` version blocks modifying it through double indirection. Where's the harm in the user imposing that restriction? Similarly for assigning `int *` to `const int *`. You're just saying "I want the target of this pointer to be read-only when accessed through this pointer". Again, where's the harm? – Tom Karzes Dec 28 '15 at 11:08
  • 4
    @TomKarzes the harm is that you could write code that modifies a const object (causing undefined behaviour) and not get any compiler warnings or errors. [See here for full description](http://c-faq.com/ansi/constmismatch.html) – M.M Dec 28 '15 at 11:14
  • @M.M Thanks for the info. I do agree that there are problem cases when an object's address is assigned to a pointer that contains stronger `const` qualification. The alternative to the language allowing these cases is to either not use `const`, or to use explicit type casts. In either case, the mechanism is being bypassed and is not serving its purpose. I personally think the compiler should allow these cases, with a big caveat emptor to the user. After all, C was very usable long before `const` even existed. – Tom Karzes Dec 28 '15 at 12:11
  • 5
    @TomKarzes: Cars were very usable long before airbags even existed. Shall we remove them? – Lightness Races in Orbit Dec 28 '15 at 13:53
  • Shouldn't it be in the last paragraph: " between same types pointer to const array[9] of **const** int" because the const qualifier is applied to the array type as well as to the element type in C++ ? – Martin Zabel Dec 28 '15 at 16:09
  • @MartinZabel; It doesn't matter. The fact is that you can't declare an array of `const` type. Arrays are non-modifiable lvalues and `const` by default. – haccks Dec 28 '15 at 16:16
  • @Lightness A very poor analogy. When is the last time you had to jump through hoops to make your airbag save your life in spite of itself? I don't think you understand the point here. – Tom Karzes Dec 28 '15 at 19:54
  • @TomKarzes one time I couldn't install a racing steering wheel in my car because the stock steering wheel had an airbag in it and it's illegal in my country to remove any airbags that the car came with. Although as Marc says we're getting a bit far afield now. – M.M Dec 28 '15 at 21:07
  • @Lightness Ok, now that was just plain insulting. Avoiding writing `const **` means not using `const` in this context (which you have argued against). The hoop jumping referred to using a type cast to achieve OP's goal. There's a big difference. – Tom Karzes Dec 29 '15 at 01:44