Related
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.
I'm trying something very simple, well supposed to be simple but it somehow is messing with me...
I am trying to understand the effect of ++ on arrays when treated as pointers and pointers when treated as arrays.
So,
int main()
{
int a[4] = { 1, 4, 7, 9 };
*a = 3;
*(a+1) = 4;
*++a = 4; //compiler error
}
1: So at *(a+1)=4 we set a[1]=4; //Happy
But when *++a = 4;, I'd expect pointer a to be incremented one since ++ is precedent to * and then * kicks in and we make it equal to 4. But this code just does not work... Why is that?
Another problem:
int main()
{
int* p = (int *)malloc(8);
*p = 5;
printf("%d", p[0]);
*++p = 9; //now this works!
printf("%d", p[1]); //garbage
printf("%d", p[0]); //prints 9
}
2: Now *++p = 9; works fine but it's not really behaving like an array. How are two different? This is just incrementing p, and making it equal to 9. If I print p[0], it now prints 9 and I see that though can't access it via p[0] anymore, *(p-1) shows 5 is still there. So indexing a pointer with [0], where exactly does it point to? What has changed?
Thanks a lot all experts!
The array names is not modifiable lvalue so operation ++ is not applied hence ++a that try to modify a is compilation time error (where a is array name).
Note *(a + 1) and *a++ are not same, a + 1 is a valid instruction as it just add 1 but doesn't modify a itself, Whereas ++a (that is equvilent to a = a + 1) try to modify a hence error.
Note 'array names' are not pointer. Pointers are variable but array names are not. Of-course when you assign array name to a pointer then in most expressions array names decays into address of first element. e.g.
int *p = a;
Note p points to first element of array (a[0]).
Read some exceptions where array name not decaying into a pointer to first element?
An expression a[i] is equivalent to *(a + i), where a can be either a pointer or an array name. Hence in your second example p[i] is valid expression.
Additionally, *++p is valid because because p is a pointer (a variable) in second code example.
int a[4] = { 1, 4, 7, 9 };
int *pa=a;
There is one difference between an array name and a pointer that must be kept in mind. A pointer is a variable, sopa=a and pa++ are legal. But an array name is not a
variable; constructions like a=pa and a++ are illegal
int* p = (int *)malloc(8);
Don't cast result of malloc()
Use index with pointer
p[1]=9; // p[1]==*(p+1)
What is the difference when array is declared as array[n] or as pointer array* according to example below? I guess that for example both 'a' and 'c' point at the first element of array, but they behave different.
#include <iostream>
int main() {
int a[3] = {1};
int b[5];
std::cout << *a << std::endl; //prints 1 - ok
//a = b; //error during compilation
int* c = new int[3];
c[0] = 2;
int* d = new int[5];
std::cout << *c << std::endl; //prints 2 - ok
c = d; //works ok!
return 0;
}
Long story short - they are essentially the same, but ever so slightly different.
From what I've gathered from http://c-faq.com/aryptr/aryptr2.html , whilst they can both act as a pointer to the front of an array, when you declare an array as
int a[3];
you are essentially binding the size of '3' to your variable a, along with the fact it's an array. Hence, when you try to assign b, of size 5 to a, you get a compilation error.
In contrast, when you write
int * a;
You are merely saying 'this is a pointer that may point to an array', with no promise on the size.
Subtle, isn't it?
The difference between the following two lines:
int g[10];
and
int* h = new int[10];
is that the second is dynamically-allocated, whereas the first is statically-allocated.
For most purposes, they are identical, but where in memory they end up living is different.
This is basically just to help me understand pointers better, so if you guys can confirm/deny/explain anything it looks like I don't understand properly I would be most appreciative. The examples of using mailboxes, and aunts, and streets, and all that crap is just confusing.
int a = 5;
int b = &a; // b will be the memory address of 'a'
int *c = a; // c will be the value of 'a' which is 5
int *d = &a; // d will be a pointer to the memory address of 'a'
int &e = a; // what would this be?
void FunctionA()
{
int a = 20;
FunctionB(&a);
// a is now 15?
}
void FunctionB(int *a)
{
a = 15;
}
Thank you guys for any help, I am just trying to improve my understanding beyond all of the crappy metaphor explanations im reading.
I'll take things one by one:
int b = &a; // b will be the memory address of 'a'
No. The compiler (probably) won't allow this. You've defined b to be an int, but &a is the address of an int, so the initialization won't work.
int *c = a;
No -- same problem, but in reverse. You've defined c to be a pointer to an int, but you're trying to initialize it with the value of an int.
int *d = &a;
Yes -- you've defined d to be a pointer to an int, and you're assigning the address of an int to it -- that's fine. The address of an int (or an array of ints) is what a pointer to int holds.
int &e = a;
This defines e to be a reference to an int and initializes it as a reference to a. It's perfectly legitimate, but probably not very useful. Just for Reference, the most common use of a reference is as a function parameter (though there are other purposes, of course).
void FunctionA() { int a = 20; FunctionB(&a); }
void FunctionB(int *a) { a = 15; }
To make this work, you need to change the assignment in FunctionB:
void FunctionB(int *a) { *a = 15; }
As it was, you were trying to assign an int to a pointer, which won't work. You need to assign the int to the int that the pointer points at to change the value in the calling function.
int &e = a; // what would this be?
e is a reference to a. In this case the ampersand is not the 'address of' operator. You treat e as you would a normal (not a pointer) variable, but the value of e and of a will be the same no matter what you do to either (as long as both remain in scope) as essentially e is just an alias.
int a = 5;
So far so good.
int b = &a; // b will be the memory address of 'a'
That's actually a compilation error. You probably mean int *b=&a;. b is a POINTER to an integer.
edit: If you mean to get the address in numerical form, you need to force the cast to an integer: int b=(int)&a;
int *c = a; // c will be the value of 'a' which is 5
This one is more confusing. At its core, a pointer is just a number, sure, but this kind of assignement is inherently not safe (as you can see, you're assigning 5 to a pointer, and trying to dereference that will most likely crash your program). If you really do want c to point at the memory location 5, you have to explicitly tell the compiler you know what you're doing: int *c=(int *)a.
int *d = &a; // d will be a pointer to the memory address of 'a'
This one is right, same as what you probably mean by the second one.
int &e = a; // what would this be?
e is a "reference" to a. Basically internally it's just a pointer to a, but you don't have to manually dereference it, the compiler handles it for you.
void FunctionA() { int a = 20; FunctionB(&a); // a is now 15? }
Yes.
void FunctionB(int *a) { a = 15; }
...assuming you write this as *a=15;. You're overwriting the VALUE pointed to by a, not the pointer itself.
You seem pretty confused by this overall, I recommend reading the book "Thinking in C++", it's really well written!
You've got plenty of good answers here for your specific example, so I'd like to share a general technique that I used to learn how pointers work when I was starting out.
Get a big sheet of graph paper and lay it lengthwise on the table in front of you. This is your computer's memory. Each box represents one byte. Pick a row, and place the number '100' below the box at far left. This is "the lowest address" of memory. (I chose 100 as an arbitrary number that isn't 0, you can choose another.) Number the boxes in ascending order from left to right.
+---+---+---+---+---+--
| | | | | | ...
+---+---+---+---+---+--
100 101 102 103 104 ...
Now, just for the moment, pretend an int is one byte in size. You are an eight-bit computer. Write your int a into one of the boxes. The number below the box is its address. Now choose another box to contain int *b = &a. int *b is also a variable stored somewhere in memory, and it is a pointer that contains &a, which is pronounced "a's address".
int a = 5;
int *b = &a;
a b
+---+---+---+---+---+--
| 5 | |100| | | ...
+---+---+---+---+---+--
100 101 102 103 104 ...
Now you can use this model to visually work through any other combinations of values and pointers that you see. It is a simplification (because as language pedants will say, a pointer isn't necessarily an address, and memory isn't necessarily sequential, and there's stack and heap and registers and so on), but it's a pretty good analogy for 99% of computers and microcontrollers.
You can extend the model for real four-byte ints too...
int a = 5;
char b = 2;
a a a a b
+---+---+---+---+---+--
| 0 | 0 | 0 | 5 | 2 | ...
+---+---+---+---+---+--
100 101 102 103 104 ...
int &e=a; is a reference to "a".
and these are bugs:
int b = &a;
int *c = a;
I think you mean:
int *b = &a;, which makes a pointer called b that points to the value of a (b is a pointer which is the address of a)
int c = *b;, (or just int c = a if you only want c to have a's value) In this case, * dereferences the pointer b
FunctionA() See below, then a will be 15 (you're passing the address of a to FunctionB)
void FunctionB(int *a) {*a = 15;} sets the value to 15 (* dereferences)
Can someone explain why I can do:
int x[4] = { 0, 1, 2, 3 };
int *p = x;
But cannot do:
int x[2][2] = { 0, 1, 2, 3 };
int **p = x;
Is there a way to be able to assign to **p the x[][]?
TIA
A pointer to int int * can point at an integer within an array of integers, so that's why the first is OK;
int x[4] = { 0, 1, 2, 3 };
int *p = x;
This makes p point to x[0], the first int in x.
A pointer to a pointer-to-int int ** can point at a pointer-to-int within an array of pointers-to-int. However, your second array is not an array of pointers-to-int; it is an array of arrays. There are no pointers-to-int, so there is nothing sensible to point int **p at. You can solve this in two ways. The first is to change the type of p so that it is a pointer-to-array-of-2-ints:
int x[2][2] = { 0, 1, 2, 3 };
int (*p)[2] = x;
Now p points at x[0], the first int [2] in x.
The alternative is to create some pointers-to-int to point at:
int x[2][2] = { 0, 1, 2, 3 };
int *y[2] = { x[0], x[1] };
int **p = y;
Now p points at y[0], which is the first int * in y. y[0] in turn points at x[0][0], and y[1] points at x[1][0].
Using the pointer notation you can do this
*(p + n) will return back a value in the subscript n which is equivalent of
p[n], a "single dimension array"
*( * (p + i) + j) will return back a value in the subscripts i and j which
is equivalent of p[i][j], a "double dimension array"
This will prove that using array subscripts [] decays into pointers as per the rule - see ANSI C Standard, 6.2.2.1 - "An array name in an expression is treated by the compiler as a pointer to the first element of the array", furthermore, ANSI C Standard 6.3.2.1 - "A subscript is always equivalent to an offset from a pointer", and also ANSI C Standard 6.7.1 - "An array name in the declaration of a function parameter is treated as a pointer to the first element in the array"
If you are not sure in understanding how pointers and arrays work, please see my other answer which will explain how it works...
Please ignore anyone who says arrays and pointers are the same - they are not...
No, there's no way to assign the array to an "int **", because a two-dimensional array is an array of arrays, not an array of pointers.
You could for instance do:
int x1[2] = {0, 1};
int x2[2] = {2, 3};
int *x[2] = {x1, x2};
int **p = x;
... and maybe that would be an acceptable equivalent to what you are trying to do (depending on what exactly that is of course!).
Update: If you must keep x as a two-dimensional array, you could also do: int (*p)[2] = x; which gives you a pointer-to-array-of-int.
There is no way to meaningfully initialize you variable p with x. Why? Well, there's no way to answer your question until you explain how you even came to this idea. The type of p is int **. The type of x is int[2][2]. These are two different types. Where did you get the idea that you should be able to assign one to the other?
If you really need to access elements of your x through a pointer of type int **, you have to do it indirectly. You have to create an additional intermediate array that will hold pointers to first elements of consecutive rows of array x:
int *rows[2] = { x[0], x[1] };
and now you can point your p to the beginning of this intermediate array
int **p = rows;
Now when you assess p[i][j] you get x[i][j].
There's no way to do it directly, without an intermediate array.
If you are really using c++ instead of c, you can get this effect by using std::vector (or some other collection type).
typedef std::vector<std::vector<int> > intmatrix;
intmatrix x;
intmatrix::iterator p = x.begin();
or some such.
It's all about Standard Conversion rule ($4.2)
An lvalue or rvalue of type “array ofN
T” or “array of unknown bound of T”
can be converted to an rvalue of type
“pointer to T.” The result is a
pointer to the first element of the
array.
The type of od and td respectively are
char [3]
char [1][3]
This means that od has type 'array of 3 chars' (N = 3, T = char).
So in accordance with the above quote, it can be converted to 'pointer to char'
Therefore char *p = od; is perfectly well-formed
Similarly the type of od is 'array of 1 array of 3 chars' (N = 1, T = array of 3 chars).
So in accordance with the above quote, it can be converted to 'pointer to array of 3 chars'
Therefore char (*p)[3] = td; is perfectly well-formed
Is there a way to be able to assign to
**p the x[][]?
No. Because type of td[x][y] for valid x and y is char. So what you can really do is char *pc = &td[0][0].