Programming Forums

Programming Forums (http://www.programmingforums.org/forumindex.php)
-   C (http://www.programmingforums.org/forum60.html)
-   -   Confusion With Pointers and Arrays (http://www.programmingforums.org/showthread.php?t=11277)

JawaKing00 Sep 8th, 2006 4:39 PM

Confusion With Pointers and Arrays
 
Ok, Pointers confuse the heck out of me, and pointers to arrays only make things worse.

Right now, I've got a number of two dimensional arrays of structures where one of the elements of the structure is a string array. On startup, depending on the configuration of the system, only one of the arrays will be used. I need to handle this by using a pointer to point to that array.

Basically I have no clue how to set up that pointer to correctly point to that array. Here is an example of my situation:

:

// The structure (INT8U is an unsigned 8 bit character)
typedef struct
{
    INT8U ID;
    INT8U AnotherValue;
    INT8U NameString[20];
} ITEM_DEFINE_t;

// The first array
ITEM_DEFINE_t FirstItemDefinitionArray[2][4] =
{
    {
        {0,  1,  "First Item"},
        {1,  1,  "Second Item"},
        {2,  1,  "Third Item"},
        {3,  1,  "Fourth Item"},
    },
    {
        {0,  2,  "First Item"},
        {1,  2,  "Second Item"},
        {2,  2,  "Third Item"},
        {3,  2,  "Fourth Item"},
    }
};

// The second array
ITEM_DEFINE_t SecondItemDefinitionArray[2][4] =
{
    {
        {0,  3,  "First Item"},
        {1,  3,  "Second Item"},
        {2,  3,  "Third Item"},
        {3,  3,  "Fourth Item"},
    },
    {
        {0,  4,  "First Item"},
        {1,  4,  "Second Item"},
        {2,  4,  "Third Item"},
        {3,  4,  "Fourth Item"},
    }
};

// Ok this is one part of the problem.
// How do I define the pointer to correctly reference the array?
ITEM_DEFINE_t **ItemPointer;

// This is the next part.
// How do I correctly set the pointer to reference the array?
ItemPointer = SecondItemDefinitionArray;

// The final part, and the part that I have the least problem with
// How do I correctly reference a member in the structure that the pointer is pointing too?
printf ("%s", ItemPointer[1][1]->NameString);


Any help would be hugely appreciated. Like I said, Pointers confuse me to no end.

Jimbo Sep 8th, 2006 4:51 PM

I'd recommend reading either Narue's tutorial or Dawei's tutorial. If you can spare the time, read both ;)

synchronized Sep 8th, 2006 5:31 PM

I think a quick tutorial is in order. Its not that hard just getting to understand the syntax for a novice can be hard. Here I go.

I will start with a 1 dimensional array. A string will do as I am not feeling too creative tonight.

:

#include <stdio.h>

int main()
{
    char MyArray[] = "Hello World";
   
    //To get to the fist element using the [] notation you would do this
    printf("%c\n", MyArray[0]);

    //That would print n, but what if i wanted to use a pointer to
    //do the same thing
    printf("%c\n", *(MyArray));
    //That will produce the same result as before printing a "H"

    //Now what if i wanted to get to the second character(second index)
    printf("%c\n", MyArray[1]);
    printf("%c\n", *(MyArray + 1));
    //All i did here was add one to the address and C is smart enough to
    //move the pointer along to the start of the next value. It does not matter
    // if the size of each element is one byte or 10 bytes it will always work.
}


So that was one dimensional arrays, but what if you want two dimensions

:

#include <stdio.h>

int main()
{
    char MyArray[3][3] = {
                                    {'1', '2', '3'}
                                    {'4', '5', '6'}
                                    {'7', '8', '9'}
                                  };
    //First lets look at the address of MyArray
    printf("%p\n", MyArray);
    //Now lets look at the adress of MyArray[0][0]
    printf("%p\n", &MyArray[0][0]);
    //Now lets see what is in MyArray[0]
    printf("%p\n", MyArray[0]);

    //If you run that code what will be printed?

  //They will all print the same memory address as simple a 2D array
  // is just an array of arrays.

  //Take a look at the values in MyArray

  printf("%c\n", MyArray[0][0]);
  printf("%c\n", *MyArray[0]);
  printf("%c\n", **MyArray);
 
  //This should all print 1 as they all refer to the same location.

 
}


So this is all the ways to get at the elements in MyArray

MyArray[0][0] = *MyArray[0] = **MyArray
MyArray[0][1] = *(MyArray[0]+1) = *(*MyArray+1)
MyArray[0][2] = *(MyArray[0]+2) = *(*MyArray+2)
MyArray[1][0] = *(MyArray[0]+3) = *(*MyArray+3) = *MyArray[1]
MyArray[1][1] = *(MyArray[0]+4) = *(*MyArray+4) = *(MyArray[1]+1)
MyArray[1][2] = *(MyArray[0]+5) = *(*MyArray+5) = *(MyArray[1]+2)
MyArray[2][0] = *(MyArray[0]+6) = *(*MyArray+6) = *MyArray[2] = *(MyArray[1] + 3)
MyArray[2][1] = *(MyArray[0]+7) = *(*MyArray+7) = *(MyArray[2]+1) = *(MyArray[1] + 4)
MyArray[2][2] = *(MyArray[0]+8) = *(*MyArray+8) = *(MyArray[2]+2) = *(MyArray[1] + 5)

So you can see the pattern and how it works. I will leave it for someone else to make some contrive example for a 2D array. :)

If i have made a silly error then please feel free to kill me :p

Adak Sep 8th, 2006 7:33 PM

Quote:

Originally Posted by JawaKing00
Ok, Pointers confuse the heck out of me, and pointers to arrays only make things worse.

Right now, I've got a number of two dimensional arrays of structures where one of the elements of the structure is a string array. On startup, depending on the configuration of the system, only one of the arrays will be used. I need to handle this by using a pointer to point to that array.

Basically I have no clue how to set up that pointer to correctly point to that array. Here is an example of my situation:

Any help would be hugely appreciated. Like I said, Pointers confuse me to no end.

Internally, C handles array as with pointers. But that's internally - YOU can just use the array's name and element number, and off you go.

Pointers are wonderful, and very powerful, but if they're leading you into great confusion, just use them very sparingly. When you're working with a 2d array, pointer notation in your code does NOT help it's clarity, one bit. Array[2]2], is infinitely clearer than any other notation, including pointer.

Adak

Arevos Sep 9th, 2006 4:53 AM

Quote:

Originally Posted by synchronized (Post 113709)
If i have made a silly error then please feel free to kill me :p

Aside from trivial syntax errors, all your code appears to be correct. However, it does not answer the original posters question, which essentially boils down to why this piece of code does not work:
:

  1. my_type** pointer_to_2d_array = my_2d_array;

At first, I didn't understand what was wrong either. It seems logical, after all. And from this logical approach I would infer that JawaKing00 knows more than people are giving him credit for.

JawaKing00 appears to understand the fact that a 2D array is simply an array of arrays, and he comprehends that a pointer can be assigned to an array. What he slips up on is to assume that arrays and pointers are the same thing.

The correct approach can be inferred by DaWei's excellent and knowledgable guide to pointers. However, as far as I can see, the correct way of pointing to a 2D array is ever explicitly mentioned, so I'll mention it now. This is partially for JawaKing00's benefit, partially for mine; if I'm incorrect, I'd like to know about it!
:

  1. ITEM_DEFINE_t (*ItemPointer)[4];
  2. ItemPointer = SecondItemDefinitionArray;
  3. printf ("%s", ItemPointer[1][1].NameString);


grumpy Sep 9th, 2006 7:37 AM

The relationship between pointers and arrays trips everyone up sometime. Heck, I typed a response to a question about passing multi-dimensional arrays to functions a couple of days back, and got it wrong too (that was at the end of a stressful couple of weeks, which started with undergoing knee surgery and then the pain of recovery and physiotherapy to get walking again). I can't find the thread at the moment, but I think Narue corrected my error.

The links to both Narue's and Dawei's tutorials given above are good, and should be read over and over again. They both cover the key issues quite well, although they are written in different styles that may suit different people.

As Arevos has said, arrays are different things from pointers. However, C treats pointers and arrays as equivalent (i.e. interchangeable) in some contexts.

A pointer is a variable that can contain the address of another variable. For example, a pointer to int can contain the address of an int. For example;
:

int main()
{
    int x;
    int *px = &x;      /* px contains the address of x  */
    *px = 2;            /*  sets x to be 2  */
}

Note that there is no array in the above. However, if we have an array, the name of that array is (in the beloved programming language called C) treated by the compiler as the address of the first element in that array. For example;
:

int main()
{
    int array[5];
    int *px = x;        /* px contains the address of x[0] */
    *px = 2;            /*  sets x[0] to be 2  */
    *(px + 1) = 5    /*  sets x[1] to be 5 */
    px[1] = 5;        /*  does exactly the same thing as the previous line */
}

This example illustrates the equivalence of an array to a pointer. But, if we do this;
:

int main()
{
    int *px;            /* px is an uninitialised pointer */
    *px = 2;            /*  sets the value pointed at by px to be 2 */
    px[0] = 2;          /*  same as previous line */
}

This code actually yields undefined behaviour, as px has been uninitialised, so does not point at anything. This means that computing *px (or, equivalently, px[0]) could do anything. Assigning the result of either expression to 2, as in the above example, typically yields strange behaviour (eg a program crash). px is a pointer, but it is NOT an array.

Multi-dimensional arrays are where this breaks down. A multi-dimensional array is an array of arrays, but (as of the 1989 C standard) it is not equivalent to a pointer to a pointer. In some contexts, it can be treated as a pointer to an array. For example;
:

int main()
{
    int x[3][4];    /* an array of 3 arrays of 4 ints */
    int (*px)[4];  /*  a pointer to an array of 4 ints */
    px = x;        /*  OK;  px is now an alias for x */
    px[1][2] = 5; /*  Sets x[1][2] to be 5 */
}

The brackets in the declaration of px mean we are declaring a pointer to an array of 4 int. Leave out the brackets, and we would have declared an array of 4 pointers to int -- which are different things BECAUSE this is an example of where pointers and arrays ARE DIFFERENT. The compiler would then have complained bitterly about the assignment "px = x" (with a message to the effect that px and x have different types).

Where things get a little squirrelly (and can trip up people - like myself - who used C before there was a standard) is that pre-standard versions of C did not recognise an array as a distinct type, so allowed an implicit conversion of an "array of array" to "pointer to pointer". That caused a lot of headaches for programmers (as compilers would not complain about some of those conversions when they really should have, and the program would simply crash for no obvious reason). One of the great services done by the committee that produced the 1989 C standard was that they disallowed this conversion. The trade-off is that most things relating to multi-dimensional arrays in C are difficult to get right, because compilers complain bitterly if the syntax is not "just so". But a whining compiler is much better for a programmer sanity than code which "looks right, compiles OK, but crashes for the end user": end users tend to complain more loudly than compilers.

JawaKing00 Sep 12th, 2006 10:13 AM

Quote:

Originally Posted by Arevos (Post 113737)
JawaKing00 appears to understand the fact that a 2D array is simply an array of arrays, and he comprehends that a pointer can be assigned to an array. What he slips up on is to assume that arrays and pointers are the same thing.

The correct approach can be inferred by DaWei's excellent and knowledgable guide to pointers. However, as far as I can see, the correct way of pointing to a 2D array is ever explicitly mentioned, so I'll mention it now. This is partially for JawaKing00's benefit, partially for mine; if I'm incorrect, I'd like to know about it!
:

  1. ITEM_DEFINE_t (*ItemPointer)[4];
  2. ItemPointer = SecondItemDefinitionArray;
  3. printf ("%s", ItemPointer[1][1].NameString);


That appears to work perfectly. Thank you very much!

I still don't quite understand why, though. I read through both of those tutorials, and I do have a passing knowledge of pointers and arrays, but sometimes they just make my head hurt. I get that that is a pointer to an array of structures, but why is there only one dimension? Wouldn't
:

  1. ITEM_DEFINE_t (*ItemPointer)[2][4];

seem to make more sense? It would to me, but that doesn't work.

Also, why do we need to use the '.' and not '->' when accessing a member of the structure in that array? I thought when using a pointer, that the '->' notation was used, but again that doesn't work.

Finally, why do we initialize the pointer as the whole array and not the address of the array? I would expect something like
:

  1. ItemPointer = &SecondItemDefinitionArray[0][0];

and not
:

  1. ItemPointer = SecondItemDefinitionArray;


I guess my inexperience with pointers to arrays is showing here, but I'd like to know more about the reasoning behind this. Especially since I'll probably need to explain it to the lead engineer on the project, since he doesn't appear to fully understand this implementation either :D

Thank you again for all the help. It has been incredibly useful.

Arevos Sep 12th, 2006 12:15 PM

Quote:

Originally Posted by JawaKing00 (Post 113972)
I get that that is a pointer to an array of structures, but why is there only one dimension? Wouldn't
:

  1. ITEM_DEFINE_t (*ItemPointer)[2][4];

seem to make more sense? It would to me, but that doesn't work.

In order to get a pointer of an array, we must assign the address of the first element of the array to a pointer of the appropriate type. Thus, if we have an array of ints, then we can assign a pointer to it in the following manner:
:

  1. int *p = &(array_of_ints[0])

Or, more concisely:
:

  1. int *p = array_of_ints

Now, you already know, I presume, that a 2D array is merely an array of arrays. Thus, instead of an int pointer, we'd need an int array pointer:
:

  1. int (*p)[5] = two_dimentional_array_of_ints

Even though C can convert an array into a pointer, it is not clever enough to convert a pointer to an array into a pointer to a pointer. That's why we need to have a pointer to an array, or (*p)[n].

Quote:

Originally Posted by JawaKing00 (Post 113972)
Also, why do we need to use the '.' and not '->' when accessing a member of the structure in that array? I thought when using a pointer, that the '->' notation was used, but again that doesn't work.

Your array contains ITEM_DEFINE_t, and not ITEM_DEFINE_t*, which is why you need a . and not a ->.

DaWei Sep 13th, 2006 12:56 PM

If you want to see the difference between an array name used as a pointer, and the usage of a pointer to the same array, simply write the two instructions (which look identical) and examine the emitted assembly code. In many cases the compiler babies us (at the cost of some confusion). After all, we all know we can't assign to arrays, but we all write char myStuff [] = "ABC";. ;)

grumpy Sep 14th, 2006 5:11 AM

:D Dawei, an assignment for you: write on the blackboard 100 times "Dawei should not tell lies".


All times are GMT -5. The time now is 12:54 AM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC