I have this code - http://ideone.com/wslBnj
#include <stdio.h>
int main(void) {
int array[2][3] = {5,10,15,20,25,30};
int (*ptr)[2][3] = &array; // line 1
printf("%d\n",***ptr);
printf("%d\n",*(*ptr)[1]);// line 2
printf("%d\n",(*ptr)[1][2]);
return 0;
}
I don't understand the LHS(left hand side) syntax at 'line 1'. What does it mean (in words)?
I don't understand the priority given in 'line 2'. I mean, the output could be for (*(*ptr))[1] this grouping, but it was for this *((*ptr)[1]). Why is that?
Can someone explain both the doubts in detail? I couldn't understand the search terms to search for this question on internet. That's why I am asking it directly here.
I don't understand the LHS(left hand side) syntax at 'line 1'. What
does it mean (in words)?
In C, declarations follow use.. It means that (*ptr)[2][3] is of type int (well, technically, [2][3] would be out of bounds, but you get the point). Anyway, this implies that (*ptr) is a multi-dimensional array of ints - namely, a 2x3 array of ints. Therefore, ptr is a pointer to an array[2][3] of ints.
I don't understand the priority given in 'line 2'. I mean, the output
could be for (*(*ptr))[1] this grouping, but it was for this
*((*ptr)[1]). Why is that?
Indexing has higher priority than dereferencing. Thus, *(*ptr)[1] is equivalent to *((*ptr)[1]), and in this case it is equivalent to (*ptr)[1][0], since (*ptr)[1] is an array of 3 ints.
This line
int (*ptr)[2][3] = &array; // line 1
declares a pointer ( *ptr ) to an object of type int[2][3] That is it is a pointer to two dimensional array of type int[2][3]
If you would not use parentheses
int * ptr[2][3];
then the declaration would mean a two dimensional array of pointers of type int *
In line #2 expression
*(*ptr)[1]
is evaluated in the following order.
1. ( *ptr )
As ptr is declared as an pointer to a two dimemsional array then dereferencing the pointer will give the object pointed to by the pointer. So ( *ptr ) means array
int array[2][3]
2. ( *ptr )[1]
is the reference to the second row of the array
and at last in this expression
3. *( *ptr )[1]
the array ( *ptr )[1] that is the second row of the array "array" is converted to pointer to its first element and this pointer is dereferenced. So as result *( *ptr )[1]the first element of the second row of array "array" that is you will get 20.
Operator * that is an unary operator has lower priority than operator [] and is evaluated right to left.
From the C++ Standard
5.3 Unary expressions
1 Expressions with unary operators group right-to-left.
Thus if you will write for your array "array" expression
**array
then at first there is evaluated subexpression *array that will yield the reference to the first row of the array and then *( *array ) will yield the first element of the first row.
int (*ptr)[2][3] could also mean a pointer to the first [2][3] matrix in a 3d array, for example:
int array[2][2][3] = {{{4,8,12},{8,16,24}},{{5,10,15},{20,25,30}}};
int (*ptr)[2][3] = array; // note &array is not used here
Related
What is the difference between the following declarations:
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
What is the general rule for understanding more complex declarations?
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers
The third one is same as the first.
The general rule is operator precedence. It can get even much more complex as function pointers come into the picture.
Use the cdecl program, as suggested by K&R.
$ cdecl
Type `help' or `?' for help
cdecl> explain int* arr1[8];
declare arr1 as array 8 of pointer to int
cdecl> explain int (*arr2)[8]
declare arr2 as pointer to array 8 of int
cdecl> explain int *(arr3[8])
declare arr3 as array 8 of pointer to int
cdecl>
It works the other way too.
cdecl> declare x as pointer to function(void) returning pointer to float
float *(*x)(void )
I don't know if it has an official name, but I call it the Right-Left Thingy(TM).
Start at the variable, then go right, and left, and right...and so on.
int* arr1[8];
arr1 is an array of 8 pointers to integers.
int (*arr2)[8];
arr2 is a pointer (the parenthesis block the right-left) to an array of 8 integers.
int *(arr3[8]);
arr3 is an array of 8 pointers to integers.
This should help you out with complex declarations.
int *a[4]; // Array of 4 pointers to int
int (*a)[4]; //a is a pointer to an integer array of size 4
int (*a[8])[5]; //a is an array of pointers to integer array of size 5
The answer for the last two can also be deducted from the golden rule in C:
Declaration follows use.
int (*arr2)[8];
What happens if you dereference arr2? You get an array of 8 integers.
int *(arr3[8]);
What happens if you take an element from arr3? You get a pointer to an integer.
This also helps when dealing with pointers to functions. To take sigjuice's example:
float *(*x)(void )
What happens when you dereference x? You get a function that you can call with no arguments. What happens when you call it? It will return a pointer to a float.
Operator precedence is always tricky, though. However, using parentheses can actually also be confusing because declaration follows use. At least, to me, intuitively arr2 looks like an array of 8 pointers to ints, but it is actually the other way around. Just takes some getting used to. Reason enough to always add a comment to these declarations, if you ask me :)
edit: example
By the way, I just stumbled across the following situation: a function that has a static matrix and that uses pointer arithmetic to see if the row pointer is out of bounds. Example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUM_ELEM(ar) (sizeof(ar) / sizeof((ar)[0]))
int *
put_off(const int newrow[2])
{
static int mymatrix[3][2];
static int (*rowp)[2] = mymatrix;
int (* const border)[] = mymatrix + NUM_ELEM(mymatrix);
memcpy(rowp, newrow, sizeof(*rowp));
rowp += 1;
if (rowp == border) {
rowp = mymatrix;
}
return *rowp;
}
int
main(int argc, char *argv[])
{
int i = 0;
int row[2] = {0, 1};
int *rout;
for (i = 0; i < 6; i++) {
row[0] = i;
row[1] += i;
rout = put_off(row);
printf("%d (%p): [%d, %d]\n", i, (void *) rout, rout[0], rout[1]);
}
return 0;
}
Output:
0 (0x804a02c): [0, 0]
1 (0x804a034): [0, 0]
2 (0x804a024): [0, 1]
3 (0x804a02c): [1, 2]
4 (0x804a034): [2, 4]
5 (0x804a024): [3, 7]
Note that the value of border never changes, so the compiler can optimize that away. This is different from what you might initially want to use: const int (*border)[3]: that declares border as a pointer to an array of 3 integers that will not change value as long as the variable exists. However, that pointer may be pointed to any other such array at any time. We want that kind of behaviour for the argument, instead (because this function does not change any of those integers). Declaration follows use.
(p.s.: feel free to improve this sample!)
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
As a rule of thumb, right unary operators (like [], (), etc) take preference over left ones. So, int *(*ptr)()[]; would be a pointer that points to a function that returns an array of pointers to int (get the right operators as soon as you can as you get out of the parenthesis)
I think we can use the simple rule ..
example int * (*ptr)()[];
start from ptr
" ptr is a pointer to "
go towards right ..its ")" now go left its a "("
come out go right "()" so
" to a function which takes no arguments " go left "and returns a pointer " go right "to
an array" go left " of integers "
Here's an interesting website that explains how to read complex types in C:
http://www.unixwiz.net/techtips/reading-cdecl.html
Here's how I interpret it:
int *something[n];
Note on precedence: array subscript operator ([]) has higher priority than
dereference operator (*).
So, here we will apply the [] before *, making the statement equivalent to:
int *(something[i]);
Note on how a declaration makes sense: int num means num is an int, int *ptr or int (*ptr) means, (value at ptr) is
an int, which makes ptr a pointer to int.
This can be read as, (value of the (value at ith index of the something)) is an integer. So, (value at the ith index of something) is an (integer pointer), which makes the something an array of integer pointers.
In the second one,
int (*something)[n];
To make sense out of this statement, you must be familiar with this fact:
Note on pointer representation of array: somethingElse[i] is equivalent to *(somethingElse + i)
So, replacing somethingElse with (*something), we get *(*something + i), which is an integer as per declaration. So, (*something) given us an array, which makes something equivalent to (pointer to an array).
I guess the second declaration is confusing to many. Here's an easy way to understand it.
Lets have an array of integers, i.e. int B[8].
Let's also have a variable A which points to B. Now, value at A is B, i.e. (*A) == B. Hence A points to an array of integers. In your question, arr is similar to A.
Similarly, in int* (*C) [8], C is a pointer to an array of pointers to integer.
int *arr1[5]
In this declaration, arr1 is an array of 5 pointers to integers.
Reason: Square brackets have higher precedence over * (dereferncing operator).
And in this type, number of rows are fixed (5 here), but number of columns is variable.
int (*arr2)[5]
In this declaration, arr2 is a pointer to an integer array of 5 elements.
Reason: Here, () brackets have higher precedence than [].
And in this type, number of rows is variable, but the number of columns is fixed (5 here).
In pointer to an integer if pointer is incremented then it goes next integer.
in array of pointer if pointer is incremented it jumps to next array
What is the difference between the following declarations:
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
What is the general rule for understanding more complex declarations?
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers
The third one is same as the first.
The general rule is operator precedence. It can get even much more complex as function pointers come into the picture.
Use the cdecl program, as suggested by K&R.
$ cdecl
Type `help' or `?' for help
cdecl> explain int* arr1[8];
declare arr1 as array 8 of pointer to int
cdecl> explain int (*arr2)[8]
declare arr2 as pointer to array 8 of int
cdecl> explain int *(arr3[8])
declare arr3 as array 8 of pointer to int
cdecl>
It works the other way too.
cdecl> declare x as pointer to function(void) returning pointer to float
float *(*x)(void )
I don't know if it has an official name, but I call it the Right-Left Thingy(TM).
Start at the variable, then go right, and left, and right...and so on.
int* arr1[8];
arr1 is an array of 8 pointers to integers.
int (*arr2)[8];
arr2 is a pointer (the parenthesis block the right-left) to an array of 8 integers.
int *(arr3[8]);
arr3 is an array of 8 pointers to integers.
This should help you out with complex declarations.
int *a[4]; // Array of 4 pointers to int
int (*a)[4]; //a is a pointer to an integer array of size 4
int (*a[8])[5]; //a is an array of pointers to integer array of size 5
The answer for the last two can also be deducted from the golden rule in C:
Declaration follows use.
int (*arr2)[8];
What happens if you dereference arr2? You get an array of 8 integers.
int *(arr3[8]);
What happens if you take an element from arr3? You get a pointer to an integer.
This also helps when dealing with pointers to functions. To take sigjuice's example:
float *(*x)(void )
What happens when you dereference x? You get a function that you can call with no arguments. What happens when you call it? It will return a pointer to a float.
Operator precedence is always tricky, though. However, using parentheses can actually also be confusing because declaration follows use. At least, to me, intuitively arr2 looks like an array of 8 pointers to ints, but it is actually the other way around. Just takes some getting used to. Reason enough to always add a comment to these declarations, if you ask me :)
edit: example
By the way, I just stumbled across the following situation: a function that has a static matrix and that uses pointer arithmetic to see if the row pointer is out of bounds. Example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUM_ELEM(ar) (sizeof(ar) / sizeof((ar)[0]))
int *
put_off(const int newrow[2])
{
static int mymatrix[3][2];
static int (*rowp)[2] = mymatrix;
int (* const border)[] = mymatrix + NUM_ELEM(mymatrix);
memcpy(rowp, newrow, sizeof(*rowp));
rowp += 1;
if (rowp == border) {
rowp = mymatrix;
}
return *rowp;
}
int
main(int argc, char *argv[])
{
int i = 0;
int row[2] = {0, 1};
int *rout;
for (i = 0; i < 6; i++) {
row[0] = i;
row[1] += i;
rout = put_off(row);
printf("%d (%p): [%d, %d]\n", i, (void *) rout, rout[0], rout[1]);
}
return 0;
}
Output:
0 (0x804a02c): [0, 0]
1 (0x804a034): [0, 0]
2 (0x804a024): [0, 1]
3 (0x804a02c): [1, 2]
4 (0x804a034): [2, 4]
5 (0x804a024): [3, 7]
Note that the value of border never changes, so the compiler can optimize that away. This is different from what you might initially want to use: const int (*border)[3]: that declares border as a pointer to an array of 3 integers that will not change value as long as the variable exists. However, that pointer may be pointed to any other such array at any time. We want that kind of behaviour for the argument, instead (because this function does not change any of those integers). Declaration follows use.
(p.s.: feel free to improve this sample!)
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
As a rule of thumb, right unary operators (like [], (), etc) take preference over left ones. So, int *(*ptr)()[]; would be a pointer that points to a function that returns an array of pointers to int (get the right operators as soon as you can as you get out of the parenthesis)
I think we can use the simple rule ..
example int * (*ptr)()[];
start from ptr
" ptr is a pointer to "
go towards right ..its ")" now go left its a "("
come out go right "()" so
" to a function which takes no arguments " go left "and returns a pointer " go right "to
an array" go left " of integers "
Here's an interesting website that explains how to read complex types in C:
http://www.unixwiz.net/techtips/reading-cdecl.html
Here's how I interpret it:
int *something[n];
Note on precedence: array subscript operator ([]) has higher priority than
dereference operator (*).
So, here we will apply the [] before *, making the statement equivalent to:
int *(something[i]);
Note on how a declaration makes sense: int num means num is an int, int *ptr or int (*ptr) means, (value at ptr) is
an int, which makes ptr a pointer to int.
This can be read as, (value of the (value at ith index of the something)) is an integer. So, (value at the ith index of something) is an (integer pointer), which makes the something an array of integer pointers.
In the second one,
int (*something)[n];
To make sense out of this statement, you must be familiar with this fact:
Note on pointer representation of array: somethingElse[i] is equivalent to *(somethingElse + i)
So, replacing somethingElse with (*something), we get *(*something + i), which is an integer as per declaration. So, (*something) given us an array, which makes something equivalent to (pointer to an array).
I guess the second declaration is confusing to many. Here's an easy way to understand it.
Lets have an array of integers, i.e. int B[8].
Let's also have a variable A which points to B. Now, value at A is B, i.e. (*A) == B. Hence A points to an array of integers. In your question, arr is similar to A.
Similarly, in int* (*C) [8], C is a pointer to an array of pointers to integer.
int *arr1[5]
In this declaration, arr1 is an array of 5 pointers to integers.
Reason: Square brackets have higher precedence over * (dereferncing operator).
And in this type, number of rows are fixed (5 here), but number of columns is variable.
int (*arr2)[5]
In this declaration, arr2 is a pointer to an integer array of 5 elements.
Reason: Here, () brackets have higher precedence than [].
And in this type, number of rows is variable, but the number of columns is fixed (5 here).
In pointer to an integer if pointer is incremented then it goes next integer.
in array of pointer if pointer is incremented it jumps to next array
I have created a 2D array, and tried to print certain values as shown below:
int a[2][2] = { {1, 2},
{3, 4}};
printf("%d %d\n", *(a+1)[0], ((int *)a+1)[0]);
The output is:
3 2
I understand why 3 is the first output (a+1 points to the second row, and we print its 0th element.
My question is regarding the second output, i.e., 2. My guess is that due to typecasting a as int *, the 2D array is treated like a 1D array, and thus a+1 acts as pointer to the 2nd element, and so we get the output as 2.
Are my assumptions correct or is there some other logic behind this?
Also, originally what is the type of a when treated as pointer int (*)[2] or int **?
When you wrote expression
(int *)a
then logically the original array can be considered as a one-dimensional array the following way
int a[4] = { 1, 2, 3, 4 };
So expression a points to the first element equal to 1 of this imaginary array. Expression ( a + 1 ) points to the second element of the imaginary array equal to 2 and expression ( a + 1 )[0] returns reference to this element that is you get 2.
Are my assumptions correct or is there some other logic behind this?
Yes.
*(a+1)[0] is equivalent to a[1][0].
((int *)a+1)[0] is equivalent to a[0][1].
Explanation:
a decays to pointer to first element of 2D array, i.e to the first row. *a dereferences that row which is an array of 2 int. Therefore *a can be treated as an array name of first row which further decay to pointer to its first element, i.e 1. *a + 1 will give the pointer to second element. Dereferencing *a + 1 will give 1. So:
((int *)a+1)[0] == *( ((int *)a+1 )+ 0)
== *( ((int *)a + 0) + 1)
== a[0][1]
Note that a, *a, &a, &a[0] and &a[0][0] all have the same address value although they are of different types. After decay, a is of type int (*)[2]. Casting it to int * just makes the address value to type int * and the arithmetic (int *)a+1 gives the address of second element.
Also, originally what is the type of a when treated as pointer (int (*)[2] or int **?
It becomes of type pointer to array of 2 int, i.e int (*)[2]
A 2D-array is essentially a single-dimensional array with some additional compiler's knowledge.
When you cast a to int*, you remove this knowledge, and it's treated like a normal single-dimensional array (which in your case looks in memory like 1 2 3 4).
The key thing to recognize here is that the a there holds the value of the address where the first row is located at. Since the whole array starts from the same location as that, the whole array also has the same address value; same for the very first element.
In C terms:
&a == &a[0];
&a == &a[0][0];
&a[0] == &a[0][0];
// all of these hold true, evaluate into 1
// cast them if you want, looks ugly, but whatever...
&a == (int (*)[2][2]) &a[0];
&a == (int (*)[2][2]) &a[0][0];
&a[0] == (int (*)[2]) &a[0][0];
For this reason, when you cast the a to int *, it simply becomes 1-to-1 equivalent to &a[0][0] both by the means of type and the value. If you were to apply those operations to &a[0][0]:
(&a[0][0] + 1)[0];
(a[0] + 1)[0];
*(a[0] + 1);
a[0][1];
As for the type of a when treated as a pointer, although I am not certain, should be int (*)[2].
I have a rather simple question about arrays and pointer to arrays.
consider this code fragment..
int (*ptr)[3]; //A pointer to an array of 3 ints
int arr1[3] = {2,4,6,};
ptr = &arr1; //ptr now points to arr1
//3 different ways to express the same address
cout << &arr1 << "\t" << arr1 << "\t" << &arr1[0] << endl;
Now if:
&arr1 == arr1 == &arr1[0]..
why is this code not correct:
ptr = arr1;
or
ptr = &arr1[0];
This has been driving me crazy...so please any explanation would be appreciated. Also please not that this is not an homework question, just something I'm trying to get a grips on.
In
ptr = arr1;
arr1 is converted to an int*, so you're trying to assign from an incompatible pointer type. &arr1[0] is directly an int*, without conversion, so again incompatible.
&arr1 == arr1 == &arr1[0]
is wrong, since the entities have different types. They only point to the same address, so when printing out, they give the same result.
In most contexts, an expression with an array type is implicitly converted to a pointer to the first element of such array, as explained by 6.3.2.1p3:
Except when it is the operand of the sizeof operator, the _Alignof operator, or the
unary & operator, or is a string literal used to initialize an array, an expression that has
type array of type is converted to an expression with type pointer to type that points
to the initial element of the array object and is not an lvalue.
Thus the right-hand side of your assignment
ptr = arr1;
is implicitly converted to an incompatible pointer type (int* vs. int (*)[3]), and can't be stored to the pointer variable without a cast.
This isn't really an exception to any rule, as you need to use the unary & operator with other types, too:
T val, *ptr;
ptr = &val;
Below programme will help you to better understand difference between
pointer_to_first_member_of_array, pointer_to_1D_array, pointer_to_2D_array.
Please carefully look at the programme, execute it and see output.
#include<stdio.h>
int priv_element = 88;
int array[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int next_element = 99;
main (int argc, char *argv[])
{
int *ptr_to_first_element = &array[0][0];
int (*ptr_to_1d_arry)[5] = &array[0];
int (*ptr_to_2d_arry)[2][5] = &array;
printf ("Print first num of first array of 2-Dim array: %d\n",
*ptr_to_first_element);
ptr_to_first_element += 5;
printf ("Print first num of second array of 2-Dim array: %d\n",
*ptr_to_first_element);
printf ("Print first num of first array of 2-Dim array: %d\n",
(*ptr_to_1d_arry)[0]);
ptr_to_1d_arry++;
printf ("Print first num of second array of 2-Dim array: %d\n",
(*ptr_to_1d_arry)[0]);
printf ("Print first num of first array of 2-Dim array: %d\n",
(*ptr_to_2d_arry)[0][0]);
ptr_to_2d_arry++;
printf
("Now you increased to point end of 2d-array space. So next to it is next_element on data-seg: %d\n",
(*ptr_to_2d_arry)[0][0]);
}
When you printed the various expressions, it showed you that their values were the same. However, they do not have the same types.
C and C++ include type features to reduce human mistakes and to make it easier to write complicated code. Suppose you had an address in some pointer p and C/C++ allowed you to do either:
float *f = p;
or:
int *i = p;
This would be a mistake, because, generally, whatever bits are in the memory at p do not represent both a useful int and a useful float. By enforcing rules about types, the language prevents a programmer from making a mistake here; the pointer p can only be assigned to another pointer of a compatible type, unless the programmer explicitly overrides the rules with a cast.
Similarly, your ptr is a pointer to an array of three int. At first, it might seem like your arr1 is also an array of three int, so you should be able to assign ptr = arr1;. This is wrong because ptr is merely a pointer, but arr1 is an entire array object. You cannot put an entire array into a pointer; you need to put a pointer to the array into the pointer. To get a pointer to the array, you use the & operator: ptr = &arr1;.
Another thing that is confusing here is that C/C++ includes an automatic shortcut: It converts an array to a pointer to the first element of the array. When arr1 appears in most contexts, it is changed automatically to &arr1[0]. There is not a huge philosophical reason for this; it is just a convenience for the ways we often use arrays. So ptr = arr1; would be equivalent to ptr = &arr1[0];, which is also not allowed. In this form, you can see that arr1 has become a pointer to an int, so you cannot assign it to a pointer to an array of int. Even though the pointer has the value you want, it is the wrong type.
When an array appears as the operand of & or sizeof or _Alignof, this automatic conversion does not occur. So &arr1 results in the address of the array.
A string literal that is used in an initialization such as char a[] = "abc"; is treated specially and is not automatically converted as described above.
I understood multi-dimensional arrays as pointers to pointers, but perhaps I am wrong?
For example, I though:
char * var = char var[]
char ** var = char* var[] or char var[][]
char *** var = char var[][][] or char* var[][] or char** var[]
Is this incorrect? I was confused because I saw a char*[][] cast as a char** in a simple text book example.
I pasted the example below. Can anyone clear this up for me? Thanks!
/* A simple dictionary. */
#include <stdio.h>
#include <string.h>
#include <ctype.h>
/* list of words and meanings */
char *dic[][40] = {
"atlas", "A volume of maps.",
"car", "A motorized vehicle.",
"telephone", "A communication device.",
"airplane", "A flying machine.",
"", "" /* null terminate the list */
};
int main(void)
{
char word[80], ch;
char **p;
do {
puts("\nEnter word: ");
scanf("%s", word);
p = (char **)dic;
/* find matching word and print its meaning */
do {
if(!strcmp(*p, word)) {
puts("Meaning:");
puts(*(p+1));
break;
}
if(!strcmp(*p, word)) break;
p = p + 2; /* advance through the list */
} while(*p);
if(!*p) puts("Word not in dictionary.");
printf("Another? (y/n): ");
scanf(" %c%*c", &ch);
} while(toupper(ch) != 'N');
return 0;
}
The rule for C is as follows:
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.
The language for C++ is a little different:
4.2 Array-to-pointer conversion [conv.array]
1 An lvalue or rvalue of type “array of N 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.
...
8.3.4 Arrays [dcl.array]
...
7 A consistent rule is followed for multidimensional arrays. If E is an n-dimensional array of rank i × j × ... × k, then E appearing in an expression is converted to a pointer to an (n−1)-dimensional array with rank j × ... × k. If the * operator, either explicitly or implicitly as a result of subscripting, is applied to this pointer, the result is the pointed-to (n−1)-dimensional array, which itself is immediately converted into a pointer.
So the following all hold true:
Declaration Expression Type Decays to
----------- ---------- ---- ---------
T a[N] a T [N] T *
&a T (*)[N]
*a T
a[i] T
T a[M][N] a T [M][N] T (*)[N]
&a T (*)[M][N]
*a T [N] T *
a[i] T [N] T *
&a[i] T (*)[N]
*a[i] T
a[i][j] T
T a[M][N][O] a T [M][N][O] T (*)[M][N]
&a T (*)[M][N][O]
*a T [M][N] T (*)[N]
a[i] T [M][N] T (*)[N]
&a[i] T (*)[M][N]
*a[i] T [N] T *
a[i][j] T [N] T *
&a[i][j] T (*)[N]
*a[i][j] T
a[i][j][k] T
The pattern should be clear for higher-dimensional arrays.
So let's analyze your dictionary:
/* list of words and meanings */
char *dic[][40] = {
"atlas", "A volume of maps.",
"car", "A motorized vehicle.",
"telephone", "A communication device.",
"airplane", "A flying machine.",
"", "" /* null terminate the list */
};
This isn't going to set up your dictionary the way you want; you've basically set this up as a 1-element array of 40 pointers to char. If you want an array of pairs of strings, then the declaration should look like this:
char *dic[][2] =
{
{"atlas", "A volume of maps"},
{"car", "A motorized vehicle"},
{"telephone", "A communication device"},
{"airplane" , "A flying machine"},
{NULL, NULL} // empty strings and NULLs are different things.
};
The type of dic is "5-element array of 2-element arrays of pointer to char", or char *[5][2]. Going by the rules above, the expression dic should decay to char *(*)[2] -- a pointer to a 2-element array of pointer to char.
A function to search this dictionary would then look like this:
char *definition(char *term, char *(*dictionary)[2]) // *NOT* char ***dictionary
{
while ((*dictionary)[0] != NULL && strcmp((*dictionary)[0], term) != 0)
dictionary++;
return (*dictionary)[1];
}
and you would call it from your main function like
char *def = definition(term, dic);
Note that we have to use parentheses around the *dictionary expression in the function. The array subscript operator [] has higher precedence than the dereference operator *, and we don't want to subscript into dictionary directly, we want to subscript into the array that dictionary points to.
I understood multi-dimensional arrays as pointers to pointers, but perhaps I am wrong?
Yes, you are wrong. There is a difference between an array and a pointer. An array can decay into a pointer, but a pointer doesn't carry state about the size or configuration of the array to which it points. Don't confuse this automatic decay with the idea that arrays and pointers are the same -- they are not.
A char ** is a pointer to a memory block containing character pointers, which themselves point to memory blocks of characters. A char [][] is a single memory block which contains characters.
If you have a char ** and access it using ptr[x][y], the compiler changes that into *(*(ptr + x)+y). If you have a char [][], the compiler changes arr[x][y] into *(ptr + rowLength*y + x). (Note: I'm not 110% positive on the order of Xs and Ys here, but that doesn't matter for the point I'm making here) Note that given the pointer, the compiler doesn't know anything about the size or dimensions of the array, and cannot determine the actual address if you treat the pointer as a multidimensional array.
char *dic[][40] is an array of arrays of size forty, which contain character pointers. Therefore it doesn't match your assignment there at all.
p = (char **)dic; <-- This is why casts are bad. The compiler was telling you that what you really wanted to do to dic didn't make any sense. But since you can cast a pointer to any other pointer, the cast succeeds, even though trying to read the data that way will result in undefined behavior.
You need to refer to 'right left rule'. Alternatively you can deciper most of the C-ish declarations at here
So,
char *p[2][3] is parsed as
p is an array of 2 elements where each element is an array of 3 elements, such that each element is a pointer to a character.([] binds stronger than *)
char (*p)[2][3] is parsed as
"p is a pointer to a 2 element char array where each element is a char array of 3 elements." (parenthesis binds the strongest)
Not looked in too much detail but I think the author is relying upon c laying out a 2d string array like this:
key, value, key, value, key, value in contiguous memory. Then traversing this array as a 1d array of strings p = (char **)dic;
This is one of the beauties and potential problems with C - it has lots of low level power but the you have protect yourself with decent code to prevent side effects.
One of my memorization rules for combinations of * and [] is the signature of main. Works! :-)
Your dic is an array of 40-element arrays, each element of which is a pointer to char.
#include <iostream>
#include <typeinfo>
using namespace std;
template< class Type, unsigned N >
void tellMeAbout( Type const (&)[N] )
{
cout << "Well, it's an array of " << typeid( Type ).name() << ".\n";
}
int main()
{
char *dic[][40] = { 0 };
tellMeAbout( dic );
}
Using Visual C++ I get ...
Well, it's an array of char * [40].
Cheers & hth.,
– Alf