0

As is well known, arrays decay to pointers when passed as parameters in C. But an array of arrays only decays to a pointer of arrays; hence if we enclose our original array itself in an array, we can retrieve the original array length after passing the enclosing array into a function. Here's a program that tests this idea:

/* array_workaround.c */

#include <stdio.h>

void printsize(char array_pointer[][10]);

int main(void)
{
    char a[10];

    for(char i = 0; i < 10; i++)
        a[i] = i;

    char a_p[1][10] = {a};

    printsize(a_p);

    return 0;
}

void printsize(char array_pointer[][10])
{
    printf("%lu\n", sizeof(array_pointer[0]));
}

When run, it prints out the correct value (10), but the compiler gives this warning:

array_workaround.c:12:24: warning: incompatible pointer to integer conversion
      initializing 'char' with an expression of type 'char [10]' [-Wint-conversion]
    char a_p[1][10] = {a};

Of course, the issue with this "workaround" is that it requires that the size of the array be hard-coded into the function receiving it, which sort of defeats the whole purpose. However, it does mean that the array is checked to be of the right size, without having to pass in the length as a separate parameter... (as long as it's always 10! lol).

So what is going on with the warning? Is the compiler simply not "smart enough" to know that what is going on here doesn't corrupt any data? Or am I just getting lucky somehow? Can anyone see a way to make it an actual workaround so that you don't have to hard-code the number 10 in there?

Kara
  • 6,115
  • 16
  • 50
  • 57
Chris Middleton
  • 5,654
  • 5
  • 31
  • 68

3 Answers3

1

The line

char a_p[1][10] = {a};

is equivalent to

char a_p[1][10];
a_p[0][0] = a;

Hopefully, you can see the problem there. As for why you were getting lucky with the size, it's because you set the first dimension of the array to 1. Try this

char a_p[5][10];

and see what you get. There is no work around to avoid the number 10 in the printsize function. The compiler needs the 10 to properly compute the addresses of the elements of the array.

user3386109
  • 34,287
  • 7
  • 49
  • 68
  • I realize that the `[1]` is necessary. That I did on purpose. I don't quite understand your second code block though. Wouldn't it be equivalent to `a_p[0] = a`? (EDIT: See my comment to leeduhem's answer for an elaboration.) – Chris Middleton Mar 12 '14 at 08:53
  • 1
    When you initialize an array, each initializer is assigned to a single element of the array. If you don't specify enough initializers, any remaining elements of the array are filled with zeros. So the compiler thinks you want to initialize element [0][0] with the pointer `a` and leave the rest of the array empty. – user3386109 Mar 12 '14 at 09:04
  • Thank you for that point about the array being filled with zeros - I glossed over it on the first reading. It helps clarify things. – Chris Middleton Mar 12 '14 at 09:33
1

In

char a_p[1][10] = {a};

the value of a, which is the base address of a and has type of pointer to char, is used to initialize a_p[0][0], which should be a char, that is why compiler gives you that conversion warning.

Lee Duhem
  • 14,695
  • 3
  • 29
  • 47
1

Thanks for the answers, everyone. I've given you the answer check, leeduhem, since you pointed out all my misconceptions. However I also wanted to add an answer here (rather than making a messy edit), since I figured out a kludgy way to make it work using variable-length array declarations and a global variable.

#include <stdio.h>

int length;

void printsize(char array_pointer[][length]);

int main(void)
{
    char a[10];

    length = sizeof a;

    for(int i = 0; i < sizeof a; i++)
        a[i] = i;

    char a_p[1][sizeof a];
    for(int i = 0; i < sizeof a; i++)
        a_p[0][i] = a[i];

    printsize(a_p);

    return 0;
}

void printsize(char array_pointer[][length])
{
    printf("%lu\n", sizeof(array_pointer[0]));
}

This returns 10 for the size of a.

EDIT: Here's a version that uses pointers to get around the problem. It gets the value of length at runtime but somehow the declarations depending on length are still okay. (I tried passing in the array directly, but it gave me back the char pointer size, not the array size.)

#include <stdio.h>

int length;

int arraylength(char (*a)[length]);

int main(void){

    int n;

    printf("Enter array length: ");
    scanf("%d", &n);

    char a[n];
    for(int i = 0; i < sizeof a; i++)
        a[i] = i;

    length = sizeof a;

    printf("%d\n", arraylength(&a));

}

int arraylength(char (*a)[length]){ /* pointer to array of chars */

    return sizeof (*a); // note that the "(*a)" here is a deferenced array

}
Chris Middleton
  • 5,654
  • 5
  • 31
  • 68
  • 1
    This is a very mice trick. By the way, you even could read the length of `a` from a command line argument. – Lee Duhem Mar 12 '14 at 09:20
  • Thank you, and yes, that's true. In the example I've given there is no point to doing this, but I think it could be put to use somehow... or maybe not. Haha.. but it was a fun exercise. – Chris Middleton Mar 12 '14 at 09:26