3

I’ve an integer array int foo[3] and I want to pass it to another function. I would like to accomplish two things:

  • Pass it by reference
  • Set it as constant as it should not be modified.

The function I’ve defined is:

void print_foo(const int(*const foo)[3]) {

    // Print the second integer
    printf("%d\n", (*foo)[1]);
}

And I call it as:

int foo[3] = {1, 2, 3};

print_foo(&foo);

When I compile it with MinGW’s gcc I get the warning:

warning: passing arg 1 of `print_foo` from incompatible pointer type

I would like to understand what am I doing wrong.

Note: I can hide the warning declaring the function without the first const as:

void print_foo(int(*const foo)[3])

But this seems like a workaround as I want not only the pointer address to be constant but also the contents of the memory (that’s the reason of the two const).

bolov
  • 72,283
  • 15
  • 145
  • 224
Octan
  • 348
  • 4
  • 19
  • I'm don't think it's related to your problem but in `“%d\n”` those are the wrong quotes. You need `"` instead. But maybe you are using the right quotes and it's the copy/pasting that changed those quotes (in which case you should copy/paste directly from the IDE to your browser without going through a word processor first). – Blaze Feb 22 '19 at 08:01
  • 1
    You are right, I’m asking with the iPad and there is a problem with the quotes. I’ll edit it. – Octan Feb 22 '19 at 08:04
  • Does https://stackoverflow.com/questions/13730786/c-pass-int-array-pointer-as-parameter-into-a-function perhaps help? – hat Feb 22 '19 at 08:14
  • If you want to pass by reference then why not pass by reference? `void print_foo(const int( & foo )[3])` – user7860670 Feb 22 '19 at 08:15
  • which version of gcc is that ? – Sander De Dycker Feb 22 '19 at 08:18
  • 1
    and : C++ or C ? – Sander De Dycker Feb 22 '19 at 08:54
  • I don’t known the version of the compiler. I think MinGW is a Windows wrapper for gcc. And It is C. – Octan Feb 22 '19 at 08:56
  • @Octan C doesn't have references – bolov Feb 22 '19 at 09:18
  • Possible duplicate of [Pointer to array with const qualifier in C & C++](https://stackoverflow.com/questions/34488559/pointer-to-array-with-const-qualifier-in-c-c) – Sander De Dycker Feb 22 '19 at 11:12
  • also : [Const correctness for array pointers?](https://stackoverflow.com/questions/36199473/const-correctness-for-array-pointers) – Sander De Dycker Feb 22 '19 at 11:14

2 Answers2

3

just do that :

#include <stdio.h>

// note can also use "const int foo[3]" but in that
// case 3 is nothing more than a comment for a reader
void print_foo(const int foo[])
{
  // Print the second integer
  printf("%d\n", foo[1]);
}

int main()
{
  int foo[3] = {1, 2, 3};

  print_foo(foo);
}

the array is already given by address

bruno
  • 32,421
  • 7
  • 25
  • 37
  • This will not check array size though because array will decay to normal pointer. `int foo[1]{1}; print_foo(foo);` – user7860670 Feb 22 '19 at 08:16
  • This means that including the `[3]` array in the argument definition already makes it a pointer? I thought that It shall be an empty braces: `const int foo[]`, without the 3. – Octan Feb 22 '19 at 08:19
  • @Octan using "[]" or "[3]" in hte signature of `print_foo` changes nothing for the compiler, is just a kind of comment saying the size is 3 – bruno Feb 22 '19 at 08:23
  • It is misleading to specify a size in `void print_foo(const int foo[3])` because it is completely ignored by the compiler. Either write `void print_foo(const int foo[])` or `void print_foo(const int *foo)` – chqrlie Feb 22 '19 at 08:24
  • 1
    If you want to specify 3 as a comment, write it in a comment. `[3]` is confusing for beginners. As a matter of fact, the function works for any array size greater than 1. – chqrlie Feb 22 '19 at 08:26
  • @chqrlie in a way you are saying the compiler is confusing allowing that ^^ But in fact I agree with you, I just come from the user code ;-) – bruno Feb 22 '19 at 08:28
  • @chqrlie I am new to c and i have a doubt from the comment you mentioned above why will compiler ignore the 3 in array ? trying to learn. – heeat Feb 22 '19 at 08:30
  • @heeat replace `[3]` by `[1]` in the signature and you will see you will continue to access to `foo[1]` without warning ;-) – bruno Feb 22 '19 at 08:31
  • Ok so you are saying that in the compiler side there is no difference between `const int foo[]` and something like `const int (*const foo)[3]`. I just wanted to state that **only** size 3 arrays are accepted as an input. – Octan Feb 22 '19 at 08:32
  • Indeed the C language allows for many confusing constructs and compilers should issue many more warnings by default, most of which should be errors. This would save so many hours wasted in debugging stupid mistakes. `gcc -Wall -Wextra -Werror` is first step. – chqrlie Feb 22 '19 at 08:33
  • @chqrlie because of the protests on the [3] even accompanied by the commentary I replaced it with [] ;-) – bruno Feb 22 '19 at 08:36
  • @Octan: OK, but you pass the address of an array of 3 `int` that is not `const`, hence the type mismatch. It is not idiomatic in C to specify function signatures in such a restrictive way. – chqrlie Feb 22 '19 at 08:36
  • Thanks. I need MISRA-C compliance and I wanted to be really specific on the input type: constant pointer, with constant contents and array size of 3. However I’ve tried the tree options and the variable is always modifiable even the `const`. – Octan Feb 22 '19 at 08:59
  • @Octan the array is already given by address, you do not need to add a level of pointer, just using `const int foo[]` you have what finally you want, you cannot modify the array – bruno Feb 22 '19 at 09:10
