In C, I know I can dynamically allocate a two-dimensional array on the heap using the following code:
int** someNumbers = malloc(arrayRows*sizeof(int*));
for (i = 0; i < arrayRows; i++) {
someNumbers[i] = malloc(arrayColumns*sizeof(int));
}
Clearly, this actually creates a one-dimensional array of pointers to a bunch of separate one-dimensional arrays of integers, and "The System" can figure out what I mean when I ask for:
someNumbers[4][2];
But when I statically declare a 2D array, as in the following line...:
int someNumbers[ARRAY_ROWS][ARRAY_COLUMNS];
...does a similar structure get created on the stack, or is it of another form completely? (i.e. is it a 1D array of pointers? If not, what is it, and how do references to it get figured out?)
Also, when I said, "The System," what is actually responsible for figuring that out? The kernel? Or does the C compiler sort it out while compiling?
A static two-dimensional array looks like an array of arrays - it's just laid out contiguously in memory. Arrays are not the same thing as pointers, but because you can often use them pretty much interchangeably it can get confusing sometimes. The compiler keeps track properly, though, which makes everything line up nicely. You do have to be careful with static 2D arrays like you mention, since if you try to pass one to a function taking an int ** parameter, bad things are going to happen. Here's a quick example:
int array1[3][2] = {{0, 1}, {2, 3}, {4, 5}};
In memory looks like this:
0 1 2 3 4 5
exactly the same as:
int array2[6] = { 0, 1, 2, 3, 4, 5 };
But if you try to pass array1 to this function:
void function1(int **a);
you'll get a warning (and the app will fail to access the array correctly):
warning: passing argument 1 of ‘function1’ from incompatible pointer type
Because a 2D array is not the same as int **. The automatic decaying of an array into a pointer only goes "one level deep" so to speak. You need to declare the function as:
void function2(int a[][2]);
or
void function2(int a[3][2]);
To make everything happy.
This same concept extends to n-dimensional arrays. Taking advantage of this kind of funny business in your application generally only makes it harder to understand, though. So be careful out there.
The answer is based on the idea that C doesn't really have 2D arrays - it has arrays-of-arrays. When you declare this:
int someNumbers[4][2];
You are asking for someNumbers to be an array of 4 elements, where each element of that array is of type int [2] (which is itself an array of 2 ints).
The other part of the puzzle is that arrays are always laid out contiguously in memory. If you ask for:
sometype_t array[4];
then that will always look like this:
| sometype_t | sometype_t | sometype_t | sometype_t |
(4 sometype_t objects laid out next to each other, with no spaces in between). So in your someNumbers array-of-arrays, it'll look like this:
| int [2] | int [2] | int [2] | int [2] |
And each int [2] element is itself an array, that looks like this:
| int | int |
So overall, you get this:
| int | int | int | int | int | int | int | int |
unsigned char MultiArray[5][2]={{0,1},{2,3},{4,5},{6,7},{8,9}};
in memory is equal to:
unsigned char SingleArray[10]={0,1,2,3,4,5,6,7,8,9};
In answer to your also: Both, though the compiler is doing most of the heavy lifting.
In the case of statically allocated arrays, "The System" will be the compiler. It will reserve the memory like it would for any stack variable.
In the case of the malloc'd array, "The System" will be the implementer of malloc (the kernel usually). All the compiler will allocate is the base pointer.
The compiler is always going to handle the type as what they are declared to be except in the example Carl gave where it can figure out interchangeable usage. This is why if you pass in a [][] to a function it must assume that it is a statically allocated flat, where ** is assumed to be pointer to pointer.
Suppose, we have a1 and a2 defined and initialized like below (c99):
int a1[2][2] = {{142,143}, {144,145}};
int **a2 = (int* []){ (int []){242,243}, (int []){244,245} };
a1 is a homogeneous 2D array with plain continuous layout in memory and expression (int*)a1 is evaluated to a pointer to its first element:
a1 --> 142 143 144 145
a2 is initialized from a heterogeneous 2D array and is a pointer to a value of type int*, i.e. dereference expression *a2 evaluates into a value of type int*, memory layout does not have to be continuous:
a2 --> p1 p2
...
p1 --> 242 243
...
p2 --> 244 245
Despite totally different memory layout and access semantics, C-language grammar for array-access expressions looks exactly the same for both homogeneous and heterogeneous 2D array:
expression a1[1][0] will fetch value 144 out of a1 array
expression a2[1][0] will fetch value 244 out of a2 array
Compiler knows that the access-expression for a1 operates on type int[2][2], when the access-expression for a2 operates on type int**. The generated assembly code will follow the homogeneous or heterogeneous access semantics.
The code usually crashes at run-time when array of type int[N][M] is type-casted and then accessed as type int**, for example:
((int**)a1)[1][0] //crash on dereference of a value of type 'int'
To access a particular 2D array consider the memory map for an array declaration as shown in code below:
0 1
a[0]0 1
a[1]2 3
To access each element, its sufficient to just pass which array you are interested in as parameters to the function. Then use offset for column to access each element individually.
int a[2][2] ={{0,1},{2,3}};
void f1(int *ptr);
void f1(int *ptr)
{
int a=0;
int b=0;
a=ptr[0];
b=ptr[1];
printf("%d\n",a);
printf("%d\n",b);
}
int main()
{
f1(a[0]);
f1(a[1]);
return 0;
}
Related
From what I remember arrays are always passed as pointers. For instance, the declaration:
void foo(int array[2][5]);
means for compiler exactly the same thing as:
void foo(int (*array)[5]);
You can say that these both forms are equivalent. Now, I wonder, why it is allowed then to declare it as:
void foo(int (*array)[]);
while not as:
void foo(int array[][]);
Take an example:
#include <stdio.h>
void foo(int (*p)[3]);
void bar(int (*p)[]);
int main(void)
{
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
foo(a);
bar(a);
return 0;
}
// The same as int p[][3] or int p[N][3] where N is a constant expression
void foo(int (*p)[3])
{
}
// Would it the same as int p[][] or int p[N][] (by analogy)?
void bar(int (*p)[])
{
}
It compiles fine and without warnings, but if I change bar's declaration to:
void bar(int p[][]);
then it's an error.
Why C allows such "obscure" way to pass an array?
Arrays are not pointers, if you declare an array of pointers with unspecified size it's ok because it will store the addresses stored in the poitners contigously and the size of each element is known, but p[][] would require the arrays to be contigous not their addresses and the size of the array is unknown so that's the problem.
To make it clear if you say int p[][] the you don't know how far is p[0] from p[1] whereas in int (*p)[] you know that the distance is the size of a pointer.
Arrays are converted to pointers but the are not pointers.
Arrays are usable as pointers, but they're more than pointers; they point to a number of things which have a size. That way, you can tell the compiler you're interested in the third or fifth element of your array, and it will calculate the location of that element by multiplying the size of one element by three or five to find an offset, and adding that offset to the address of the array.
C doesn't actually have multidimensional arrays. What it does have is arrays of arrays, which is pretty much the same thing. Say you have this:
int a[4][5];
In memory, this would look like this:
[0] |[1] |[2] |[3]
+-------------------|-------------------|-------------------|-------------------+
a | | | | | | | | | | | | | X | | | | | | | |
+-------------------|-------------------|-------------------|-------------------+
0 1 2 3 4 | 0 1 2 3 4 | 0 1 2 3 4 | 0 1 2 3 4 |
and you then try to access the element at index [2][2], then the system needs to perform the following calculation (conceptually):
Take the size of an int, and multiply that by five to get the size of the inner array
Take the size of that inner array, and multiply by two to get the offset of the second element in the outer array
Take the size of an int again, and multiply that by two to get the offset of the second element in the inner array
Add the two offsets; that's your memory address
In this case, the calculation is *(a + (2 * 5 * sizeof(int)) + (2 * sizeof(int))), giving *(a + 12*sizeof(int)), which is indeed the correct offset from the starting pointer.
All that means is that the size of the inner array needs to be defined in order for the compiler to be able to do that calculation. If you don't define the size of any dimension (but the leftmost one) of your multidimensional array, you don't have a defined size, and the compiler will balk.
You're tripping over the C language support for arrays of 'unspecified' size, which you can declare, but can't generally use directly unless you give it a more specific size somewhere.
In your example, if you actually try to do anything with the array in bar, you'll get an error:
void bar(int (*p)[])
{
printf("%d\n", p[1][1]);
}
% gcc -Wall t.c
t.c: In function ‘bar’:
t.c:23:5: error: invalid use of array with unspecified bounds
printf("%d\n", p[1][1]);
^
The only thing you can do with p in bar is cast it to some type with an explicit size, or pass it to some other function that takes a pointer to an array (of specified or unspecified size), and if you use the wrong explicit size to try access the array, you get undefined behavior with no warning.
I am trying to pass a two-dimensional char array to a function. I have found how to do it with a one-dimensional array, but I am having a hard time extrapolating that to how to deal with my current situation. See example code:
void foo( char ** bar ) {
}
int main () {
char mlady[100][100];
foo(mlady);
}
The compiler hints that the argument should be of type char (*)100, but I can't get that to compile. I also tried a lot of bruteforce without luck.
I am surprised I can't find this on google/SO, but I guess I am using the wrong keywords.
Change void foo( char ** bar ) to void foo( char bar[][100] ).
When you are passing a multidimensional array to a function, the first array dimension does not have to be specified. However the second(and subsequent) dimensions must be provided to the function.
For more information check this C-Faq.
When you define a multi-dimensional array, the bytes are laid out contiguously in memory.
For instance:
char a[3][5];
will give you 15 contiguous bytes of memory, laid out as so:
----------------------------------------------
| a + 0 | a + 1 | a + 2 | a + 3 | a + 4 |
----------------------------------------------
| a + 5 | a + 6 | a + 7 | a + 8 | a + 9 |
----------------------------------------------
| a + 10 | a + 11 | a + 12 | a + 13 | a + 14 |
----------------------------------------------
To reference a[x][y], the compiler will translate this to a + y + x * 5, in the same way that it translates indexing of a single element array from a[x] to a + x.
You'll see that in order for this translation to work for a two-dimensional array, you must know the rightmost dimension. In other words, the 5 in the definition char a[3][5] appears in the calculation a + y + x * 5, and must appear for that calculation to be able to work.
You don't need to know the leftmost dimension, because it doesn't appear in the calculation.
This is generally true of any multi-dimensional array. In order to calculate the offset of a specified index, you must know all of the dimensions except for the leftmost one. It doesn't hurt to have the leftmost one, but you don't need it.
Another way of saying all this is that "multi-dimensional arrays" in C are really just convenient ways of working with one-dimensional arrays. There's nothing char a[3][5] gives you that you can't accomplish with char a[15], and replacing all instances of a[x][y] with *(a + y + x * 5), it just makes the syntax slightly more convenient. Under the hood, these two ways are actually identical.
So, on the one hand, attempting to pass a two-dimensional array to a function as a char ** will not work, because you're not providing the rightmost dimension to the function, and the function needs it. void func(char (*bar)[5]) and void func(char bar[][5]) would be equivalent ways of doing that. In the first case, you need the parentheses around *bar because otherwise you'd have void func(char *bar[5]), where bar would be declared as a five element array of pointers to char, which is not what you have at all.
More fundamentally, passing it as a char ** won't work because a char ** is a pointer to a char pointer, and in a two-dimensional char array, you don't have any char pointers in there - just a bunch of plain chars. A pointer to the array will be passed to the function, but when you dereference it once, there are no more pointers to find, and to get from a char ** to a char you need to deference twice.
If you were to do something like (error checking omitted for brevity):
char ** list_strings = malloc(3 * sizeof *list_strings);
list_strings[0] = malloc(5);
list_strings[1] = malloc(5);
list_strings[2] = malloc(5);
then you could pass list_strings as a char ** because it actually is one, and not a true multi-dimensional array - there are two levels of indirection in there, once to get to the individual strings, and then again to get to an individual character in one of those strings. What you'd be passing to your function would be a single-dimensional (dynamically allocated) array of char *. The fact that your function could then go on to deference any of those members again does not make it a multi-dimensional array.
When you pass a char ** in this way, you technically don't need to know any of the dimensions, at least not to access any particular element. You do, of course, need to know the dimensions to avoid the function dropping off the end of any of them, unless you hardcode the dimensions of the array into your function and only ever pass it arrays of that size, which is generally not a good way to write programs.
On 6th line instead of multiArray[0], when I write multiArray, program still works. Don't understand why. I was thinking before that multiArray is a pointer to multiArray[0] which is a pointer to multiArray[0][0]. So multiArray alone is a pointer to a pointer. multiArray[0] is a pointer to a 4 element int array. So it seems that multiArray and multiArray[0] must be different. But in below code, both work. Print function I wrote expects a pointer to a 4 element int array. So only multiArray[0] must work and multiArray must not work. But both works. Didn't understand that.
#include <stdio.h>
void printArr(int(*ptr)[4]);
int i, k;
int main(void){
int multiArray[3][4] = { { 1, 5, 2, 4 }, { 0, 6, 3, 14 }, { 132, 4, 22, 5 } };
int(*point)[4] = multiArray[0];
for (k = 0; k < 3; k++)
{
printArr(point++);
}
getchar();
}
void printArr(int(*ptr)[4]){
int *temp = (int *)ptr;
for (i = 0; i < 4; i++)
{
printf("%d ", *temp);
temp++;
}
puts("\n");
}
Someone else wrote "Multi-dimensional arrays are syntactic sugar for 1-D arrays".
This is sort of like saying that int is just syntactic sugar for a unsigned char[4] . You could do away with expressions like 4 + 5 and get the same result by manipulating arrays of 4 bytes.
You could even say that C is just syntactic sugar for a Universal Turing Machine script, if you want to take this concept a bit further.
The reality is that multi-dimensional arrays are a part of the type system in C, and they have syntax associated with them. There's more than one way to skin a cat.
Moving on, the way C arranges what we are calling a multi-dimension array is to say: "Arrays can only have one dimension, but the element type may itself be another array". We say "multi-dimension array" as a matter of convenience, but the syntax and the type system actually reflect the one-dimensional nature of the array.
So, int multiArray[3][4] is an array of 3 elements. Each of those elements is an array of 4 ints.
In memory, an array's elements are stored contiguously -- regardless of what the element type is. So, the memory layout is an array of 4 int, immediately followed by another array of 4 int, and finally another array of 4 int.
There are 12 contiguous int in memory, and in the C type system they are grouped up into 3 groups of 4.
You will note that the first int of the 12 is also the first int of the first group of 4. This is why we find that if we ask "What is the memory location of the first int?", "What is the memory location of the first group of 4 ints?", and "What is the memory location of the entire bloc of 12 ints?", we get the same answer every time. (In C, the memory location of a multi-byte object is considered to start at the location of its first byte).
Now, to talk about the pointer syntax and representation. In C, a pointer tells you where in memory an object can be found. There are two aspects to this: the memory location of the object, and what type of object it is. (The size of the object is a corollary of the type).
Some presentations only focus on the first of those, they will say things like "A pointer is just a number". But that is forgetting about the type information, which is a crucial part of a pointer.
When you print the pointer with %p, you lose the type information. You're just putting out the location in memory of the first byte. So they all look the same, despite the fact that the three pointers are pointing at differently-sized objects (which overlap each other like matruskha dolls).
In most implementations of C, the type information is all computed at compile-time, so if you try to understand C by comparing source code with assembly code (some people do this), you only see the memory-location part of the pointer. This can lead to misunderstanding if you forget that the type information is also crucial.
Footnote: All of this is independent of a couple of syntax quirks that C has; which have caused a lot of confusion over the years (but are also useful sometimes). The expression x is a shortcut for &x[0] if x is an array, except when used as the operand of & or sizeof. (Otherwise this would be a recursive definition!). The second quirk is that if you write what looks like an array declarator in a function formal parameter list, it is actually as if you wrote a pointer declarator. I stress again that these are just syntax oddities, they are not saying anything fundamental about the nature of arrays and pointers, which is actually not that complicated. The language would work just as well without both of these quirks.
Multidiemensional arrays var_t arr[size_y][size_x] provide means of declaring and accessing array elements (memory) in a conveniant manner. But all multidiemensional arrays are internally continuous memory blocks.
You may say that arr[y][x] = arr[y*cols+x].
In terms of pointer-level, the pointers multiArray and multiArray[0] are the same, they're int* - though the formal type for arr will be int (*)[2]. Using that type will allow one to take advantage of all pointer mechanics (++ on such pointer will move the address by 8 bytes, not 4).
Try this:
void t1(int* param)
{
printf("t1: %d\n", *param);
}
void t2(int** param)
{
printf("t2: %d\n", **param);
}
int main(void) {
int arr[2][2] = { { 1, 2 } , { 3, 4 } };
t1(arr); // works ok
t1(arr[0]); // works ok
t2(arr); // seg fault
t2(arr[0]);
}
int(*point)[4] = multiArray[0];
This works because both multiArray[0] and multiArray point to same address, the address of first element of array: multiArray[0][0].
However in this case, you may get a warning from compiler because type of multiArray[0] is int* while of point is int [4]*(pointer to array of 4 integers).
Why can't my parameter be
void example(int Array[][]){ /*statements*/}
Why do I need to specify the column size of the array? Say for example, 3
void example(int Array[][3]){/*statements*/}
My professor said its mandatory, but I was coding before school started and I remembered that there was no syntactical or semantic error when I made this my parameter? Or did I miss something?
When it comes to describing parameters, arrays always decay into pointers to their first element.
When you pass an array declared as int Array[3] to the function void foo(int array[]), it decays into a pointer to the beginning of the array i.e. int *Array;. Btw, you can describe a parameter as int array[3] or int array[6] or even int *array - all these will be equivalent and you can pass any integer array without problems.
In case of arrays of arrays (2D arrays), it decays to a pointer to its first element as well, which happens to be a single dimensional array i.e. we get int (*Array)[3].
Specifying the size here is important. If it were not mandatory, there won't be any way for compiler to know how to deal with expression Array[2][1], for example.
To dereference that a compiler needs to compute the offset of the item we need in a contiguous block of memory (int Array[2][3] is a contiguous block of integers), which should be easy for pointers. If a is a pointer, then a[N] is expanded as start_address_in_a + N * size_of_item_being_pointed_by_a. In case of expression Array[2][1] inside a function (we want to access this element) the Array is a pointer to a single dimensional array and the same formula applies. The number of bytes in the last square bracket is required to find size_of_item_being_pointed_by_a. If we had just Array[][] it would be impossible to find it out and hence impossible to dereference an array element we need.
Without the size, pointers arithmetics wouldn't work for arrays of arrays. What address would Array + 2 produce: advance the address in Array 2 bytes ahead (wrong) or advance the pointer 3* sizeof(int) * 2 bytes ahead?
In C/C++, even 2-D arrays are stored sequentially, one row after another in memory. So, when you have (in a single function):
int a[5][3];
int *head;
head = &a[0][0];
a[2][1] = 2; // <--
The element you are actually accessing with a[2][1] is *(head + 2*3 + 1), cause sequentially, that element is after 3 elements of the 0 row, and 3 elements of the 1 row, and then one more index further.
If you declare a function like:
void some_function(int array[][]) {...}
syntactically, it should not be an error. But, when you try to access array[2][3] now, you can't tell which element is supposed to be accessed. On the other hand, when you have:
void some_function(int array[][5]) {...}
you know that with array[2][3], it can be determined that you are actually accessing element at the memory address *(&array[0][0] + 2*5 + 3) because the function knows the size of the second dimension.
There is one other option, as previously suggested, you can declare a function like:
void some_function(int *array, int cols) { ... }
because this way, you are calling the function with the same "information" as before -- the number of columns. You access the array elements a bit differently then: you have to write *(array + i*cols + j) where you would usually write array[i][j], cause array is now a pointer to integer (not to a pointer).
When you declare a function like this, you have to be careful to call it with the number of columns that are actually declared for the array, not only used. So, for example:
int main(){
int a[5][5];
int i, j;
for (i = 0; i < 3; ++i){
for (int j=0; j < 3; ++j){
scanf("%d", &a[i][j]);
}
}
some_function(&a[i][j], 5); // <- correct
some_function(&a[i][j], 3); // <- wrong
return 0;
}
C 2018 6.7.6.2 specifies the semantics of array declarators, and paragraph 1 gives constraints for them, including:
The element type shall not be an incomplete or function type.
In a function declaration such as void example(int Array[][]), Array[] is an array declarator. So it must satisfy the constraint that its element type must not be incomplete. Its element type in that declaration is int [], which is incomplete since the size is not specified.
There is no fundamental reason the C standard could not remove that constraint for parameters that are about to be adjusted to pointers. The resulting type int (*Array)[] is a legal declaration, is accepted by compilers, and can be used in the form (*Array)[j].
However, the declaration int Array[][] suggests that Array is at least associated with a two-dimensional array, and hence is to be used in the form Array[i][j]. Even if the declaration int Array[][] were accepted and were adjusted to int (*Array)[], using it as Array[i][j] would not be possible because the subscript operator requires that its pointer operand be a pointer to a complete type, and this requirement is not avoidable as it is needed to calculate the address of the element. Thus, keeping the constraint on the array declarator makes sense, as it is consistent with the intended expression that the argument will be a two-dimensional array, not just a pointer to one one-dimensional array.
Actually whether it is a 2d array or a 1d array, it is stored in the memory in a single line.So to say the compiler where should it break the row indicating the next numbers to be in the next rows we are supposed to provide the column size. And breaking the rows appropriately will give the size of the rows.
Let's see an example:
int a[][3]={ 1,2,3,4,5,6,7,8,9,0 };
This array a is stored in the memory as:
1 2 3 4 5 6 7 8 9 0
But since we have specified the column size as 3 the memory splits after every 3 numbers.
#include<stdio.h>
int main() {
int a[][3]={1,2,3,4,5,6},i,j;
for(i=0;i<2;i++)
{
for(j=0;j<3;j++)
{
printf("%d ",a[i][j]);
}
printf("\n");
}
}
OUTPUT:
1 2 3
4 5 6
In the other case,
int a[3][]={1,2,3,4,5,6,7,8,9,0};
The compiler only knows that there are 3 rows but it doesn't know the number of elements in each row so it cannot allocate memory and will show an error.
#include<stdio.h>
int main() {
int a[3][]={1,2,3,4,5,6},i,j;
for(i=0;i<3;i++)
{
for(j=0;j<2;j++)
{
printf("%d ",a[i][j]);
}
printf("\n");
}
}
OUTPUT:
c: In function 'main':
c:4:8: error: array type has incomplete element type 'int[]'
int a[3][]={1,2,3,4,5,6},i,j;
^
As we know, we can pass a variable as an argument(s) in a function. Similarly, we can pass two-dimensional arrays in C++.
C++ does not allow us to pass an entire array as an argument to a function. However, we can pass a pointer to an array by specifying the array's name without an index.
We can pass a 2D array to a function by specifying the size of the columns of a 2D array. One of the important things to remember here is that the size of rows is optional but the size of the column should not be left empty else the compiler will show an error. A 2D array is stored in the memory in a single line. So, to say the compiler where should it break the row indicating the following numbers to be in the next rows we are supposed to provide the column size. And breaking the rows appropriately will automatically give the size of the rows.
source: https://www.scaler.com/topics/two-dimensional-array-in-cpp/
There is a similar post regarding this. You can refer below link.
Creating Array in C and passing pointer to said array to function
Hope it helps.
On the other hand, compiler needs to the second dimension so that it can move "Array" from one pointer to next since the whole memory is arranged in a linear fashion
I thought this was a cool approach. If you take this as the formula to calculate the address of an element in the array:
a[i][j] = baseArrayAddress + (i + (colSize + elementSize)) + (j * (elementSize))
Then you can see that the only thing the compiler needs to know (which it can't otherwise infer) is the size of the column, thus you need to provide it as the programmer so the algorithm can run to calculate the offset.
The row number only acts as a multiplier and is provided by the programmer when trying to dereference an array location.
When you create a 2D array, anytype a[3][4], in memory what you actually create is 3 contiguous blocks of 4 anytype objects.
a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3]
Now the next question is, why is that so? Because, keeping with the spec and structure of the language, anytype a[3][4] actually expands out into anytype (*a)[4], because arrays decay into pointers. And in fact that also expands out into anytype (*(*a)), however, you've now completely lost the size of the 2D array. So, you must help the compiler out a bit.
If you ask the program for a[2], the program can follow the exact same steps that it does for 1D arrays. It simply can return the 3rd element of sizeof(object pointed to), the object pointed to here is of size 4 anytype objects.
I can declare:
int (*ap)[N];
So ap is pointer to int array of size N. Why is this ever useful? If I pass it to function, what useful thing it can do with it that it could not do with just a normal pointer to the array's contents?
C FAQ say:
2.12: How do I declare a pointer to an array?
Usually, you don't want to.
A pointer to an array can be used to dynamically allocate a multi-dimensional array N, where N-1 dimensions are known. Below creates a Nx3 array.
int (*ap)[3];
ap = malloc(N * sizeof(*ap));
/* can now access ap[0][0] - ap[N-1][2] */
#Adam E/Cruachan, This is not the same thing as a pointer to a pointer. ap is a single pointer to a block of memory containing three consecutive integers. ap++ will advance the pointer address to the next block of three integers. for int **pp;, pp points to an integer pointer, each of which can point to an integer anywhere in memory.
+-----+ +------+ +-----+
ap ---> | int | vs. pp ---> | int* | -> | int |
| int | +------+ +-----+
| int | pp+1 -> | int* | -\
+-----+ +------+ \ +-----+
ap+1 -> | int | : : -> | int |
| int | +-----+
| int |
+-----+
: :
If you increment the pointer, it will then point to the start of the next group of N elements.
This is not a big deal and it's use is up to the developer.
Generally, the only time you'll see a pointer to an array (T (*a)[N]) is as a function parameter, where a is meant to be a 2d array:
void foo(int (*a)[N], size_t count)
{
size_t i;
for (i = 0; i < count; i++)
a[i][j] = ...;
...
}
void bar(void)
{
int arr[M][N];
foo(arr, M);
}
Note that for a function parameter declaration, int a[][N] is equivalent to int (*a)[N], but this is only true for function parameter declarations:
void foo (int a[][N], size_t count) {...}
Pointers to arrays are generally not as useful as pointers to the base type, since you need to know the array size to properly declare a pointer to it (a pointer to a 10-element array of int is a different type from a pointer to a 20-element array of int). Personally, I haven't found much use for them in 20-some-odd years of programming.
Remember that in most contexts, an array expression (such as arr above) will have its type implicitly converted from "N-element array of T" to "pointer to T" (except when the array expression is an operand of sizeof or &, or the array is a string literal being used as an initializer in a declaration). In this case, the type of arr in the call to foo is implicitly converted from "M-element array of N-element array of int" to "pointer to N-element array of int".
Given the declaration T a[M][N], all of the following expressions will evaluate to the same location (the address of the first element in the array), but the types will be different as shown below:
Expression Type Implicitly converted to
---------- ---- -----------------------
a T [M][N] T (*)[N]
a[0] T [N] T *
&a T (*)[M][N]
&a[0] T (*)[N]
&a[0][0] T *
It's not useful, really. But sometimes pointers to arrays are used e.g. in Microsoft Windows API - I've seen a lot of this stuff there.
There are situations where you want to pass the memory location across programs. e.g a windows API might expect you to pass a pointer to data structure or an array where as you are programming in some other language, say c#. Windows API does not care how the target language handles array's, for windows API it is just a stream of bytes in memory and it will fill it, send it back to you. to avoid cross language type missmatch in some cases we use pointer to array rather than implicit array name as pointer. More over it is not guaranteed that the implicit array name is a long pointer, some compilers might optimize it to be a relative value withing segment. a pointer to an array guarantees that it is of the order of machine register size and you can point to a location anywhere in the available RAM.
This will probably careen off into subjective/argumentative, but...
In my opinion, pointers to arrays are in the language because they fell into the language. There are pointers to every other declarable data type, so these are here too. I've never seen anyone get really useful work out of them. Hypothetically, they allow a prototype that demands an array and not a pointer to the element, but ...
It seems pretty useless to me to do a pointer to an array. In C, an array is already a pointer to a block of that data type.
int (*ap)[N];
int **ipp;
are both the same data type (a pointer to a pointer to an Integer). The only difference there is that there is space for N integers allotted for ap.
There's no need to pass an array by a pointer to a function, like, for the purpose of changing the contents of the array within that function because it's already a pointer. As a general rule, I'd say it's unnecessary and just creates an additional need to dereference the pointer to get at the data in the array. But, I'm sure there's a program or algorithm somewhere that could find a legitimate use for it.
Following code is a part of my article : Pointers and Arrays in C C++
You can check it out # http://pointersandarrays.blogspot.com/
Pointers and 2D Arrays
Following code snippet illustrates how to declare and access a 2D array. Underneath 2D Array lies a single dimensional array. You will get be sure of this after playing with the following piece of code.
Code Snippet #4
#include<iostream&rt;
using namespace std;
int main()
{
cout<< "Understanding Pointers and 2 D Arrays"<<endl;
cout<< "----------------------\n"<<endl;
//Declaration of a 2D Array.
int tab[3][5];
//Total space required : 3*5 * sizeof(int) = 15 * sizeof(int)
//Funda : Since the amount of required space is known by compiler, contiguous 15 memory cells are allocated here.
//Hence the case is similar to a 1 D Array of size 15. Lets try this out!
//Array initialization using array name
for(int i=0; i<3;i++)
for(int j=0; j<5;j++)
tab[i][j]=i+2*j;
//Print array using array name
cout << "\nPrint array using array name ..."<<endl;
for(int i=0; i<3;i++)
{
for(int j=0; j<5;j++)
printf("%2d ",tab[i][j] );
printf("\n");
}
//Print array using a pointer. Proof of 1 D array being allocated
cout << "\nPrint array using a pointer. Proof of 1 D array being allocated ..." << endl;
int *tptr;
tptr = &tab[0][0]; // pointer tptr points at first element of the array.
for(int i=0; i<15;i++)
printf("%d ",*(tptr+i) );
tptr = &tab[0][0];
cout << "\nNotice that array is printed row by row in a linear fashion."<<endl;
return 0;
}
Output #4:
Understanding Pointers and 2D Arrays
Print array using array name ...
0 2 4 6 8
1 3 5 7 9
2 4 6 8 10
Print array using a pointer. Proof of 1 D array being allocated ...
0 2 4 6 8 1 3 5 7 9 2 4 6 8 10
Notice that array is printed row by row in a linear fashion.
I think the conclusion from this whole discussion is never. Nobody here really demonstrated a use for this construct where something else wouldn't work in a more comprehensible way.