423,680 Members | 2,439 Online
Bytes IT Community
Submit an Article
Got Smarts?
Share your bits of IT knowledge by writing an article on Bytes.

Arrays Revealed

weaknessforcats
Expert Mod 5K+
P: 9,186
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:
Expand|Select|Wrap|Line Numbers
  1. int array[5];
  2.  
That is an array of 5 elements each of which is an int.

Expand|Select|Wrap|Line Numbers
  1. int array[];
  2.  
won't compile. You need to declare the number of elements.

Second, this array:
Expand|Select|Wrap|Line Numbers
  1. int array[5][10];
  2.  
is still an array of 5 elements. Each element is an array of 10 int.

Expand|Select|Wrap|Line Numbers
  1. int array[5][10][15];
  2.  
is still an array of 5 elements. Each element is an array of 10 elements where each element is an array of 15 int.


Expand|Select|Wrap|Line Numbers
  1. int array[][10];
  2.  
won't compile. You need to declare the number of elements.

Third, the name of an array is the address of element 0
Expand|Select|Wrap|Line Numbers
  1. int array[5];
  2.  
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*.

Expand|Select|Wrap|Line Numbers
  1. int array[5][10];
  2.  
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:
Expand|Select|Wrap|Line Numbers
  1. int array[5][10];
  2.  
  3. int (*ptr)[10] = array;
  4.  
Fourth, when the number of elements is not known at compile time, you create the array dynamically:

Expand|Select|Wrap|Line Numbers
  1. int* array = new int[value];
  2. int (*ptr)[10] = new int[value][10];
  3. int (*ptr)[10][15] = new int[value][10][15];
  4.  
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:

Expand|Select|Wrap|Line Numbers
  1. int** ptr = new int[value][10];    //ERROR
  2.  
new returns the address of an array of 10 int and that isn't the same as an int**.

Likewise:
Expand|Select|Wrap|Line Numbers
  1. int*** ptr = new int[value][10][15];    //ERROR
  2.  
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:
Expand|Select|Wrap|Line Numbers
  1. int array[10] = {0,1,2,3,4,5,6,7,8,9};
  2.  
has a memory layout of

0 1 2 3 4 5 6 7 8 9

Wheras this array:
Expand|Select|Wrap|Line Numbers
  1. int array[5][2] = {0,1,2,3,4,5,6,7,8,9};
  2.  
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:

Expand|Select|Wrap|Line Numbers
  1. int arr[3][4][5];
  2.  

can be passed to a function if the argument to func() is a pointer to a [4][5] array:
Expand|Select|Wrap|Line Numbers
  1. void func(int (* arg)[4][5], unsigned int x);
  2.  
  3. int main()
  4. {
  5.     int arr[3][4][5];
  6.     func(arr, 3);
  7. }
  8.  
But if the func argument is a pointer to an [5] array of int, you need to pass the &arr[0][0]:
Expand|Select|Wrap|Line Numbers
  1. void func(int (* arg)[5], unsigned int x, int y);
  2. int main()
  3. {
  4.     int arr[3][4][5];
  5.     func(&arr[0][0], 3, 4);
  6. }
  7.  
But if the func argument is a pointer to an int, you need to pass the &arr[0][0][0]:
Expand|Select|Wrap|Line Numbers
  1. void func(int * arg, unsigned int x, int y, int z);
  2. int main()
  3. {
  4.     int arr[3][4][5];
  5.     func(&arr[0][0][0], 3, 4, 5);
  6. }
  7.  
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*:
Expand|Select|Wrap|Line Numbers
  1. int* func(int arg)
  2. {
  3.     int* temp = new int[arg];
  4.     return temp;
  5. }
  6. int main()
  7. {
  8.     int* arr = func(5);
  9. }
  10.  
This does not work when you create a multi-dimensional array:
Expand|Select|Wrap|Line Numbers
  1. int (*)[5] func(int arg)   // ERROR: Cannot return an array
  2. {
  3.     int (* temp)[5] = new int[arg][5];
  4.     return temp;
  5. }
  6. int main()
  7. {
  8.     int (* arr)[5] = func(4);
  9. }
  10.  
In this case you could pass in the address of a pointer to an array of 5 int:
Expand|Select|Wrap|Line Numbers
  1. void func(int arg, int (**rval)[5])
  2. {
  3.     int (* temp)[5] = new int[arg][5];
  4.     *rval = temp;
  5. }    
  6. int main()
  7. {
  8.     int (* arr)[5] = 0;
  9.     func(4, &arr);
  10.     //arr is now a [4][5] array of int
  11. }
  12.  
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:

