2

I am new to C and got stuck on "How to pass multidimensional array to a function". I do understand that you have to pass column size so that if you want to go from a[0][0] to a[1][0] it can calculate the size of 1-D array and can jump over it.

I have written a small program:

#include<stdio.h>
void foo(char[][2]);
int main()
{
     char arr[2][2] = {'a','b','c','d'};

     foo(arr);
     return 0;
}

void foo(char temp[][2])
{
      temp++;
      printf("%c",*temp);
}

I expected that this code will print the letter c, since temp initially points to 'a' and once we increment it, it will skip over first 1-d array and go to first element of second 1-d array which is 'c'. But it does not work that way I guess.

Please explain the how compiler computes these addresses.

5
  • Didn't you get a warning when compiling that code? If not, use -Wall. Don't ignore warnings. Commented Oct 2, 2015 at 13:43
  • @CoolGuy - The only error OP original will get is non-fatal run-time due to parameter mismatch, and a subsequent -1 return error from printf. I have my compiler set to aggressive, and get no warnings or errors. Commented Oct 2, 2015 at 14:56
  • @ryyker I get warning: format ‘%c’ expects argument of type ‘int’, but argument 2 has type ‘char *’ [-Wformat=] and warning: missing braces around initializer [-Wmissing-braces]. Are you sure you get no warnings? Commented Oct 2, 2015 at 15:33
  • @CoolGuy - Yes, I am sure. I have set my compiler to both C89 and C99, both times set to aggressive. No warnings or errors at compile time. However, from looking at comments under Olaf's post, I see he also gets warnings. He is using GCC. What are you using? Commented Oct 2, 2015 at 15:43
  • @ryyker I'm using GCC too. Here is a demo where you can see the warnings emitted. Commented Oct 2, 2015 at 15:46

3 Answers 3

6

The pointer arithmetic is good. You just forgot to dereference *temp, which has type char[2].

Change it to **temp, and you'll get an output c.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for quick reply it worked. but could you please explain how compiler computes the address of a 2-D array or given me some link where i can find explanation .
3

For function argument char temp[][2], temp decays to a pointer. But only for the first (outer) dimension. So it is effectively a pointer to array [2] of char.

Incrementing the pointer will advance it to the next outer index, as you already assumed.

So, you either have to use (*temp)[0] or **temp to get the first element. **temp works, because *temp is an array itself, so it decays to a pointer to the first element of the inner array. The second (left) * then dereferences this pointer: c.

Note that, allthough it uses the same syntax like char **temp, they are fundamentally different. A pointer is not an array and here, incrementing temp will only advance by the size of a pointer, which is not what you want.

Note that the initializer would better be according to the 2-simensional nature:

{ { 'a', 'b' } , { 'c', 'd' } }

This ensures you get the correct values for the inner arrays and is good practice. Omitting a value in the non-nested form will result in wrong sequence for the inner array. When having enabled the recommended warnings (-Wall) at least, gcc warns about missing braces.

5 Comments

Out of curiosity, for what compiler settings are you able to get a compiler error (or even a warning) for the initializer OP used? It appears to be completely legal and sufficient. By adding the curly braces, you've improved readability, but nothing else.
@ryyker: Just the standard warnings enabled: gcc -std=c11 -Wall -O3 test.c test.c: In function ‘main’: test.c:19:2: warning: missing braces around initializer [-Wmissing-braces]
Hmm. must be a gcc extension thing. The C standard does not require the inner braces.
@ryyker: Huh? This is an array of array, so they are actually fully compliant - they are not an extension. Hmm, actually you seem to have a point, reading on in the linked paragraph, the original versions seems to be correct, too. However, that looks like another legacy to me.
My internet is spotty where I am right now. Sorry for delay. It would have been more correct for me to say that inner braces, although not required, remove ambiguity to initialization by adding specificity to where each initializer will be placed in memory (or in the array). Your edit is good.
3

Except when it is the operand of the sizeof or unary & operator, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element in the array.

In main, the type of the expression arr in the function call foo(arr) is "2-element array of 2-element array of char"; since it isn't the operand of the sizeof or unary & operators, it "decays" to an expression of type "pointer to 2-element array of char", or char (*)[2].

Thus, the parameter temp is type char (*)[2], and it points to the first element of arr. The parameter declaration char temp[][2] is equivalent to char (*temp)[2]. The expression *temp is equivalent to temp[0], and both have type "2-element array of char" (char [2]). The expression temp + 1 gives you the address of the next 2-element array of char, so *(temp + 1) is equivalent to temp[1].

Here's a table to summarize all of that:

    Expression        Type            Decays To         Value
    ----------        ----            ---------         -----
           arr        char [2][2]     char (*)[2]       &arr[0][0]
          *arr        char [2]        char *            arr[0]
          &arr        char (*)[2][2]  n/a               &arr[0][0]
        arr[i]        char [2]        char *            &arr[i][0]
       *arr[i]        char            n/a               arr[i][0]
       &arr[i]        char (*)[2]     n/a               &arr[i][0]
     arr[i][j]        char            n/a               arr[i][j]

          temp        char (*)[2]     n/a               &arr[0][0]
         *temp        char [2]        char *            arr[0]
         &temp        char (**)[2]    n/a               addr of temp variable
       temp[i]        char [2]        char *            &arr[i][0]
      *temp[i]        char            n/a               arr[i][0]
      &temp[i]        char (*)[2]     n/a               &arr[i][0]
    temp[i][j]        char            n/a               arr[i][j]

       arr + 1        char [2][2]     char (*)[2]       &arr[1][0]
    *(arr + 1)        char [2]        char *            arr[1]
      temp + 1        char (*)[2]     n/a               &arr[1][0]
   *(temp + 1)        char [2]        char *            arr[1]

     arr[0][0]        char            n/a               'a'
     arr[0][1]        char            n/a               'b'
     arr[1][0]        char            n/a               'c'
     arr[1][1]        char            n/a               'd'

        **temp        char            n/a               'a'
      *temp[0]        char            n/a               'a'
    temp[0][0]        char            n/a               'a'

  **(temp + 1)        char            n/a               'c'
      *temp[1]        char            n/a               'c'
    temp[1][0]        char            n/a               'c'

So, in your print statement, you would write either

printf("%c", **temp); // temp == &arr[1][0] after temp++

or

printf("%c", *temp[0]); // temp[0] == arr[1] after temp++

or

printf("%c", temp[0][0]); // temp[0][0] == arr[1][0] after temp++

The expressions arr, &arr, *arr, arr[0], &arr[0], *arr[0], and &arr[0][0] all yield the same value - the address of the first element of arr (remember that the address of the array and the address of the first element of the array are the same). They just differ in type.

Note that &temp gives us a different value than &arr. Since temp was declared as a pointer, not an array, &temp gives us the address of the pointer variable.

A picture may help:

      +---+    
 arr: |'a'|  arr[0][0] <------------------+ before temp++
      +---+                               |
      |'b'|  arr[0][1]                    |
      +---+                               |
      |'c'|  arr[1][0] <--+ after temp++  |   
      +---+               |               |
      |'d'|  arr[1][1]    |               |
      +---+               |               |
       ...                |               |
      +---+               |               |
temp: |   |---------------+---------------+
      +---+

2 Comments

Thanks for the nicely explained answer.
The table is awesome.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.