Arrays Revealed

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • weaknessforcats
    Recognized Expert Expert
    • Mar 2007
    • 9214

    Arrays Revealed

    Arrays Revealed

    Introduction
    Arrays are the built-in containers of C and C++. This article assumes the reader has some experiece with
    arrays and array syntax but is not clear on a )exactly how multi-dimensional arrays work, b) how to call
    a function with a multi-dimensional array, c) how to return a multi-dimensional array from a function,
    or d) how to read and write arrays from a disc file.

    Note to C++ programmers: You should be using vectors instead of arrays. The protective code you need
    to write around your array is a duplication of the protective code written around the array in the STL.
    The STL just calls their array plus managing code a vector.

    Note to C programmers: Everything in this article conforms to ANSI-C with the exception of heap allocations.
    The examples use the C++ new operator where a C programmer would call malloc().


    How to Define Arrays
    First, there are only one-dimensional arrays in C or C++. The number of elements is put between brackets:
    [code=c]
    int array[5];
    [/code]

    That is an array of 5 elements each of which is an int.

    [code=c]
    int array[];
    [/code]

    won't compile. You need to declare the number of elements.

    Second, this array:
    [code=c]
    int array[5][10];
    [/code]

    is still an array of 5 elements. Each element is an array of 10 int.

    [code=c]
    int array[5][10][15];
    [/code]

    is still an array of 5 elements. Each element is an array of 10 elements where each element is an array of 15 int.


    [code=c]
    int array[][10];
    [/code]

    won't compile. You need to declare the number of elements.

    Third, the name of an array is the address of element 0
    [code=c]
    int array[5];
    [/code]

    Here array is the address of array[0]. Since array[0] is an int, array is the address of an int. You can assign the name array to an int*.

    [code=c]
    int array[5][10];
    [/code]

    Here array is the address of array[0]. Since array[0] is an array of 10 int, array is the address of an array of 10 int. You can assign the name array to a pointer to an array of 10 int:
    [code=c]
    int array[5][10];

    int (*ptr)[10] = array;
    [/code]

    Fourth, when the number of elements is not known at compile time, you create the array dynamically:

    [code=c]
    int* array = new int[value];
    int (*ptr)[10] = new int[value][10];
    int (*ptr)[10][15] = new int[value][10][15];
    [/code]

    In each case value is the number of elements. Any other brackets only describe the elements.

    Using an int** for an array of arrays is incorrect and produces wrong answers using pointer arithmetic. The compiler knows this so it won't compile this code:

    [code=c]
    int** ptr = new int[value][10]; //ERROR
    [/code]

    new returns the address of an array of 10 int and that isn't the same as an int**.

    Likewise:
    [code=c]
    int*** ptr = new int[value][10][15]; //ERROR
    [/code]

    new returns the address of an array of 10 elements where each element is an array of 15 int and that isn't the same as an int***.

    With the above in mind this array:
    [code=cpp]
    int array[10] = {0,1,2,3,4,5,6, 7,8,9};
    [/code]
    has a memory layout of

    0 1 2 3 4 5 6 7 8 9

    Wheras this array:
    [code=cpp]
    int array[5][2] = {0,1,2,3,4,5,6, 7,8,9};
    [/code]
    has a memory layout of

    0 1 2 3 4 5 6 7 8 9

    Kinda the same, right?

    So if your disc file contains

    0 1 2 3 4 5 6 7 8 9

    Does it make a difference wheher you read into a one-dimensional array or a two-dimensional array? No.

    Therefore, when you do your read use the address of array[0][0] and read as though you have a
    one-dimensional array and the values will be in the correct locations.

    Passing Multi-dimensional Arrays to Functions

    This array:

    [code=cpp]
    int arr[3][4][5];
    [/code]


    can be passed to a function if the argument to func() is a pointer to a [4][5] array:
    [code=cpp]
    void func(int (* arg)[4][5], unsigned int x);

    int main()
    {
    int arr[3][4][5];
    func(arr, 3);
    }
    [/code]

    But if the func argument is a pointer to an [5] array of int, you need to pass the &arr[0][0]:
    [code=cpp]
    void func(int (* arg)[5], unsigned int x, int y);
    int main()
    {
    int arr[3][4][5];
    func(&arr[0][0], 3, 4);
    }
    [/code]

    But if the func argument is a pointer to an int, you need to pass the &arr[0][0][0]:
    [code=cpp]
    void func(int * arg, unsigned int x, int y, int z);
    int main()
    {
    int arr[3][4][5];
    func(&arr[0][0][0], 3, 4, 5);
    }
    [/code]

    As you omit dimensions, notice that you need to add arguments to func() since the "array-ness" is
    lost on the call. This is called decay of array and it occurs whenever an array is passed
    to a function. From inside the function all you see is an address and not an array. That forces you to pass
    the number of elements in the "dimensions ".

    Returning Multi-dimensional Arrays from Functions

    Returning an array from a function only has meaning if the array was created by the function. Otherwise,
    no return is necessary since an existing array is passed by the address. However, if the function has
    created the array on the heap, you can return the address of element 0.

    The problem here is that you can't use the function return type unless you a) return a type or
    b) return a pointer to a type. That is, you cannot return a pointer to an array since an array is not
    a type. So, if you create an array of int you can return the array as an int*:
    [code=cpp]
    int* func(int arg)
    {
    int* temp = new int[arg];
    return temp;
    }
    int main()
    {
    int* arr = func(5);
    }
    [/code]

    This does not work when you create a multi-dimensional array:
    [code=cpp]
    int (*)[5] func(int arg) // ERROR: Cannot return an array
    {
    int (* temp)[5] = new int[arg][5];
    return temp;
    }
    int main()
    {
    int (* arr)[5] = func(4);
    }
    [/code]

    In this case you could pass in the address of a pointer to an array of 5 int:
    [code=cpp]
    void func(int arg, int (**rval)[5])
    {
    int (* temp)[5] = new int[arg][5];
    *rval = temp;
    }
    int main()
    {
    int (* arr)[5] = 0;
    func(4, &arr);
    //arr is now a [4][5] array of int
    }
    [/code]

    However, if you need to use the function as an RVAL, then you need to return a type. Here the easiest thing to do is define a type to be a pointer to an element of your multi-dimensional array. In this example you would need a pointer to an array of 5 int:

    Code:
    typedef int (*IntArray5Ptr)[5];
    Now you have a type that can be returned:

    Code:
    IntArray5Ptr func(int arg)    
    { 
        int (* temp)[5] = new int[arg][5]; 
        return temp; 
    }
    
    int main() 
    { 
        int (* arrA)[5] = func(4); 
    }
    You can return a pointer to type in addition to returning a type. So you could define a type to be an element of your array (in the example this is an array of 5 int) and return a pointer to that type:

    Code:
    typedef int IntArray5[5]; 
      
    IntArray5* funcB(int arg) 
    { 
        int (* temp)[5] = new int[arg][5]; 
        return temp; 
    } 
    
    
    int main() 
    { 
        int (* arr)[5] = func(4); 
    }
    Finally, as was stated at the beginning of this article: There are no multi-dimensional arrays in C++.
    Therefore, a function could just create a one-dimensional array of the correct number of elements and return
    the address of element 0. In this case, element 0 is a type and you can use the return type of a function
    to return a pointer to a type. Then the calling function could typecast the return so the array can be
    used with muliple dimensions:

    [code=cpp]
    int* func(int arg)
    {
    int * temp = new int[arg];
    return temp;
    }
    int main()
    {
    //This is arr[60]
    int* arr = func(60);

    //This is arr[12][5] --> 12 x 5 = 60
    int (*arr1)[5] = (int(*)[5])func(60);

    //This is arr[3][4][5] -> 3 * 4 * 5 = 60
    int (*arr2)[4][5] = (int(*)[4][5])func(60);

    //This is arr[1][3][4][5] -> 1*3*4*5 = 60;
    int (*arr3)[3][4][5] = (int(*)[3][4][5])func(60);



    }
    [/code]

    Revision History

    2010/04/11 - Added examples of returning a multi-dimensional array from an RVAL function.


    Copyright 2010 Buchmiller Technical Associates North Bend WA USA
    Last edited by weaknessforcats; Apr 15 '10, 04:17 PM. Reason: Revision 2010/04/11
  • AmberJain
    Recognized Expert Contributor
    • Jan 2008
    • 922

    #2
    PERFECT........ ....

    What an article.......m ust read for every C newbie like me....
    Thanks for such a nice article

    Comment

    • cube
      New Member
      • Feb 2008
      • 18

      #3
      Very good article!

      If you could write some examples that we could copy and paste them to a compiler, just to see the result, it would be nice!

      Comment

      • weaknessforcats
        Recognized Expert Expert
        • Mar 2007
        • 9214

        #4
        Originally posted by cube
        Very good article!

        If you could write some examples that we could copy and paste them to a compiler, just to see the result, it would be nice!
        How do you mean? All of the code in the article can be compiled.

        Comment

        • cube
          New Member
          • Feb 2008
          • 18

          #5
          Originally posted by weaknessforcats
          How do you mean? All of the code in the article can be compiled.
          Yes, but after compilation, they don't return any results to the screen (propably because there are no printf). And at least for me who just started reading about C is difficult to place a printf on examples with pointers and arrays, etc.
          ...or am I doing sthg wrong and I don't get results??????

          Comment

          • whodgson
            Contributor
            • Jan 2007
            • 542

            #6
            On a minor point, early in the article you say that int a [ ] [10] won't compile. I assume you mean when the code is declaring the array, because it is permissable to call a function which includes an array as its arguement like void print( int a [ ] [10 ]..... or so my book says.
            Thanks for your article

            Comment

            • weaknessforcats
              Recognized Expert Expert
              • Mar 2007
              • 9214

              #7
              Originally posted by whodgson
              On a minor point, early in the article you say that int a [ ] [10] won't compile. I assume you mean when the code is declaring the array, because it is permissable to call a function which includes an array as its arguement like void print( int a [ ] [10 ]..... or so my book says.
              Thanks for your article
              Your book is correct. As a function argument int a [ ] [10 ] is a pointer. As an array definition it is incomplete because it lacks the number of elements in the array. It is the array definition that is referred to in the article.

              Comment

              • euclidav
                New Member
                • Jul 2008
                • 1

                #8
                Thank you .... What a wonderful article !!! I should have read it a couple months back :(

                Comment

                • ajmaschinot
                  New Member
                  • Sep 2008
                  • 1

                  #9
                  Thank you for your guide!

                  Correct me if I am wrong, but shouldn't the following line:

                  int (*ptr)[10];

                  be interpreted as "an array of 10 pointers to int" and not as "a ptr to an array of 10 ints"? I thought that the bracket operator had higher precedence than dereference (hence the parenthesis around *ptr).

                  Comment

                  • weaknessforcats
                    Recognized Expert Expert
                    • Mar 2007
                    • 9214

                    #10
                    Originally posted by ajmaschinot
                    Thank you for your guide!

                    Correct me if I am wrong, but shouldn't the following line:

                    int (*ptr)[10];

                    be interpreted as "an array of 10 pointers to int" and not as "a ptr to an array of 10 ints"? I thought that the bracket operator had higher precedence than dereference (hence the parenthesis around *ptr).
                    An array of 10 pointers to int would be:

                    int* ptr[10];

                    The parentheses are needed to define ptr as a pointer to an array of 10 rather than ptr as an array of 10.

                    Comment

                    • tux4life
                      New Member
                      • Jul 2009
                      • 5

                      #11
                      I see that you didn't cover unsized array declarations, so I'll provide an example here.

                      As already told by weaknessforcats, the following declaration isn't possible:
                      Code:
                      int array[]; // not possible
                      unless you directly initialize the array, like this:
                      Code:
                      int array[] = {1,2,3,4,5}; // an array of 5 elements

                      Comment

                      • weaknessforcats
                        Recognized Expert Expert
                        • Mar 2007
                        • 9214

                        #12
                        Yes but now you are specifying the nunber of elements. I was trying to illustrate that unless you specify the number of elements, the compiler will not compile your code.

                        I didn't want pot get into initialization schemes since these do not seem to cause problems.

                        Comment

                        • ashitpro
                          Recognized Expert Contributor
                          • Aug 2007
                          • 542

                          #13
                          weaknessforcats , Could you please update this article to include array traversal?

                          You can visit C/C++ forum and see why I am asking this favor.

                          Comment

                          • vijith vijayan
                            New Member
                            • Aug 2011
                            • 5

                            #14
                            i think in C & C++ inside a loop a[i] can also be used as i[a],hence (a+i)=(i+a).... i m not sure.Anyways good article...

                            Comment

                            • weaknessforcats
                              Recognized Expert Expert
                              • Mar 2007
                              • 9214

                              #15
                              That's true. arr[5] is the same as arr + 5. Also, 5 + arr is the same as arr + 5. Threfore,
                              5[arr] is the same as arr[5].

                              However, if you code this way expect a little heat in your code reviews.
                              Last edited by weaknessforcats; Aug 27 '11, 09:41 PM. Reason: line formatting

                              Comment

                              Working...