Expand|Select|Wrap|Line Numbers
  1. typedef int (*IntArray5Ptr)[5]; 
Now you have a type that can be returned:

Expand|Select|Wrap|Line Numbers
  1. IntArray5Ptr func(int arg)    
  2.     int (* temp)[5] = new int[arg][5]; 
  3.     return temp; 
  4. }
  5.  
  6. int main() 
  7.     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:

Expand|Select|Wrap|Line Numbers
  1. typedef int IntArray5[5]; 
  2.  
  3. IntArray5* funcB(int arg) 
  4.     int (* temp)[5] = new int[arg][5]; 
  5.     return temp; 
  6.  
  7.  
  8. int main() 
  9.     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:

Expand|Select|Wrap|Line Numbers
  1. int* func(int arg)
  2. {
  3.     int * temp = new int[arg];
  4.     return temp;
  5. }    
  6. int main()
  7. {
  8.     //This is arr[60]
  9.     int* arr = func(60);
  10.  
  11.     //This is arr[12][5] --> 12 x 5 = 60
  12.     int (*arr1)[5] = (int(*)[5])func(60);
  13.  
  14.     //This is arr[3][4][5] -> 3 * 4 * 5 = 60
  15.     int (*arr2)[4][5] = (int(*)[4][5])func(60);
  16.  
  17.     //This is arr[1][3][4][5] -> 1*3*4*5 = 60;
  18.     int (*arr3)[3][4][5] = (int(*)[3][4][5])func(60);
  19.  
  20.  
  21.  
  22. }
  23.  
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
Feb 18 '08 #1
Share this Article
Share on Google+
29 Comments


AmberJain
Expert 100+
P: 884
PERFECT............

What an article.......must read for every C newbie like me....
Thanks for such a nice article
Feb 20 '08 #2

P: 18
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!
Feb 20 '08 #3

weaknessforcats
Expert Mod 5K+
P: 9,186
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.
Feb 21 '08 #4

P: 18
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??????
Feb 28 '08 #5

100+
P: 542
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
Apr 10 '08 #6

weaknessforcats
Expert Mod 5K+
P: 9,186
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.
Apr 10 '08 #7

euclidav
P: 2
Thank you .... What a wonderful article !!! I should have read it a couple months back :(
Jul 18 '08 #8

P: 1
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).
Sep 29 '08 #9

weaknessforcats
Expert Mod 5K+
P: 9,186
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.
Sep 29 '08 #10

P: 5
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:
Expand|Select|Wrap|Line Numbers
  1. int array[]; // not possible
  2.  
unless you directly initialize the array, like this:
Expand|Select|Wrap|Line Numbers
  1. int array[] = {1,2,3,4,5}; // an array of 5 elements
  2.  
Jul 13 '09 #11

weaknessforcats
Expert Mod 5K+
P: 9,186
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.
Sep 12 '09 #12

ashitpro
Expert 100+
P: 542
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.
Oct 12 '10 #13

P: 5
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...
Aug 26 '11 #14

weaknessforcats
Expert Mod 5K+
P: 9,186
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.
Aug 27 '11 #15

lspxy
P: 12
thanks,excellent work.
Jan 3 '12 #16

Ajay Bhalala
100+
P: 119
Wow..... It's really nice article.
I have get lot's of information from this article.
Jan 1 '15 #17

P: 8
what a nice article!!!! i'm just curious if to locate the value of the 1st member of the array, you would type distance[0], how about if i used in the 1st member will be distance[1] instead of zero, the program will be still run in c++?
May 18 '15 #18

weaknessforcats
Expert Mod 5K+
P: 9,186
Is your array named distance? If so, distance[0] is the value of the first element. distance[1] is the value of the second element.

Try it out. It will run in both C and C++.
May 19 '15 #19

P: 8
ah ok.. thanks..:D may god bless you.. :D
May 19 '15 #20

P: 22
It was excellent article i had lot of confusion in multidimensional arrays, it all got cleared. But it would have been great if explained that after passing the multidimensional array as argument to a function how do you access the elements of the array inside the function.
Oct 2 '15 #21

P: 56
Can you please explain how arr+1 and &arr+1 are converted to rvalues ?
Apr 4 '17 #22

