jml-happy
jml-happy

Reputation: 57

Address of typedef'd constant-sized array in function argument causes incompatible pointer types when array is another function's formal parameter

I've read this thread and searched around for more information but most people just say "don't use typedefs like this." This way of doing things is appealing to me and I am also trying to learn new things, so I want to stick with this.

I'm using gcc version 9.2.1

Here is a minimum reproducible example of what I'm doing:

#define TABLE_SZ 10

typedef int vec_t[3];

typedef struct {
    vec_t table_one[TABLE_SZ];
} tables;

void foo(vec_t * foo_vec) {
  (*foo_vec)[0] = 0;
}

void bar (tables bar_table, vec_t bar_vec) {
  foo(&(bar_table.table_one[0]));
  foo(&bar_vec);
}


int main() {
  vec_t vector;
  foo(&vector);
}
/home/happy/CLionProjects/os_2019_p5/tests/test_array_typedef_ops.c: In function ‘bar’:
/home/happy/CLionProjects/os_2019_p5/tests/test_array_typedef_ops.c:18:7: warning: passing argument 1 of ‘foo’ from incompatible pointer type [-Wincompatible-pointer-types]
   18 |   foo(&bar_vec);
      |       ^~~~~~~~
      |       |
      |       int **
/home/happy/CLionProjects/os_2019_p5/tests/test_array_typedef_ops.c:12:18: note: expected ‘int (*)[3]’ but argument is of type ‘int **’
   12 | void foo(vec_t * foo_vec) {
      |          ~~~~~~~~^~~~~~~

I assume this has something to do with "pointer decay", I guess bar converting bar_vec to int **? Specifically, why is the type of &(bar_table.table_one[0]) int (*)[3] when bar_vec is int ** ?

I obviously don't want to supress all incompatible pointer type warnings in gcc.
Casting the pointer seems like a kludge. I want to keep the typedef. I don't want to hide it in a struct.

Is there a compiler flag or other solution to tell the compiler to treat this situation as I expect it to? Is there more information on this I should be aware of?

Thank you.

p.s. does stack overflow have pretty printing for compiler output?

Upvotes: 0

Views: 50

Answers (1)

Lundin
Lundin

Reputation: 213960

void bar (tables bar_table, vec_t bar_vec) is bad in many ways:

  • You pass the struct by value which very ineffective and can't get optimized by the compiler if the function is in a different translation unit.
  • The typedef vec_t doesn't prevent your array type from getting adjusted ("decay") upon getting passed as parameter, so vec_t bar_vec is equivalent to int*. And this array is therefore not getting passed by value, just its address.

The reason for the compiler error is however this: foo(&bar_vec);. You pass a pointer to the int* you have hidden beneath the typedef, meaning that you pass an int**. The function however, expects a vec_t *, meaning an array pointer of type int(*)[3]. This is what the compiler is telling you.

There is no compiler flag to solve this, because the code doesn't make any sense. Solve this by getting rid of the typedef, then rewrite the code.

Upvotes: 1

Related Questions