7

I learned from C Primer Plus that if you want to protect an array from being accidentally modified by a function, you should add const modifier before the pointer declaration in the header of function definition.

Following this sensible advice, in the following minimal example, I'm trying to pass a non-constant two-dimensional array array to the function Sum2D, one parameter of which is a pointer-to-const-int[2].

#include <stdio.h>
#define ROWS 2
#define COLS 2
int Sum2D(const int ar[][COLS], int rows); //use `const` to protect input array
int main(void)
{
    int array[ROWS][COLS]={{1,2},{3,4}}; //the non-constant array

    printf( "%d\n", Sum2D(array,ROWS) );

    return 0;
}

int Sum2D(const int ar[][COLS], int rows)
{
    int total=0;
    int i,j;
    for( i=0 ; i<rows ; i++ )
    {
        for( j=0 ; j<COLS ; j++ )
        {
            total+=ar[i][j];
        }
    }
    return total;
}

However, gcc cannot successfully compile this code without issuing the following warnings:

$gcc -ggdb3 -Wall -Wextra -o test test.c

test.c: In function ‘main’:
test.c:16:2: warning: passing argument 1 of ‘Sum2D’ from incompatible pointer type [enabled by default]
  printf( "%d\n", Sum2D(array,4) );
  ^
test.c:4:5: note: expected ‘const int (*)[4]’ but argument is of type ‘int (*)[4]’
 int Sum2D(const int ar[][COLS], int rows);
     ^

1) Why the warning?

2) How can I eliminate the "noise"?(Apart from adding const to array declaration.)

(If the array and function both use one-dimensional array, there is no warning.)

System information:

Ubuntu 14.04LTS

Compiler: gcc 4.8.2

Michał Ciuba
  • 7,876
  • 2
  • 33
  • 59
