Results 1 to 8 of 8

Thread: surprised that not being able to work between 2d arrays and pointers interchangeably

  1. #1
    Join Date
    Aug 2012
    Beans
    623

    surprised that not being able to work between 2d arrays and pointers interchangeably

    Hello,
    Say I pass a two dimensional array to a function and receive it in the function definition, as either int* a or int a[][colsize]. I'm surprised that inside the function I cannot access it using both pointers and the normal array notation immaterial of how it was received by the function definition. Surprised because arrays are basically designed using pointers(jumping above memory locations intelligently) and I would have expected both to work because a[i][j] is basically *(a+i*colsize+j). Let me give an example

    1.Receiving the array in the function definition using the array notation and trying to access elements using pointers
    Code:
    #include <stdio.h>
    
    int i = 0;
    int j = 0;
    
    int main(void)
    {
     int a[2][5] = {
                  {1,2,3,4,5},
                  {6,7,8,9,10},
                 };
     print(a);
    
     return 0;
    }
    
    int print(int a[][5])
    {
     for(i=0;i<2;i++)
     {
      for(j=0;j<5;j++)
      {
       //printf("%d ",a[i][j]); This obviously works, but the one below does not work
       printf("%d ",*(a+i*5+j)); //Does not work
      }
      printf("\n");
     }
    }
    Output
    Gives me a warning : warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int *’ [-Wformat] and random output when I run it.

    2.Receiving the array in the function definition using the pointer notation and trying to access using elements array notation
    Code:
    #include <stdio.h>
    
    int i = 0;
    int j = 0;
    
    int main(void)
    {
     int a[2][5] = {
                  {1,2,3,4,5},
                  {6,7,8,9,10},
                 };
    
     print(a);
    
     return 0;
    }
    
    
    int print(int* a)
    {
     for(i=0;i<2;i++)
     {
      for(j=0;j<5;j++)
      {
       //printf("%d ",*(a+i*5+j)); This works, but why doesn't the notation below work? Aren't they the same thing?
       printf("%d ",a[i][j]); //Does not work
      }
      printf("\n");
     }
    }
    Output
    error: subscripted value is neither array nor pointer nor vector



    Works for oned arrays
    I'm surprised because the same experiment works for one dimensional arrays and I'm able to use the notations interchangeably. See example below, both of these work
    3.Receiving the array in the function definition using the array notation and trying to access using elements pointer notation. This works.
    Code:
    #include <stdio.h>
    
    int i = 0;
    
    int main(void)
    {
     int a[] = {1,2,3,4,5};
    
     print(a);
    
     return 0;
    }
    
    int print(int a[])
    {
     for(i=0;i<10;i++)
      printf("%d ",*(a+i)); //Works fine
    }
    4.Receiving the array in the function definition using the pointer notation and trying to access using elements array notation. This also works
    Code:
    #include <stdio.h>
    
    int i = 0;
    
    int main(void)
    {
     int a[] = {1,2,3,4,5};
    
     print(a);
    
     return 0;
    }
    
    int print(int* a)
    {
     for(i=0;i<10;i++)
      printf("%d ",a[i]);//Works fine
    }

    Please advise as to why 1 and 2(above) don't work.
    Thanks.
    Last edited by IAMTubby; October 23rd, 2014 at 04:50 PM.

  2. #2
    Join Date
    May 2007
    Beans
    Hidden!
    Distro
    Ubuntu

    Re: surprised that not being able to work between 2d arrays and pointers interchangea

    The reason behind these not working comes down to an understanding in how C either passes arrays by value (not a pointer) or by reference (pointer). In the first case, you are passing the array by value, so the entire array is copied when it's passed to the print function. Therefore, you can't update its values and have the results reflected in the main function. You can still print using pointers though, but you have to start at "a[0][0]", not just "a":
    Code:
    printf("%d ", *(&a[0][0]+i*5+j));
    In the second case, you're passing a pointer. Since the function doesn't know the column size, it can't properly set the offset the first [] (in this case, 5*sizeof(int)). In cases 3 and 4, no such offset is needed, so as long as the code knows it's an int, it can jump to the correct location in memory (since it knows sizeof(int)).

  3. #3
    Join Date
    Oct 2014
    Location
    Arkansas
    Beans
    14
    Distro
    Kubuntu 14.04 Trusty Tahr

    Re: surprised that not being able to work between 2d arrays and pointers interchangea

    Your problem with 1 is you are only dereferencing it once. To access it like you intend you need to do the following:
    Code:
    printf("%d ", *(*(a+i)+j));
    Your problem with 2 is you are specifying just a pointer rather than a pointer to a pointer. You need it to be a pointer to a pointer so you can dereference the second array; otherwise, it will complain about getting a pointer to int because that is what you are giving it.

  4. #4
    Join Date
    Mar 2011
    Beans
    144
    Distro
    Ubuntu 12.04 Precise Pangolin

    Re: surprised that not being able to work between 2d arrays and pointers interchangea

    The best way to deal with these problems - for me, at least - it to draw a picture.


    Code:
    |1|2|3|4|5|   |6|7|8|9|10| // [5]
     ^            ^
     |            |
     |            |
    |ptr1        |ptr2        | // [2]
    You know that the value in memory "cubby-hole" is just an address to another contiguous block of memory which stores 5 elements. That is, the value of ptr1 is the address of the block containing values 1-5. We want to dereference the address of ptr1 because the value (brought about by the dereferencing) is a pointer to the block of 5 values. So, we can slowly build it up: *(a+i) // the addr of "what we want". Now we need to dereference that address to get the values: *(*(a+i)) , not forgetting to incremement by j to move along as the loop goes: *(*(a+i)+j)

    This may seem unhelpful as the answers are already given. My point is that I find it helpful to go back to good old pen and paper just to prove things and understand them. Perhaps other people may find this approach useful.
    Last edited by 3246251196; October 24th, 2014 at 12:21 AM.

  5. #5
    Join Date
    Feb 2009
    Beans
    1,469

    Re: surprised that not being able to work between 2d arrays and pointers interchangea

    Quote Originally Posted by Rocket2DMn View Post
    The reason behind these not working comes down to an understanding in how C either passes arrays by value (not a pointer) or by reference (pointer). In the first case, you are passing the array by value, so the entire array is copied when it's passed to the print function <snip>
    This is not true; C will never implicitly copy an array. The issue here is a question of what type the pointer is.

    In the following case,
    Code:
    int print(int a[][5]);
    you declare print as taking a pointer to array[5] of int. In that context, (a+i*5+j) is also of type pointer to array[5] of int, and when you dereference it, you get a value of type array[5] of int, which, since this is not one of the three exceptional cases, decays into pointer to int. That's why you get a warning about passing pointer-to-int to printf() when it is expecting int.

    This is where some people might think the type is the problem, and that they could get the right value by converting the pointer to the right type with a cast before dereferencing it: *(int *)(*(a+i*5+j)). But that would be a big mistake: the pointer you get in that case is not even pointing at the right location! Pointer arithmetic is done with respect to the size of the pointed-to type, so when the pointer is a pointer-to-array[5]-of-int, adding N to the pointer increments it by N times the size of array[5]-of-int. I mention this because the type error was the first hint that you were making a mistake. If you had silenced that error with a cast, as people often want to do, you might not have any idea that the indexing was all wrong. You didn't do that, and so I congratulate you on not falling into this trap. Keep this in mind: don't use casts to silence type errors, fix the type errors.

    Steven_Williams has a solution that fixes that problem, but I'd like to point out (no pun intended) that there is a huge difference between "a pointer to a pointer" and "a pointer to an array" in a construct like *(*(a+i)+j). When a is of type pointer-to-pointer-to-int, *(*(a+i)+j) suggests that a points to an array of pointer to int, which is very different from the pointer to array of int you actually have. The differences between arrays and pointers go beyond just their types.

    In the second case, if you had warnings turned on, you should have gotten one about the implicit declaration of the `print' function. Because you didn't declare print before using it, like this:
    Code:
        int print(int *);
    (either inside or outside main), you missed out on another warning that would have given you another clue to the problem in that code:
    Code:
    foobar.c: In function ‘main’:
    foobar.c:14:8: warning: passing argument 1 of ‘print’ from incompatible pointer type
      print(a);
            ^
    foobar.c:13:6: note: expected ‘int *’ but argument is of type ‘int (*)[5]’
      int print(int *);
          ^
    In other words, you defined a function that takes a pointer-to-int, and passed it a pointer-to-array[5]-of-int. Although the pointer's value is the same, the type mismatch is an important clue to how these two cases work differently, and why you can't treat them as the same. (Compile with at least "-std=c11 -Wall -Wextra" to be warned about these and many other classes of errors.)

    tl;dr No, a[i*N + j] is never the same as a[i][j]. BUT, if a and b are defined like this:
    Code:
    int a[M*N];
    int b[M][N];
    Then a[i*N + j] does essentially the same thing as b[i][j]. The i*N + j thing is basically a trick to pretend you have a two-dimensional array when all you have is a one-dimensional one. There's no reason to do such gymnastics when you actually have a two-dimensional array -- that is, when N is known at compile time.

    I'll happily ramble on if you have followup questions, but I also agree with what Steven_Williams said in another thread: You can't effectively learn C by experimentation. I don't know if you have a book -- we have book threads around here on a semi-regular basis, so you ought to be able to find some useful information by searching. Also, I may have mentioned this before, but you will learn a lot about C by lurking in comp.lang.c and posting questions on Stack Overflow (you can post in comp.lang.c, but I suggest not without your flame-retardant underwear). C is complex and a lot of people are willing to help out, but you have to know where to go.

  6. #6
    Join Date
    Aug 2012
    Beans
    623

    Re: surprised that not being able to work between 2d arrays and pointers interchangea

    Quote Originally Posted by Steven_Williams View Post
    Your problem with 2 is you are specifying just a pointer rather than a pointer to a pointer. You need it to be a pointer to a pointer so you can dereference the second array; otherwise, it will complain about getting a pointer to int because that is what you are giving it.
    Quote Originally Posted by trent.josephsen View Post
    you defined a function that takes a pointer-to-int, and passed it a pointer-to-array[5]-of-int. Although the pointer's value is the same, the type mismatch is an important clue to how these two cases work differently, and why you can't treat them as the same.

    The i*N + j thing is basically a trick to pretend you have a two-dimensional array when all you have is a one-dimensional one. There's no reason to do such gymnastics when you actually have a two-dimensional array -- that is, when N is known at compile time.
    Steven_Williams, trent.josephsen
    I clearly understand what both of you say, thank you so much. I have been trying to get it to work based on the explanation above and my new understanding that I should receive it as a pointer to pointer.
    I tried with this change, but not there yet. Where am I going wrong?
    Code:
    #include <stdio.h>
    
    
    int i = 0;
    int j = 0;
    
    
    int main(void)
    {
     int a[2][5] = {
                  {1,2,3,4,5},
                  {6,7,8,9,10},
                 };
    
    
     print(a);
    
    
     return 0;
    }
    
    
    int print(int** a)//Made this change
    {
     for(i=0;i<2;i++)
     {
      for(j=0;j<5;j++)
      {
       //printf("%d ",a[i][j]);/Does not work
       printf("%d ",*(*(a+i)+j));//Does not work
      }
      printf("\n");
     }
    }
    PS : I have been able to get the first one to work based on the change you two suggested : *(*(a+i)+j). I still haven't got the "decaying to pointer to int" part though. Let me do some reading up and come back after some time.
    Last edited by IAMTubby; October 25th, 2014 at 02:13 PM.

  7. #7
    Join Date
    Aug 2011
    Location
    47°9′S 126°43W
    Beans
    2,172
    Distro
    Ubuntu 16.04 Xenial Xerus

    Re: surprised that not being able to work between 2d arrays and pointers interchangea

    Array notation works in "print()" if you declare the parameter properly (note that normally you should declare the function before using it...):

    Code:
    #include <stdio.h>
    
    int i = 0;
    int j = 0;
    
    void print(int (*a)[5]) 
    {
     printf("Print: a at %p, a[0]=%p, a[1]=%p\n",a,a[0],a[1]);
     for(i=0;i<2;i++)
     {
      for(j=0;j<5;j++)
      {
       printf("%d ",a[i][j]);
      }
      printf("\n");
     }
    }
    
    int main(void)
    {
     int a[2][5] = {
                  {1,2,3,4,5},
                  {6,7,8,9,10},
                 };
     printf("Main: a at %p, a[0]=%p, a[1]=%p\n",a,a[0],a[1]);
     print(a);
    
     return 0;
    }
    See the explanation here
    Warning: unless noted otherwise, code in my posts should be understood as "coding suggestions", and its use may require more neurones than the two necessary for Ctrl-C/Ctrl-V.

  8. #8
    Join Date
    Aug 2012
    Beans
    623

    Re: surprised that not being able to work between 2d arrays and pointers interchangea

    Quote Originally Posted by ofnuts View Post
    Array notation works in "print()" if you declare the parameter properly (note that normally you should declare the function before using it...):

    Code:
    #include <stdio.h>
    
    int i = 0;
    int j = 0;
    
    void print(int (*a)[5]) 
    {
     printf("Print: a at %p, a[0]=%p, a[1]=%p\n",a,a[0],a[1]);
     for(i=0;i<2;i++)
     {
      for(j=0;j<5;j++)
      {
       printf("%d ",a[i][j]);
      }
      printf("\n");
     }
    }
    
    int main(void)
    {
     int a[2][5] = {
                  {1,2,3,4,5},
                  {6,7,8,9,10},
                 };
     printf("Main: a at %p, a[0]=%p, a[1]=%p\n",a,a[0],a[1]);
     print(a);
    
     return 0;
    }
    See the explanation here
    ofnuts, I went through the link you gave me and it fits in perfectly with my question. I also understood your example and the one given in the link. So 'a' is basically a pointer to an array that holds 5 ints. just underlining it because I like to read these words in one shot(makes it easy for me to visualize)

    I have a couple of questions:
    1.I'm kind of feeling disappointed reading this line from the link you gave me, "An array of arrays (i.e. a two-dimensional array in C) decays into a pointer to an array, not a pointer to a pointer". I mean, is there any reason to that?
    Passing a two-d array and receiving it as int** p; is perfectly correct, I feel, meaning wise. Why is it that the array decays into a pointer to an array?

    2.When I receive a one dimensional array inside a function like

    Code:
    main()
    {
     int myarray[] = {1,2,3,4,5};
     fun(myarray);
    }
    
    int fun(int a[])
    {
    
    }
    What does that notation int a[] actually mean? myarray means &myarray[0] which means the address of the first element, not able to understand what int a[] means.
    Last edited by IAMTubby; October 26th, 2014 at 02:51 PM.

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •