Pointers and Multi dimensional arrays [duplicate] - c++

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

Passing a 2D Array to function in C++ [duplicate]

This question already has answers here:
Passing a 2D array to a C++ function
(18 answers)
How do I use arrays in C++?
(5 answers)
What is array to pointer decay?
(11 answers)
Closed 1 year ago.
I tried passing a 2D array to a function in C++ and I didn`t understand something:
//My array declaration:
int array[5][5]={{....}}; //I declared and filled the array with values
//My different function tries:
void passArr(int **array) //ERROR
void passArr(int *array[5]) //ERROR
void passArr(int *(*array)) //ERROR
void passArr(int (*(*array))) //ERROR
void passArr(int (*array)[5]) //WORKS
All errors were: [Error] cannot convert 'int (*)[5]' to 'int**' for argument '1' to 'void passArr(int**)'
Why do the errors show up and most importantly what is the difference between int *array[5] and int (*array)[5] when passing as an argument?
If int (*array)[5] worked, then why didn´t int *(*array) or int (*(*array))) also work?
In C and C++, a 2D array is an Array-of-Arrays. The first element of a 2D array is the first 1D array it contains. The conversion is specified in C++ Standard - Array-to-pointer Conversion [conv.array]
When you declared a plain-old 2D array as:
int array[5][5]
You have an array of int[5] arrays. As with any array, it is converted to a pointer to its first element on access. The first element being an array of int[5], so the resulting type is:
int (*)[5]
A pointer-to-array-of int[5]. So for your 2D array named array, that is:
int (*array)[5]
They're just different things. Reading from right to left might be helpful.
int array[5][5] -> array of array of int, could decay to pointer to array of int
int (*array)[5] -> pointer to array of int, could be converted from the above
int **array -> pointer to pointer to int
int *(*array) -> same as above
int (*(*array)) -> same as above
int *array[5] -> array of pointer to int
Also, in the "C++" language, it is strongly advisable to use one of the very-many "container classes" for this purpose. Or perhaps a class of your own devising.
"A simple pointer" has no way to convey that it is pointing to "a 2D array," nor what its dimensions are. The extent of its job-description is that it knows how to point to "a bunch of bytes, or nothing."
But if instead you provided your caller with a reference to a class, your caller could now "ask the class" for what it needed, "and the class would magically know." Just hand the class (X,Y) and you either get the right answer or a runtime exception is thrown. Every single time ... and by the way, efficiently.
This is a much more-robust way to write code, and it really is the reason why the C++ language was invented.
Versions 1,3, and 4 are all the same. Version 2 is also the same as 1,3, and 4, because when passing an array to a function the array decays to a pointer.
Version 2 can also be written
void passArr(int* array[5])
Which probably shows the association of the asterisk with the type more clearly: it is an array of five int-pointers. With the array decaying to a pointer, it becomes
void passArr(int** array)
All of them mean that the entry of the array are addresses, pointing to another array:
[ * | * | * | * | * ]
| | | | +-> [,,,,]
| | | +-> [,,,,]
And so on.
What you have is an array when each entry is an array of 5 ints, not a pointer to one (arrays bear a lot of resemblance with pointers, but they aren't pointers!)
Your matrix looks like this:
[ [1,2,3,4,5], [6,,,,], [,,,,], [,,,,], [,,,,] ]
So, array is a pointer to an int[5], not a pointer to pointer to int. And that is what the parentheses do in version 5: int (*array)[5]. This binds the asterisk closer to array.

Why can't I initialize a two dimensional array with a pointer to a pointer to an int? [duplicate]

I need a pointer to a static 2-dimensional array. How is this done?
static uint8_t l_matrix[10][20];
void test(){
uint8_t **matrix_ptr = l_matrix; //wrong idea
}
I get all kinds of errors like:
warning: assignment from incompatible pointer type
subscripted value is neither array nor pointer
error: invalid use of flexible array member
Here you wanna make a pointer to the first element of the array
uint8_t (*matrix_ptr)[20] = l_matrix;
With typedef, this looks cleaner
typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;
Then you can enjoy life again :)
matrix_ptr[0][1] = ...;
Beware of the pointer/array world in C, much confusion is around this.
Edit
Reviewing some of the other answers here, because the comment fields are too short to do there. Multiple alternatives were proposed, but it wasn't shown how they behave. Here is how they do
uint8_t (*matrix_ptr)[][20] = l_matrix;
If you fix the error and add the address-of operator & like in the following snippet
uint8_t (*matrix_ptr)[][20] = &l_matrix;
Then that one creates a pointer to an incomplete array type of elements of type array of 20 uint8_t. Because the pointer is to an array of arrays, you have to access it with
(*matrix_ptr)[0][1] = ...;
And because it's a pointer to an incomplete array, you cannot do as a shortcut
matrix_ptr[0][0][1] = ...;
Because indexing requires the element type's size to be known (indexing implies an addition of an integer to the pointer, so it won't work with incomplete types). Note that this only works in C, because T[] and T[N] are compatible types. C++ does not have a concept of compatible types, and so it will reject that code, because T[] and T[10] are different types.
The following alternative doesn't work at all, because the element type of the array, when you view it as a one-dimensional array, is not uint8_t, but uint8_t[20]
uint8_t *matrix_ptr = l_matrix; // fail
The following is a good alternative
uint8_t (*matrix_ptr)[10][20] = &l_matrix;
You access it with
(*matrix_ptr)[0][1] = ...;
matrix_ptr[0][0][1] = ...; // also possible now
It has the benefit that it preserves the outer dimension's size. So you can apply sizeof on it
sizeof (*matrix_ptr) == sizeof(uint8_t) * 10 * 20
There is one other answer that makes use of the fact that items in an array are contiguously stored
uint8_t *matrix_ptr = l_matrix[0];
Now, that formally only allows you to access the elements of the first element of the two dimensional array. That is, the following condition hold
matrix_ptr[0] = ...; // valid
matrix_ptr[19] = ...; // valid
matrix_ptr[20] = ...; // undefined behavior
matrix_ptr[10*20-1] = ...; // undefined behavior
You will notice it probably works up to 10*20-1, but if you throw on alias analysis and other aggressive optimizations, some compiler could make an assumption that may break that code. Having said that, i've never encountered a compiler that fails on it (but then again, i've not used that technique in real code), and even the C FAQ has that technique contained (with a warning about its UB'ness), and if you cannot change the array type, this is a last option to save you :)
To fully understand this, you must grasp the following concepts:
Arrays are not pointers!
First of all (And it's been preached enough), arrays are not pointers. Instead, in most uses, they 'decay' to the address to their first element, which can be assigned to a pointer:
int a[] = {1, 2, 3};
int *p = a; // p now points to a[0]
I assume it works this way so that the array's contents can be accessed without copying all of them. That's just a behavior of array types and is not meant to imply that they are same thing.
Multidimensional arrays
Multidimensional arrays are just a way to 'partition' memory in a way that the compiler/machine can understand and operate on.
For instance, int a[4][3][5] = an array containing 4*3*5 (60) 'chunks' of integer-sized memory.
The advantage over using int a[4][3][5] vs plain int b[60] is that they're now 'partitioned' (Easier to work with their 'chunks', if needed), and the program can now perform bound checking.
In fact, int a[4][3][5] is stored exactly like int b[60] in memory - The only difference is that the program now manages it as if they're separate entities of certain sizes (Specifically, four groups of three groups of five).
Keep in mind: Both int a[4][3][5] and int b[60] are the same in memory, and the only difference is how they're handled by the application/compiler
{
{1, 2, 3, 4, 5}
{6, 7, 8, 9, 10}
{11, 12, 13, 14, 15}
}
{
{16, 17, 18, 19, 20}
{21, 22, 23, 24, 25}
{26, 27, 28, 29, 30}
}
{
{31, 32, 33, 34, 35}
{36, 37, 38, 39, 40}
{41, 42, 43, 44, 45}
}
{
{46, 47, 48, 49, 50}
{51, 52, 53, 54, 55}
{56, 57, 58, 59, 60}
}
From this, you can clearly see that each "partition" is just an array that the program keeps track of.
Syntax
Now, arrays are syntactically different from pointers. Specifically, this means the compiler/machine will treat them differently. This may seem like a no brainer, but take a look at this:
int a[3][3];
printf("%p %p", a, a[0]);
The above example prints the same memory address twice, like this:
0x7eb5a3b4 0x7eb5a3b4
However, only one can be assigned to a pointer so directly:
int *p1 = a[0]; // RIGHT !
int *p2 = a; // WRONG !
Why can't a be assigned to a pointer but a[0] can?
This, simply, is a consequence of multidimensional arrays, and I'll explain why:
At the level of 'a', we still see that we have another 'dimension' to look forward to. At the level of 'a[0]', however, we're already in the top dimension, so as far as the program is concerned we're just looking at a normal array.
You may be asking:
Why does it matter if the array is multidimensional in regards to making a pointer for it?
It's best to think this way:
A 'decay' from a multidimensional array is not just an address, but an address with partition data (AKA it still understands that its underlying data is made of other arrays), which consists of boundaries set by the array beyond the first dimension.
This 'partition' logic cannot exist within a pointer unless we specify it:
int a[4][5][95][8];
int (*p)[5][95][8];
p = a; // p = *a[0] // p = a+0
Otherwise, the meaning of the array's sorting properties are lost.
Also note the use of parenthesis around *p: int (*p)[5][95][8] - That's to specify that we're making a pointer with these bounds, not an array of pointers with these bounds: int *p[5][95][8]
Conclusion
Let's review:
Arrays decay to addresses if they have no other purpose in the used context
Multidimensional arrays are just arrays of arrays - Hence, the 'decayed' address will carry the burden of "I have sub dimensions"
Dimension data cannot exist in a pointer unless you give it to it.
In brief: multidimensional arrays decay to addresses that carry the ability to understand their contents.
In
int *ptr= l_matrix[0];
you can access like
*p
*(p+1)
*(p+2)
after all 2 dimensional arrays are also stored as 1-d.
G'day,
The declaration
static uint8_t l_matrix[10][20];
has set aside storage for 10 rows of 20 unit8_t locations, i.e. 200 uint8_t sized locations,
with each element being found by calculating 20 x row + column.
So doesn't
uint8_t (*matrix_ptr)[20] = l_matrix;
give you what you need and point to the column zero element of the first row of the array?
Edit: Thinking about this a bit further, isn't an array name, by definition, a pointer? That is, the name of an array is a synonym for the location of the first element, i.e. l_matrix[0][0]?
Edit2: As mentioned by others, the comment space is a bit too small for further discussion. Anyway:
typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;
does not provide any allocation of storage for the array in question.
As mentioned above, and as defined by the standard, the statement:
static uint8_t l_matrix[10][20];
has set aside 200 sequential locations of type uint8_t.
Referring to l_matrix using statements of the form:
(*l_matrix + (20 * rowno) + colno)
will give you the contents of the colno'th element found in row rowno.
All pointer manipulations automatically take into account the size of the object pointed to. - K&R Section 5.4, p.103
This is also the case if any padding or byte alignment shifting is involved in the storage of the object at hand. The compiler will automatically adjust for these. By definition of the C ANSI standard.
HTH
cheers,
In C99 (supported by clang and gcc) there's an obscure syntax for passing multi-dimensional arrays to functions by reference:
int l_matrix[10][20];
void test(int matrix_ptr[static 10][20]) {
}
int main(void) {
test(l_matrix);
}
Unlike a plain pointer, this hints about array size, theoretically allowing compiler to warn about passing too-small array and spot obvious out of bounds access.
Sadly, it doesn't fix sizeof() and compilers don't seem to use that information yet, so it remains a curiosity.
You can always avoid fiddling around with the compiler by declaring the array as linear and doing the (row,col) to array index calculation by yourself.
static uint8_t l_matrix[200];
void test(int row, int col, uint8_t val)
{
uint8_t* matrix_ptr = l_matrix;
matrix_ptr [col+y*row] = val; // to assign a value
}
this is what the compiler would have done anyway.
The basic syntax of initializing pointer that points to multidimentional array is
type (*pointer)[1st dimension size][2nd dimension size][..] = &array_name
The the basic syntax for calling it is
(*pointer_name)[1st index][2nd index][...]
Here is a example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// The multidimentional array...
char balance[5][100] = {
"Subham",
"Messi"
};
char (*p)[5][100] = &balance; // Pointer initialization...
printf("%s\n",(*p)[0]); // Calling...
printf("%s\n",(*p)[1]); // Calling...
return 0;
}
Output is:
Subham
Messi
It worked...
You can do it like this:
uint8_t (*matrix_ptr)[10][20] = &l_matrix;
You want a pointer to the first element, so;
static uint8_t l_matrix[10][20];
void test(){
uint8_t *matrix_ptr = l_matrix[0]; //wrong idea
}
You could also add an offset if you want to use negative indexes:
uint8_t l_matrix[10][20];
uint8_t (*matrix_ptr)[20] = l_matrix+5;
matrix_ptr[-4][1]=7;
If your compiler gives an error or warning you could use:
uint8_t (*matrix_ptr)[20] = (uint8_t (*)[20]) l_matrix;

Why do a[] and *a behave differently?

I thought a[] and *a are the same thing because they work like pointers. Yet I encountered something unexpected in the following code:
#include <iostream>
using namespace std;
class F {
public:
bool a[];
F();
};
F::F() {
*a = new bool[5];
a[2] = true;
}
int main() {
F obj;
if(obj.a[2])
cout << '?';
return 0;
}
This code prints ?, but I don't understand how it works. When:
*a = new bool[5];
is changed into:
a = new bool[5];
compiler reports:
F:\main.cpp|11|error: incompatible types in assignment of 'bool*' to 'bool [0]'|
I found this behaviour weird, so I was playing around with this code. When I changed the type of a from bool to int compiler always reports an error
F:\main.cpp|11|error: invalid conversion from 'int*' to 'int' [-fpermissive]|
Why does this work the way it does?
I thought a[] and *a are the same thing because they work like pointers.
Let's talk about this piece of declaration:
int a[4] = { 1, 2, 3, 5 };
int *b = NULL;
This is how they land in memory in the executable:
+----+----+----+----+
a: | 1 | 2 | 3 | 5 | <-- 4 integers
+----+----+----+----+
+----------+
b: | NULL | <-- 1 pointer that points to nowhere
+----------+
As you can see, they are not the same thing.
What happens after b = new int[4];?
The new memory layout of b is something like this:
+----------+
b: | 0xacf3de | <-- 1 pointer that points to a block of 4 integers
+----------+
Somewhere else in memory (at address 0xacf3de)...
+----+----+----+----+
0xacf3de: | ? | ? | ? | ? | <-- 4 integers
+----+----+----+----+
But wait, somebody told me that arrays work like pointers...
No, that's not true. The arrays do not work like pointers.
An array name (a, f.e.) can be used as a shortcut for its address in memory (which is, in fact, the address of its first element). The following notations are equivalent:
a
&a
&a[0]
The value of all of them is the address in memory where the first element of a (the 1 in the example above) is stored.
a[0] is an int, &a[0] is an int *. This is the complete form. The other two are shortcuts that the language provides in order to make the code easier to read and understand.
The things are different for pointers.
b is the value stored in the b variable (NULL or 0xacf3de in the example above. It is a value of type int * -- the address in memory where an int is stored. &b is the address in memory where (the value of) b is stored. Its type is int ** -- a pointer to a pointer to an int; or "the address in memory where is stored the address of an int".
But wait, they can be replaced one for another in some contexts
Up to some point, yes, a pointer and an array are interchangeable. As you can see above, there is a common data type involved: int *. It is the type of b (which stores the address of an int) and also the type of &a[0] (which is also the address of an int).
This means that where b can be used, a (a short of &a[0]) can be used instead, and vice-versa.
Also, where *b can be used, *a can be used instead. It is the short for *&a[0] and it means "the value (*) stored at the address (&) of a[0]" and it is, in fact, the same as a[0].
But, where &b can be used, &a cannot be used instead; remember that &a is the same as &a[0] and its type is int * (but the type of &b is int **).
The line:
*a = new bool[5];
is equivalent to:
a[0] = new bool[5];
You are not initialiazing your array, but allocating an array of bool which is then implicitly converted into bool to be assigned to a[0]: the value should be true since the pointer returned by new should be different from 0. This implicit conversion does not apply with ints: that is why you are getting an error when changing the type of a.
Now, considering this line:
a = new bool[5];
Here you are trying to assign your dynamically allocated array to a, in other words assigning a pointer to an array bool* to a static array bool[0]: as the compiler says, the types are incompatible. A static array a[] can be used as a pointer, e.g. in the following code:
int foo(bool* a) { /* ... */ }
bool my_array[5];
foo(my_array);
But pointers can not be converted into static arrays as you are trying to do.
Type of value returned from new Type[x] is Type * i.e pointer of that type
examle:
So Right syntax is
bool a;
*a = new bool[x];
so it is wrong to do like is
a=new bool[x] wrong syntax as it is invalid to assign pointer to a normal varibale
see for more details

How does array declaration work in C++? [duplicate]

This question already has answers here:
How do I use arrays in C++?
(5 answers)
Closed 7 years ago.
I'm trying to understand the different ways of declaring an array (of one or two dimensions) in C++ and what exactly they return (pointers, pointers to pointers, etc.)
Here are some examples:
int A[2][2] = {0,1,2,3};
int A[2][2] = {{0,1},{2,3}};
int **A = new int*[2];
int *A = new int[2][2];
In each case, what exactly is A? Is it a pointer, double pointer? What happens when I do A+1? Are these all valid ways of declaring matrices?
Also, why does the first option not need the second set of curly braces to define "columns"?
Looks like you got a plethora of answers while I was writing mine, but I might as well post my answer anyway so I don't feel like it was all for nothing...
(all sizeof results taken from VC2012 - 32 bit build, pointer sizes would, of course, double with a 64 bit build)
size_t f0(int* I);
size_t f1(int I[]);
size_t f2(int I[2]);
int main(int argc, char** argv)
{
// A0, A1, and A2 are local (on the stack) two-by-two integer arrays
// (they are technically not pointers)
// nested braces not needed because the array dimensions are explicit [2][2]
int A0[2][2] = {0,1,2,3};
// nested braces needed because the array dimensions are not explicit,
//so the braces let the compiler deduce that the missing dimension is 2
int A1[][2] = {{0,1},{2,3}};
// this still works, of course. Very explicit.
int A2[2][2] = {{0,1},{2,3}};
// A3 is a pointer to an integer pointer. New constructs an array of two
// integer pointers (on the heap) and returns a pointer to the first one.
int **A3 = new int*[2];
// if you wanted to access A3 with a double subscript, you would have to
// make the 2 int pointers in the array point to something valid as well
A3[0] = new int[2];
A3[1] = new int[2];
A3[0][0] = 7;
// this one doesn't compile because new doesn't return "pointer to int"
// when it is called like this
int *A4_1 = new int[2][2];
// this edit of the above works but can be confusing
int (*A4_2)[2] = new int[2][2];
// it allocates a two-by-two array of integers and returns a pointer to
// where the first integer is, however the type of the pointer that it
// returns is "pointer to integer array"
// now it works like the 2by2 arrays from earlier,
// but A4_2 is a pointer to the **heap**
A4_2[0][0] = 6;
A4_2[0][1] = 7;
A4_2[1][0] = 8;
A4_2[1][1] = 9;
// looking at the sizes can shed some light on subtle differences here
// between pointers and arrays
A0[0][0] = sizeof(A0); // 16 // typeof(A0) is int[2][2] (2by2 int array, 4 ints total, 16 bytes)
A0[0][1] = sizeof(A0[0]); // 8 // typeof(A0[0]) is int[2] (array of 2 ints)
A1[0][0] = sizeof(A1); // 16 // typeof(A1) is int[2][2]
A1[0][1] = sizeof(A1[0]); // 8 // typeof(A1[0]) is int[2]
A2[0][0] = sizeof(A2); // 16 // typeof(A2) is int[2][2]
A2[0][1] = sizeof(A2[0]); // 8 // typeof(A1[0]) is int[2]
A3[0][0] = sizeof(A3); // 4 // typeof(A3) is int**
A3[0][1] = sizeof(A3[0]); // 4 // typeof(A3[0]) is int*
A4_2[0][0] = sizeof(A4_2); // 4 // typeof(A4_2) is int(*)[2] (pointer to array of 2 ints)
A4_2[0][1] = sizeof(A4_2[0]); // 8 // typeof(A4_2[0]) is int[2] (the first array of 2 ints)
A4_2[1][0] = sizeof(A4_2[1]); // 8 // typeof(A4_2[1]) is int[2] (the second array of 2 ints)
A4_2[1][1] = sizeof(*A4_2); // 8 // typeof(*A4_2) is int[2] (different way to reference the first array of 2 ints)
// confusion between pointers and arrays often arises from the common practice of
// allowing arrays to transparently decay (implicitly convert) to pointers
A0[1][0] = f0(A0[0]); // f0 returns 4.
// Not surprising because declaration of f0 demands int*
A0[1][1] = f1(A0[0]); // f1 returns 4.
// Still not too surprising because declaration of f1 doesn't
// explicitly specify array size
A2[1][0] = f2(A2[0]); // f2 returns 4.
// Much more surprising because declaration of f2 explicitly says
// it takes "int I[2]"
int B0[25];
B0[0] = sizeof(B0); // 100 == (sizeof(int)*25)
B0[1] = f2(B0); // also compiles and returns 4.
// Don't do this! just be aware that this kind of thing can
// happen when arrays decay.
return 0;
}
// these are always returning 4 above because, when compiled,
// all of these functions actually take int* as an argument
size_t f0(int* I)
{
return sizeof(I);
}
size_t f1(int I[])
{
return sizeof(I);
}
size_t f2(int I[2])
{
return sizeof(I);
}
// indeed, if I try to overload f0 like this, it will not compile.
// it will complain that, "function 'size_t f0(int *)' already has a body"
size_t f0(int I[2])
{
return sizeof(I);
}
yes, this sample has tons of signed/unsigned int mismatch, but that part isn't relevant to the question. Also, don't forget to delete everything created with new and delete[] everything created with new[]
EDIT:
"What happens when I do A+1?" -- I missed this earlier.
Operations like this would be called "pointer arithmetic" (even though I called out toward the top of my answer that some of these are not pointers, but they can turn into pointers).
If I have a pointer P to an array of someType, then subscript access P[n] is exactly the same as using this syntax *(P + n). The compiler will take into account the size of the type being pointed to in both cases. So, the resulting opcode will actually do something like this for you *(P + n*sizeof(someType)) or equivalently *(P + n*sizeof(*P)) because the physical cpu doesn't know or care about all our made up "types". In the end, all pointer offsets have to be a byte count. For consistency, using array names like pointers works the same here.
Turning back to the samples above: A0, A1, A2, and A4_2 all behave the same with pointer arithmetic.
A0[0] is the same as *(A0+0), which references the first int[2] of A0
similarly:
A0[1] is the same as *(A0+1) which offsets the "pointer" by sizeof(A0[0]) (i.e. 8, see above) and it ends up referencing the second int[2] of A0
A3 acts slightly differently. This is because A3 is the only one that doesn't store all 4 ints of the 2 by 2 array contiguously. In my example, A3 points to an array of 2 int pointers, each of these point to completely separate arrays of two ints. Using A3[1] or *(A3+1) would still end up directing you to the second of the two int arrays, but it would do it by offsetting only 4bytes from the beginning of A3 (using 32 bit pointers for my purposes) which gives you a pointer that tells you where to find the second two-int array. I hope that makes sense.
For the array declaration, the first specified dimension is the outermost one, an array that contains other arrays.
For the pointer declarations, each * adds another level of indirection.
The syntax was designed, for C, to let declarations mimic the use. Both the C creators and the C++ creator (Bjarne Stroustrup) have described the syntax as a failed experiment. The main problem is that it doesn't follow the usual rules of substitution in mathematics.
In C++11 you can use std::array instead of the square brackets declaration.
Also you can define a similar ptr type builder e.g.
template< class T >
using ptr = T*;
and then write
ptr<int> p;
ptr<ptr<int>> q;
int A[2][2] = {0,1,2,3};
int A[2][2] = {{0,1},{2,3}};
These declare A as array of size 2 of array of size 2 of int. The declarations are absolutely identical.
int **A = new int*[2];
This declares a pointer to pointer to int initialized with an array of two pointers. You should allocate memory for these two pointers as well if you want to use it as two-dimensional array.
int *A = new int[2][2];
And this doesn't compile because the type of right part is pointer to array of size 2 of int which cannot be converted to pointer to int.
In all valid cases A + 1 is the same as &A[1], that means it points to the second element of the array, that is, in case of int A[2][2] to the second array of two ints, and in case of int **A to the second pointer in the array.
The other answers have covered the other declarations but I will explain why you don't need the braces in the first two initializations. The reason why these two initializations are identical:
int A[2][2] = {0,1,2,3};
int A[2][2] = {{0,1},{2,3}};
is because it's covered by aggregate initialization. Braces are allowed to be "elided" (omitted) in this instance.
The C++ standard provides an example in § 8.5.1:
[...]
float y[4][3] = {
{ 1, 3, 5 },
{ 2, 4, 6 },
{ 3, 5, 7 },
};
[...]
In the following example, braces in the initializer-list are elided;
however the initializer-list has the same effect as the
completely-braced initializer-list of the above example,
float y[4][3] = {
1, 3, 5, 2, 4, 6, 3, 5, 7
};
The initializer for y begins with a left brace, but the one for y[0]
does not, therefore three elements from the list are used. Likewise
the next three are taken successively for y[1] and y[2].
Ok I will try it to explain it to you:
This is a initialization. You create a two dimensional array with the values:
A[0][0] -> 0
A[0][1] -> 1
A[1][0] -> 2
A[1][1] -> 3
This is the exactly the same like above, but here you use braces. Do it always like this its better for reading.
int **A means you have a pointer to a pointer of ints. When you do new int*[2] you will reserve memory for 2 Pointer of integer.
This doesn't will be compiled.
int A[2][2] = {0,1,2,3};
int A[2][2] = {{0,1},{2,3}};
These two are equivalent.
Both mean: "I declare a two dimentional array of integers. The array is of size 2 by 2".
Memory however is not two dimensional, it is not laid out in grids, but (conceptionaly) in one long line. In a multi-dimensional array, each row is just allocated in memory right after the previous one.
Because of this, we can go to the memory address pointed to by A and either store two lines of length 2, or one line of length 4, and the end result in memory will be the same.
int **A = new int*[2];
Declares a pointer to a pointer called A.
A stores the address of a pointer to an array of size 2 containing ints. This array is allocated on the heap.
int *A = new int[2][2];
A is a pointer to an int.
That int is the beginning of a 2x2 int array allocated in the heap.
Aparrently this is invalid:
prog.cpp:5:23: error: cannot convert ‘int (*)[2]’ to ‘int*’ in initialization
int *A = new int[2][2];
But due to what we saw with the first two, this will work (and is 100% equivalent):
int *A new int[4];
int A[2][2] = {0,1,2,3};
A is an array of 4 ints. For the coder's convenience, he has decided to declare it as a 2 dimensional array so compiler will allow coder to access it as a two dimensional array. Coder has initialized all elements linearly as they are laid in memory. As usual, since A is an array, A is itself the address of the array so A + 1 (after application of pointer math) offset A by the size of 2 int pointers. Since the address of an array points to the first element of that array, A will point to first element of the second row of the array, value 2.
Edit: Accessing a two dimensional array using a single array operator will operate along the first dimension treating the second as 0. So A[1] is equivalent to A[1][0]. A + 1 results in equivalent pointer addition.
int A[2][2] = {{0,1},{2,3}};
A is an array of 4 ints. For the coder's convenience, he has decided to declare it as a 2 dimensional array so compiler will allow coder to access it as a two dimensional array. Coder has initialized elements by rows. For the same reasons above, A + 1 points to value 2.
int **A = new int*[2];
A is pointer to int pointer that has been initialized to point to an array of 2 pointers to int pointers. Since A is a pointer, A + 1 takes the value of A, which is the address of the pointer array (and thus, first element of the array) and adds 1 (pointer math), where it will now point to the second element of the array. As the array was not initialized, actually doing something with A + 1 (like reading it or writing to it) will be dangerous (who knows what value is there and what that would actually point to, if it's even a valid address).
int *A = new int[2][2];
Edit: as Jarod42 has pointed out, this is invalid. I think this may be closer to what you meant. If not, we can clarify in the comments.
int *A = new int[4];
A is a pointer to int that has been initialized to point to an anonymous array of 4 ints. Since A is a pointer, A + 1 takes the value of A, which is the address of the pointer array (and thus, first element of the array) and adds 1 (pointer math), where it will now point to the second element of the array.
Some takeaways:
In the first two cases, A is the address of an array while in the last two, A is the value of the pointer which happened to be initialized to the address of an array.
In the first two, A cannot be changed once initialized. In the latter two, A can be changed after initialization and point to some other memory.
That said, you need to be careful with how you might use pointers with an array element. Consider the following:
int *a = new int(5);
int *b = new int(6);
int c[2] = {*a, *b};
int *d = a;
c+1 is not the same as d+1. In fact, accessing d+1 is very dangerous. Why? Because c is an array of int that has been initialized by dereferencing a and b. that means that c, is the address of a chunk of memory, where at that memory location is value which has been set to the value pointed to by tovariable a, and at the next memory location that is a value pinned to by variable b. On the other hand d is just the address of a. So you can see, c != d therefore, there is no reason that c + 1 == d + 1.

How does this pointer typecasting work?

Can anyone please explain how the following code works? I wish to know what is exactly the value returned to variable p and how?
#define MR 3
#define MC 4
int (*p)[MC];
p=(int(*)[MC])malloc(MR*sizeof(*p));
Thanks in advance.
From top to bottom (noting MC 4 and MR 3)
int (*p)[MC];
declares p as a pointer to an array of 4 int.
sizeof(*p)
size of an array of 4 int
MR*sizeof(*p)
3 * (size of an array of 4 int), i.e. 12 contiguous int values.
Finally, the cast:
p=(int(*)[MC])malloc(MR*sizeof(*p));
is simply forcing the underlying void* returned by malloc tothe pointer-type that of the lvalue of the assignment, the pointer p.
In the end, this dynamically allocates an array (dim=3) of arrays (dim=4) of int, in a single dynamic contiguous block of memory. Were this allocated as an automatic variable it would be equivalent to:
int p[MR][MC]
And since you asked how it works. poorly. This is C++. It should be done as:
std::vector<std::array<int,MC>> ar(MR);
p is a variable of type "pointer to int array of size MC". sizeof(*p) is the size of MC ints. Effectively, p is now the pointer to a 2D array with MR rows and MC columns.