weaknessforcats
Expert Mod 5K+
P: 9,186
Just remember that arr is the address of element 0. It doesn't matter if element 0 is another array. Now when you use pointer arithmetic, like arr + 1, you take the address of the array and increment it by the sizeof element 0, So arr + 1 is the address of element 1.

When you use &arr you are asking for the address of the array. arr is the address of element 0. By definition the address of an array is the address of element 0 so &arr and arr will be the same.
Apr 6 '17 #23

P: 56
Thanks a lot , I understood the concept now
Apr 6 '17 #24

P: 56
I have one confusion in below 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:

typedef int IntArray5[5];

IntArray5* funcB(int arg)
{
int (* temp)[5] = new int[arg][5];
return temp;
}


int main()
{
int (* arr)[5] = func(4);
}


You said that "you could define a type to be an element of your array (in the example this is an array of 5 int)"

Now here temp holds the address of the element 0 of array[4][5],which itself is an array of 5 elements , so when we return temp in the second case , how can we return a pointer to a type ?
for this scenario , we even must have the element of the array to be a pointer to an array of 5 int but the element of the array is an integer value .


Please explain this briefly .
Apr 6 '17 #25

weaknessforcats
Expert Mod 5K+
P: 9,186
First, a pointer is a variable that contains an address. So all we are doing is returning an address. Like int* or char*. That means int (* temp)[5] is just a pointer and it would contain the address of element 0 of the 5 element array of int it points to.

Second, what hurts here is that the function allocated an array of 5 int arrays based on arg but all the function returns is an address and not the number of elements. That means this technique will not work since the function must return the address of the arrays plus the number of elements.

One way out of this is to have funcB() allocate just the correct number of bytes and return the address of these bytes as a void*. Then the calling function can typecast the void* to a pointer to an array of 5 int. Since the calling function provided the number of bytes, it could also divide that number by the sizeof(int) * 5 to obtain the number of elements.

Microsoft COM got at this by using a struct called a VARIANT which contains the starting address, number of elements, and a thing called a discriminator which is used to determine the type of the elements in the array.

C++ uses library code called a vector which lets you generate code based on the contents of the array.

All this is by way of saying that array handling can be difficult. Knowing that, try to keep your designs simple to avoid complex issues like above.
Apr 7 '17 #26

P: 56
But using typedef , you already defined that you are returning the address of the array of 5 integer , so why to put extra * on the type name IntArray5 in second case , what do we achieve through this extra * here ?
Apr 7 '17 #27

weaknessforcats
Expert Mod 5K+
P: 9,186
You probably mean this:

Expand|Select|Wrap|Line Numbers
  1. typedef int IntArray5[5]; 
  2.  
  3. IntArray5* funcB(int arg) 
  4. int (* temp)[5] = new int[arg][5]; 
  5. return temp; 
  6.  
}

If funcB reurns IntArray5 you will get a compile error that your function is trying to return an array. You can only return a type or a pointer to a type. An array is neither of these so you can't return it. Never mind that IntArray5 is the address of element 0. Your compiler just does not care. So you add the asterisk to make the compiler happy.

The other thing you can do is change your typedef to::

Expand|Select|Wrap|Line Numbers
  1. typedef int (*IntArray5)[5];
Now you can return IntArray5.
Apr 8 '17 #28

P: 56
Now I got it , just last doubt , can you please explain this statement with an example :

There are no rvalues of array type in C. There are pointer lvalues and rvalues, integer lvalues and rvalues, structure lvalues and rvalues etc... But only lvalue arrays. When you try to convert an lvalue of array type to an rvalue, you no longer have an array, you have a pointer to the array's first member.

I am unable to get what is the meaning of rvalue of array type and lvalue of array type ?As far as I know , lvalue or rvalue corresponds to only expression so then how are we linking it to array type ?
Apr 8 '17 #29

weaknessforcats
Expert Mod 5K+
P: 9,186
This is from your code:

Expand|Select|Wrap|Line Numbers
  1. int(*temp)[5]= new int[arg][5];
temp is an lvalue. In this case a pointer to an array of 5 int. The rvalue is a new operator allocating arrays of 5 int based on arg.

You cannot tell from temp how many arrays of 5 int were allocated.

To know how many elements are in the array it must be an lvalue:

Expand|Select|Wrap|Line Numbers
  1. int temp[100][5];
Apr 9 '17 #30