2

Interestingly enough, I could compile the following code with CLang versions 3.4.1 and 6.0.0 with no warnings:

#include <stdio.h>

void print_foo(const int (* const foo)[3]) {
    printf("%d - size: %lu\n", (*foo)[1], sizeof(*foo));
}
int main() {
    int foo[3] = {1,2,3};
    print_foo(&foo);
    return 0;
}

Output is 2 - 12 on my 32 bits system, which proves that sizeof(*foo) has the expected value.

But I would say that gcc is correct here, and that Clang allows it as an extension.

The standard (draft n1570 for C11) says at 6.2.7 Compatible type and composite type §1:

Two types have compatible type if their types are the same. Additional rules for determining whether two types are compatible are described in 6.7.2 for type specifiers, in 6.7.3 for type qualifiers, and in 6.7.6 for declarators and in 6.7.3 Type qualifiers § 10

For two qualified types to be compatible, both shall have the identically qualified version of a compatible type

So types need to have the same constness to be compatible.

But passing parameters in a function call has the same semantics as an assignment, and 6.5.16.1 Simple assignment says in its constraints:

One of the following shall hold:
...
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

So ok, it is allowed to assign an int (*)[3] to an int (const *)[3] even if they are not compatibles.

But int [3] and const int[3] are different types, so you cannot assign an int (*)[3] to a const int (*)[3] nor to a const int (const *)[3].

What you want makes sense, but I cannot imagine a conformant way to declare it.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • gcc also has it as an [extension](https://gcc.gnu.org/onlinedocs/gcc/Pointers-to-Arrays.html), but I suspect the OP doesn't use a gcc version that has this (or is using `-pedantic-errors`). – Sander De Dycker Feb 22 '19 at 11:13
  • Yes, the warning is not shown when I compile with Visual Studio but it appears compiling with MinGW (which is the official compiler) – Octan Feb 22 '19 at 11:15
  • It’s curious, though, that the compiler doesn’t warn me when I cast structs to `const` in functions like `void func(const StructName * const foo)`. I assume the main difference here is that in this case there is no array involved. – Octan Feb 22 '19 at 11:30
  • Very concise explanation. You might want to quote C17 instead of C11. – chqrlie Feb 23 '19 at 08:55