views:

388

answers:

2

Referring to this question and especially the accepted answer of litb, I wonder why the gcc complain about this:

void func(const int (*ip)[3]) {
    printf("Value: %d\n", ip[1][1]);
}

int main() {
    int i[3][3] = { {0, 1, 2} , {3, 4, 5}, {6, 7, 8} };
    func(i);
    return 0;
}

If I eliminate the const the compiler keeps still. Did I something misunderstand? I wanted to be sure that func don't modify my array.

EDIT: The same thing happens if I define a data type for my matrix:

typedef int Array[3][3];

void func(const Array *p) {
    printf("Value: %d\n", (*p)[1][1]);
}

int main() {
    Array a = { {0, 1, 2}, {3, 4, 5}, {6, 7, 8} };
    func(&a);
    return 0;
}

I accept, this kind of code isn't very C style, more like C++. In C++ indeed there would be no problem if I define Array as a class containing all the matrix behavior.

class Array {...};

I suppose, I didn't understand very well the concept of arrays and arrays of arrays in C and passing them to functions. Any enlightenment?

Thank you in advance.

EDIT2: Meanwhile I chewed a bit on this problem and it seems to converge to the following question: C/C++ implicitly converts a pointer to an int to a pointer to an const int. Thus the following works:

func(const int a[]) // aquivalent: func(const int *a)
{ ... }

int main() 
{
    int b[10];
    func(b);
    return 0;
}

But C/C++ don't implicitly converts a pointer to an array of n ints to a pointer to an array of n const ints. Even though an array of n ints is implicitly converted to an array of n const ints. This level of indirection in the implicit conversion isn't supported. The following will be rejected (at least with a warning in C):

func(const int a[][n]) // aquivalent: func(const int (*a)[n])
{ ... }

int main()
{
    int b[m][n];
    func(b);
    return  0;
}

It's similar to the problem that C++ doesn't implicitly convert a template for the type A into a template of type B even if A can be implicitly converted to B. The two templates are of completely different types.

Is this the right answer?

+1  A: 

You don't need to eliminate const, just pass a compatible value by casting the argument in the call to func:

   func( (void *)i );

If possible, it would be preferrable to declare i as const, but this hack should work.

William Pursell
Casting to `(void *)` is ugly... why not just cast to const, like this: `func((const int (*)[3])i);`
Martin B
Ugliness is in the eye of the beholder. I agree that the explicit cast is safer, but think the void * cast is cleaner to read. Either way, declaring i to be const is better.
William Pursell
Moreover to a solution, I'm interested in the "Why." Why isn't the compiler able to see that an array of ints can simply converted to an array of const ints. Please read my updates in the original question too.
phlipsy
+1  A: 

Your i variable is an array with 3 elements.

When you pass it to a function, inside the function, it becomes a pointer to the first element. The compiler can add const either to the pointer or to the thing pointed to: an array of 3 ints. It cannot however change the thing pointed to from an array of 3 ints to an array of 3 constants.

I think you need to do the cast yourself.

#include <stdio.h>

typedef const int array_of_3_constants[3];

void func(int (* const i)[3]) {
  ++i[0][0];
  printf("Value: %d\n", i[1][1]);
}

void gunc(array_of_3_constants *i) {
  ++i[0][0];                              /* error */
  printf("Value: %d\n", i[1][1]);
}

int main(void) {
  int i[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
  func(i);
  func((array_of_3_constants*)i);         /* warning */
  gunc(i);                                /* warning */
  gunc((array_of_3_constants*)i);
  return 0;
}
pmg
Why can't the compiler change the array of 3 ints to an array of 3 constants? It doesn't hurt, does it?
phlipsy
Read http://web.torek.net/torek/c/expr.html , particularly the parts mentioning "The Rule"
pmg
I edited my question. Did I understand the consequences of the cited article correct?
phlipsy