Naitree
  • 1,088
  • 1
  • 14
  • 23
  • Very straight forward. The function `Sum2D` is expecting to receive a const 2d array but you give it a non const one. This might be dangerous but not necessarily, that's why a warning and not error. – in need of help Jan 21 '15 at 07:55
  • @inneedofhelp Actually, the function expects a *pointer* to a size COLS array of `const int`. In a function parameter, `const int ar[][COLS]` is the same as `const int (*ar)[COLS]` – juanchopanza Jan 21 '15 at 07:56
  • Any reason for your parameter `rows` is there rather than just using the defined dimension? – Mario Jan 21 '15 at 07:56
  • Apparently gcc had a field day with their warnings. [4.9.2 shows no such warnings](http://coliru.stacked-crooked.com/a/a099d825ec238dfe) – WhozCraig Jan 21 '15 at 08:00
  • @inneedofhelp But this is actually an simplified example in the book _C Primer Plus_. It says that assigning a non-const pointer to a const pointer is okay. Because the `const` is for protection. Maybe it is not correct? – Naitree Jan 21 '15 at 08:01
  • @Mario No, not really. It's just for use with arrays with different row number. – Naitree Jan 21 '15 at 08:03
  • For the record, this compiles fine with clang, in strict c89, c99 and c11 modes. – juanchopanza Jan 21 '15 at 08:04
  • @WhozCraig Well that's somewhat relieving. Thanks for providing result from different version of gcc. – Naitree Jan 21 '15 at 08:06
  • @juanchopanza Thanks for providing results from clang. – Naitree Jan 21 '15 at 08:08
  • @juanchopanza and WhozCraig, Do you think this might be a "bug" in gcc 4.8.2? And this should actually be valid? (As it seems to be in gcc 4.9.2.) – Naitree Jan 21 '15 at 08:32
  • 1
    Compilers are allowed to compile non-conforming programs as an extension, so it wouldn't be a serious bug, however it could be annoying if somebody writes code this way and later ports the code to a compiler that doesn't have the extension – M.M Jan 21 '15 at 09:03

2 Answers2

13

This is an unfortunate "bug" in C's design; T (*p)[N] does not implicitly convert to T const (*p)[N]. You will have to either use an ugly cast, or have the function parameter not accept const.


At first sight it looks like this conversion should be legal. C11 6.3.2.3/2:

For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type;

However also look at C11 6.7.3/9 (was /8 in C99):

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

This last quote says that int const[4] is not considered to be a const-qualified version of int[4]. Actually it is a non-const-qualified array of 4 const ints. int[4] and int const[4] are arrays of different element types.

So 6.3.2.3/2 does not in fact permit int (*)[4] to be converted to int const (*)[4].


Another weird situation where this issue with const and arrays shows up is when typedefs are in use; for example:

typedef int X[5];
void func1( X const x );
void func1( int const x[5] );

This would cause a compiler error: X const x means that x is const, but it is pointing to an array of non-const ints; whereas int const x[5] means x is not const but it is pointing to an array of const ints!

Further reading here, thanks to @JensGustedt

M.M
  • 138,810
  • 21
  • 208
  • 365
  • 1
    Do you have a reference for this? I wonder if this means clang is "fixing" this as an extension. I tried compiling in strict mode (`-Wall -Wextra -Wconversion -pedantic-errors -std=cxx` where `xx` is 89, 99, 11.) – juanchopanza Jan 21 '15 at 08:08
  • By ugly cast, do you mean something like `(const int (*)[])`? I did "fix" it by adding this in function call. – Naitree Jan 21 '15 at 08:21
  • I think my knowledge is still too limited to fully understand what you mean. But when I am trying, I want to ask: Are `const int (*)[4]` and `int const (*)[4]` the same thing? As the former is what I saw in the warning message. – Naitree Jan 21 '15 at 09:25
  • 1
    @Naitree `const int (*)[4]` and `int const (*)[4]` are the same thing; a pointer to an array of four const `int`. *Both* differ from `int (*const)[4]`, a const pointer to an array of four non-const `int`. And lest we leave it out, `int const (*const)[4]`, a const pointer to an array of four const `int`. – WhozCraig Jan 21 '15 at 10:23
  • @Naitree And imho, the const-issue of a fixed-declaration of a non-const array of const-qualified elements, per C11 6.7.3/9, is nothing if not pointless. As the array id itself is not an lvalue, the lack of const-ness applied therein shouldn't even be a question. That the compiler pukes a warning on the conversion-to-const-qualified-pointer-to-first-element where other versions of gcc don't makes me go "wtf". – WhozCraig Jan 21 '15 at 10:38
  • @MattMcNabb Sorry for bothering you again. Would you mind checking if I understand your point correctly? As I understand, the problem is that `const int[4]` means the data type "array-of-4-constant-integer-numbers", rather than "constant-array-of-4-integer-numbers". A pointer to "array-of-4-integer-numbers"(`int[4]`) may be converted to "constant-array-of-4-integer-numbers", rather than to "array-of-4-constant-integer numbers"(`const int[4]`). – Naitree Jan 22 '15 at 02:26
  • add "a pointer to" before your last two quoted terms, and then it's ok – M.M Jan 22 '15 at 02:50
  • 1
    In the latest draft of c23 the text was changed to "If the specification of an array type includes any type qualifiers, **both** the array and the element type is so-qualified.". So this issue maybe finally solved – tstanisl Sep 19 '21 at 21:31
  • @tstanisl Great to hear although I suspect not everyone will upgrade to C23 quickly :) – M.M Sep 19 '21 at 22:41
0

You can type cast the array while calling the function. It will not automatically convert non-const into const. You can use this.

Sum2D( (const int (*)[])array, ROWS );
M.M
  • 138,810
  • 21
  • 208
  • 365
Karthikeyan.R.S
  • 3,991
  • 1
  • 19
  • 31
  • I actually still get warning using your typecast. I think what you mean is `(const int (*)[])`. Then the warning is gone. – Naitree Jan 21 '15 at 08:17