I am studying pointers, but I have been stumped by the example program below. It is supposed to be doing a conversion of char** to char*, but I don't understand the logic behind the program. What is the program doing?
#include <iostream>
using namespace std;
int main() {
char *notes[] = {"cpp","python","java","mariadb"};
void * base = notes; // notes and base, holds the address of note's first element
void * elemAddr = (char*) base + 3* sizeof(char *); // i didn't understand this line???
cout << *(char **)elemAddr; // and this line
return 0;
}
These lines:
char *notes[] = {"cpp","python","java","mariadb"};
void * base = notes;
void * elemAddr = (char*) base + 3* sizeof(char *);
cout << *(char **)elemAddr;
are an obfuscated equivalent of:
char *notes[] = {"cpp","python","java","mariadb"};
cout << notes[3];
Explanation:
void * base = notes;
void * elemAddr = (char*) base + 3* sizeof(char *);
is the same as:
char * base = (char*)notes;
char * elemAddr = base + 3 * sizeof(char *);
Since pointers are usually of the same size, those lines are kind of the same as:
char ** base = notes;
char ** elemAddr = base + 3;
which makes elemAddr == ¬es[3]. That leads to the line
cout << *(char **)elemAddr;
to be the same as
cout << notes[3];
Ok, I'll bite:
char *notes[] = {"cpp","python","java","mariadb"};
Declares an array of pointers to char *. (should actually be const char *notes[] since we can't modify the content ever)
void * base = notes; // notes and base, holds the address of note's first element
So assigns the address of the array notes to base, and losing any type information in the process.
void * elemAddr = (char*) base + 3* sizeof(char *); // i didn't understand this line???
Cast base to char *, meaning each element is now sizeof(char) == 1. Add 3 * sizeof(char *) to that pointer -> 3 elements into the notes array, and assign it back to elemAddr.
cout << *(char **)elemAddr; // and this line
Since elemAddr is pointing to an element in notes, which is a char*, it really is a pointer to a pointer to char, and we want to print what it point to, hence the * at the very beginning.
It is not very readable, and it would be MUCH simpler to write
const char* notes[] = { ... };
cout << notes[3];
but then you wouldn't have posted here...
Apparently the example code is meant to illustrate what goes on under the hood when you use array indexing.
Repeating the code (as it was when I wrote this):
#include <iostream>
using namespace std;
int main() {
char *notes[] = {"cpp","python","java","mariadb"};
void * base = notes; // notes and base, holds the address of note's first element
void * elemAddr = (char*) base + 3* sizeof(char *); // i didn't understand this line???
cout << *(char **)elemAddr; // and this line
return 0;
}
First, the declaration
char *notes[] = {"cpp","python","java","mariadb"};
declares an array of pointers to char. Each pointer is initialized with a string literal. This language feature was deprecated in the original C++ standard, C++98, and was finally removed in C++11, so that with modern C++ (as of this writing C++14) it's just invalid code, code that will not compile with a conforming compiler.
In standard C++ it could be
char const *notes[] = {"cpp","python","java","mariadb"};
But let's ignore the const issue, and assume C++03 or C++98.
Then the declaration
void * base = notes;
declares a void* pointer called base, initialized to the address of the first item of the array notes. This works via array expression decay, where an expression referring to an array produces a pointer to its first item.
The declaration
void * elemAddr = (char*) base + 3* sizeof(char *);
is evidently intended to illustrate what's going on behind the scenes for the [3] indexing in
auto p = & notes[3];
This works via byte oriented address arithmetic (char and its variants is the C++ notion of smallest addressable unit, a.k.a. byte). Starting with the base address of the array, one adds 3 times the size of each item. This lands you on the start of the 3'rd item.
Finally, the expression
*(char **)elemAddr
uses that item. It's just ugly due to using low level types. But essentially, the item is a char* and so the address of the item is casted to char**, and then that pointer is dereferenced, yielding the char* pointer itself, which is the result of the expression (and passed to cout).
The third line says:
"Convert base to a char pointer, and add 3 times the size of an char pointer to the result. Then, implicitly cast the result to a void pointer and store it in elemAddr"
In the end, elemAddr points to the address of "mariadb". The cast to (char*) isn't necessary to do the pointer arithmetic, but it prevents the compiler from printing a warning. It is equivalent to
¬es[3]
The fourth line tells the compiler to interpret the pointer as an pointer to char pointers. This pointer is then dereferenced so that the data pointed to by elemAddr is treated as an char pointer, which will be interpreted by std::cout as a C-string.
I don't know where you got this example from, but this type of programming is a honey pot for really horrible bugs and hard to debug code.
Let's go line by line:
1st Line: We create a pointer to an array of strings... this is a pointer to the base address of an array... of pointers.
2nd line: We make our char* into a void*, this this doesn't change the value of the pointer at all, just the type associated with it by the compiler.
3rd line: When we operate on the base pointer, we cast it to a char* essentially undoing what we did on line 2. We add 3 * the sizeof( char*) to our pointer. This makes the pointer that was pointing to the base of our array of pointers, now point to the 4th pointer in our array.
4th line: now we double deference our pointer, what is our pointer pointing to? The third element in an array of pointers. What is that pointer pointing to? The string created back on line one.
Related
What is the difference between int* i and int** i?
Pointer to an integer value
int* i
Pointer to a pointer to an integer value
int** i
(Ie, in the second case you will require two dereferrences to access the integer's value)
int* i : i is a pointer to a object of type int
int** i : i is a pointer to a pointer to a object of type int
int*** i : i is a pointer to a pointer to a pointer to object of type int
int**** i : i is a pointer to a pointer to a pointer to a pointer to object of type int
...
int* pi
pi is a pointer to an integer
int **ppi
ppi is a pointer to a pointer to an integer.
EDIT :
You need to read a good book on pointers. I recommend Pointers on C by Kenneth Reek.
Let's say you're a teacher and have to give notes to one of your students.
int note;
Well ... I meant the whole class
int *class_note; /* class_note[0]: note for Adam; class_note[1]: note for Brian; ... */
Well ... don't forget you have several classes
int **classes_notes; /* classes_notes[0][2]: note for Charles in class 0; ... */
And, you also teach at several institutions
int ***intitute_note; /* institute_note[1][1][1]: note for David in class 1 of institute 1 */
etc, etc ...
I don't think this is specific to opencv.
int *i is declaring a pointer to an int. So i stores a memory address, and C is expecting the contents of that memory address to contain an int.
int **i is declaring a pointer to... a pointer. To an int. So i contains an address, and at that memory address, C is expecting to see another pointer. That second memory address, then, is expected to hold an int.
Do note that, while you are declaring a pointer to an int, the actual int is not allocated. So it is valid to say int *i = 23, which is saying "I have a variable and I want it to point to memory address 23 which will contain an int." But if you tried to actually read or write to memory address 23, you would probably segfault, since your program doesn't "own" that chunk of RAM. *i = 100 would segfault. (The solution is to use malloc(). Or you can make it point to an existing variable, as in int j = 5; int *i = &j)
Imagine you have a few friends, one of them has to give you something (a treasure... :-)
Say john has the treasure
int treasure = 10000; // in USD, EUR or even better, in SO rep points
If you ask directly john
int john = treasure;
int you = john;
If you cannot join john, but gill knows how to contact him,
int john = treasure;
int *gill = &john;
int you = *gill;
If you cannot even join gill, but have to contact first jake who can contact gill
int john = treasure;
int *gill = &john;
int **jake = &gill;
int you = **jake;
Etc... Pointers are only indirections.
That was my last story for today before going to bed :-)
I deeply believe that a picture is worth a thousand words. Take the following example
// Finds the first integer "I" in the sequence of N integers pointed to by "A" .
// If an integer is found, the pointer pointed to by P is set to point to
// that integer.
void f(int N, int *A, int I, int **P) {
for(int i = 0; i < N; i++)
if(A[i] == I) {
// Set the pointer pointed to by P to point to the ith integer.
*P = &A[i];
return;
}
}
So in the above, A points to the first integer in the sequence of N integers. And P points to a pointer that the caller will have the pointer to the found integer stored in.
int Is[] = { 1, 2, 3 };
int *P;
f(3, &Is[0], 2, &P);
assert(*P == 2);
&P is used to pass the address of P to the function. This address has type int **, because it's the address of a pointer to int.
int* i is the address of a memory location of an integer
int** is the address of a memory location of an address of a memory location of an integer
int* i; // i is a pointer to integer. It can hold the address of a integer variable.
int** i; // i is a pointer to pointer to integer. It can hold address of a integer pointer variable.
Neither is a declaration. Declaration syntax does not allow () around the entire declaration. What are these () doing there? If this is supposed to be a part of function declaration, include the whole function declaration thing in your question, since in general case the actual meaning of a declaration might depend on that. (Not in this one though.)
As for the difference... There is one * in the first and there are two *s in the second. Does it help? Probably not. The first one declares ias a pointer to int. The second one declares i as a pointer to int *. Does this help? Probably not much either. Without a more specific question, it is hard to provide a more meaningful answer.
Provide more context, please. Or, if this is actually as specific as it can get, read your favorite C or C++ book about pointers. Such broad generic questions is not something you ask on the net.
Note that
int *i
is not fully interchangeable with
int i[]
This can be seen in that the following will compile:
int *i = new int[5];
while this will not:
int i[] = new int[5];
For the second, you have to give it a constructor list:
int i[] = {5,2,1,6,3};
You also get some checking with the [] form:
int *i = new int[5];
int *j = &(i[1]);
delete j;
compiles warning free, while:
int i[] = {0,1,2,3,4};
int j[] = {i[1]};
delete j;
will give the warnings:
warning C4156: deletion of an array expression without using the array form of 'delete'; array form substituted
warning C4154: deletion of an array expression; conversion to pointer supplied
Both of these last two examples will crash the application, but the second version (using the [] declaration type) will give a warning that you're shooting yourself in the foot.
(Win32 console C++ project, Visual studio 2010)
Textual substitution is useful here, but beware of using it blindly as it can mislead you (as in the advanced example below).
T var; // var has type T
T* var; // var has type "pointer to T"
This works no matter what T is:
int* var; // pointer to int
char* var; // pointer to char
double* var; // pointer to double
// advanced (and not pure textual substitution):
typedef int int3[3]; // confusing: int3 has type "array (of size 3) of ints"
// also known as "int[3]"
int3* var; // pointer to "array (of size 3) of ints"
// aka "pointer to int[3]"
int (*var)[3]; // same as above, note how the array type from the typedef
// gets "unwrapped" around the declaration, using parens
// because [] has higher precedence than *
// ("int* var[3];" is an array (size 3) of pointers to int)
This works when T is itself a pointer type:
typedef int* T; // T is a synonym for "pointer to int"
T* var; // pointer to T
// which means pointer to pointer to int
// same as:
int** var;
I am trying to pick up my C++; I have basic understanding of pointers and references; but when it comes to char pointer to array, it seems nothing works for me.
I have a small piece of codes here (omitted include and namespace statements), I have included my questions as comments below:
I have gone through at least 5 other questions on SO to try to understand it; but those answers didn't the answer I expected and to the extent that could help understand the actual issue there.
Could you kindly explain the problems I commented below with a bit of depth from the surface (so please don't dive into it directly)?
int main(){
// 1 this is a char pointer to a char;
char * c = new char;
*c ='A';
cout << c << endl; // this gives me memory address;
cout << *c << endl;// this gives me the value in the memory address;
// 2 this is a char array initialised to value "world";
char d[6] = "world";
cout << d[0] << endl; // this gives me the first element of char array;
// 3 this is char pointer to char array (or array of char pointers)?
char * str = new char[6];
for(int i=0;i<6;i++){ //
str[i]=d[i]; // are we assigning the memory address (not value) of respective elements here?
} // can I just do: *str = "world"; what's the difference between initialising with value
// and declaring the pointer and then assign value?
char * strr = "morning";
char b[6] = "hello";
cout << b << endl;
cout << (*str)[i] << endl; // why? error: subscripts requires array or pointer type
cout << str[1] << endl;
cout << (*strr)[1] << endl; // why? error: subscripts requires array or pointer type
}
// 1 this is a char pointer to a char;
Right.
// 2 this is a char array initialised to value "world";
Right, "world\0" is created by the compiler and is put in the read-only memory area of the program. Note that this is called a string literal. Then the string is copied over to the char array d.
// 3 this is char pointer to char array (or array of char pointers)?
That's a char pointer yes, a pointer to a single char.
// are we assigning the memory address (not value) of respective
elements here?
No, you're assigning the values of the elements. This is allowed because str[i] is the same as *(str + i) so you can use the same "array style" access with the pointer str. You're looping over the individual chars you have allocated with new and are assigning them the value of the chars in the char array d.
// why? error: subscripts requires array or pointer type
Because you already dereference str (which is pointing at the start of the 6 element char array) with * which gives you a char, then you try to use that char like an array with [1] which makes no sense. *str would give you 'w' (the first element). And str[1] would give you *(str + 1) which is 'o' (the second element), don't double up.
A small-big side note, string literals are of type const char[], not char[], they're placed in read only memory and thus they can not be altered by the program (don't write to them).
char * strr = "morning";
This is very very bad, it treats a const char[] as a char[], this has been deprecated in the standard for a while now and according to the current standard this is even illegal, yet compilers still allow it for some reason.
Because compilers allow this you could get some nasty situations like trying to modify the string literal:
char * strr = "morning";
strr[0] = 'w'; // change to "worning"
This will attempt to write to read-only memory, which is undefined behaviour and will probably/hopefully get you a segmentation fault. Long story short, use the appropriate type to have the compiler stop you before the code reaches runtime:
const char * strr = "morning";
side side note : don't forget to delete anything you allocated with new.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What does this C statement mean?
What does this expression mean?
char *(*c[10])(int **p);
c is an array of 10 function pointers that return a char* and take a int** as an argument.
(*c[10])
^^^^ = array of 10
(*c[10])
^ = function pointer
So right now we have an array of 10 function pointers.
char *(*c[10])
^^^^^^ = returns a char*
char *(*c[10])(int** p)
^^^^^ = takes a int** as an argument
Array of 10 function pointers that return a char* and take a int** as an argument.
NOTE: If you write code like this you deserve to be slapped in the face.
cdecl is a nice tool to translate C gibberish into English
$ cdecl explain 'char * (*c[10]) (int **)'
declare c as array 10 of pointer to function (pointer to pointer to int) returning pointer to char
Some examples, and a snippet at the end that uses the decl.
1. Pointer to a function:
void (*foo)(void);
Would give you a function pointer foo which takes no parameters and returns nothing.
Ex. A:
void fun_1(void)
{
...
}
foo = fun_1;
foo(); /* this would call fun_1() */
Ex. B:
char (*bar)(int);
Would give you a function pointer bar which takes 1 parameter as integer and return a char.
char fun_2(int x)
{
if (x == 50)
return 'a';
return 'Z';
}
char v;
bar = fun_2;
v = bar(50); /* this would call fun_2() with 50 as parameter and return 'a' */
2. Pointer to a pointer
int **p; is a pointer that points to a pointer of type int.
Ex. C:
int y[3] = {4, 3, 6};
int *w = &y[0];
int **z = &w;
printf("print: %d ", **z);
printf("%d ", *++(*z));
printf("%d\n", *(*z+1));
print: 4 3 6
3. Function returning a pointer
Ex. D:
char *zez(char *s)
{
s = "def";
return s;
}
char *str = "abc";
printf("%s - ", str);
printf("%s\n", zez(str));
abc - def
4. Function pointer to a function returning a pointer
Creating a function pointer to zez()
Ex. E:
char *(*ptr_zez)(char *);
ptr_zez = zez;
printf("ptr: %s - ", str);
printf("%s\n", ptr_zez(str));
ptr: abc - def
5. Array of function pointers, to function returning a char pointer, taking a char pointer
Ex. F:
char *(*c[10])(char *);
c[0] = zez;
printf("c[0]: %s - ", str);
printf("%s\n", c[0](str));
c[0]: abc - def
6. "Declare c as array 10 of pointer to function (pointer to pointer to int) returning pointer to char"
char *cumlade(int **p)
{
char *c;
int i;
if ((c = malloc(sizeof(char) * 7)) == NULL) {
fprintf(stderr, "Unable to reserve 7 bytes\n");
exit(0);
}
for (i = 0; i < 6; ++i) {
c[i] = (unsigned char)*(*p+i);
}
c[6] = '\0';
return c;
}
int main(void)
{
int t[3][3] = {{97 ,98, 99}, {100, 101, 102}};
int *u = &t[0][0];
int **v = &u;
char *ex;
char *(*c[10])(int **p); /* <-- the fun */
c[0] = cumlade;
c[1] = cumlade;
ex = c[0](v);
printf("EX: %s\n", ex);
free(ex);
ex = c[1](v);
printf("AX: %s\n", ex);
free(ex);
return 0;
}
EX: abcdef
AX: abcdef
c is an array of 10 pointers to functions taking a pointer to pointer to int as its parameter and returning a pointer to char.
Type declaration involves three operators: array [SIZE], pointer * and function (type1 param1, type2 param2, ...). Remember that all the three operators are right-associative.
char *(*c[10])(int **p);
Let's add more parenthesis to make the associativity more clear.
char *((*(c[10]))(int *(*p)))
Start from c, the variable.
c[10] means "c is an array of 10 elements, but each element is a ..."
Then see the * beside it. *(c[10]) means "c is an array of 10 elements, each element is a pointer pointing to ..."
Then (*(c[10]))(int *(*p)) means "c is an array of 10 elements, each element is a pointer to a function, which returns ..." Using similar methods we see the function takes one parameter which is "a pointer to a pointer to an int".
Then *((*(c[10]))(int *(*p))) means "c is an array of 10 elements, each element is a pointer to a function, which returns a pointer to a ..."
Finally char *((*(c[10]))(int *(*p))) means "c is an array of 10 elements, each element is a pointer to a function, which returns a pointer to a char". That's it.
I find the Clockwise/Spiral Rule very useful. See http://c-faq.com/decl/spiral.anderson.html
But I'd rather add more brackets than using spirals.
ok now the answer you have, it is an array of function pointer, but is there clean(er) way to write code like this? Yes there is, and I am sure this code might be understood at the first glance:
typedef char *(*weirdFuncPtr)(int **p);
weirdFuncPtr funcPtrArray[10];
Btw. usually I avoid typdefs - I use them when declaring function pointers though. This makes it easier to understand this sort of C code (C is an abbreviation for Cryptic, is it?)
It declares an array of function pointers. There are 10 elements in the array (from c[10] part of the declaration). the function to which these pointers can point will return char* and takes only one parameter i.e pointer to pointer to integer (int **p)
Take a look at the first answer to this question How can I use an array of function pointers?
there you will find another example of declaring function-pointer array and it may end your confusion.
If you are looking for intuitive explanation for this,
http://www.geeksforgeeks.org/archives/16841
They explained this using postfix order evaluation, just like expression evaluation.
What is the difference between int* i and int** i?
Pointer to an integer value
int* i
Pointer to a pointer to an integer value
int** i
(Ie, in the second case you will require two dereferrences to access the integer's value)
int* i : i is a pointer to a object of type int
int** i : i is a pointer to a pointer to a object of type int
int*** i : i is a pointer to a pointer to a pointer to object of type int
int**** i : i is a pointer to a pointer to a pointer to a pointer to object of type int
...
int* pi
pi is a pointer to an integer
int **ppi
ppi is a pointer to a pointer to an integer.
EDIT :
You need to read a good book on pointers. I recommend Pointers on C by Kenneth Reek.
Let's say you're a teacher and have to give notes to one of your students.
int note;
Well ... I meant the whole class
int *class_note; /* class_note[0]: note for Adam; class_note[1]: note for Brian; ... */
Well ... don't forget you have several classes
int **classes_notes; /* classes_notes[0][2]: note for Charles in class 0; ... */
And, you also teach at several institutions
int ***intitute_note; /* institute_note[1][1][1]: note for David in class 1 of institute 1 */
etc, etc ...
I don't think this is specific to opencv.
int *i is declaring a pointer to an int. So i stores a memory address, and C is expecting the contents of that memory address to contain an int.
int **i is declaring a pointer to... a pointer. To an int. So i contains an address, and at that memory address, C is expecting to see another pointer. That second memory address, then, is expected to hold an int.
Do note that, while you are declaring a pointer to an int, the actual int is not allocated. So it is valid to say int *i = 23, which is saying "I have a variable and I want it to point to memory address 23 which will contain an int." But if you tried to actually read or write to memory address 23, you would probably segfault, since your program doesn't "own" that chunk of RAM. *i = 100 would segfault. (The solution is to use malloc(). Or you can make it point to an existing variable, as in int j = 5; int *i = &j)
Imagine you have a few friends, one of them has to give you something (a treasure... :-)
Say john has the treasure
int treasure = 10000; // in USD, EUR or even better, in SO rep points
If you ask directly john
int john = treasure;
int you = john;
If you cannot join john, but gill knows how to contact him,
int john = treasure;
int *gill = &john;
int you = *gill;
If you cannot even join gill, but have to contact first jake who can contact gill
int john = treasure;
int *gill = &john;
int **jake = &gill;
int you = **jake;
Etc... Pointers are only indirections.
That was my last story for today before going to bed :-)
I deeply believe that a picture is worth a thousand words. Take the following example
// Finds the first integer "I" in the sequence of N integers pointed to by "A" .
// If an integer is found, the pointer pointed to by P is set to point to
// that integer.
void f(int N, int *A, int I, int **P) {
for(int i = 0; i < N; i++)
if(A[i] == I) {
// Set the pointer pointed to by P to point to the ith integer.
*P = &A[i];
return;
}
}
So in the above, A points to the first integer in the sequence of N integers. And P points to a pointer that the caller will have the pointer to the found integer stored in.
int Is[] = { 1, 2, 3 };
int *P;
f(3, &Is[0], 2, &P);
assert(*P == 2);
&P is used to pass the address of P to the function. This address has type int **, because it's the address of a pointer to int.
int* i is the address of a memory location of an integer
int** is the address of a memory location of an address of a memory location of an integer
int* i; // i is a pointer to integer. It can hold the address of a integer variable.
int** i; // i is a pointer to pointer to integer. It can hold address of a integer pointer variable.
Neither is a declaration. Declaration syntax does not allow () around the entire declaration. What are these () doing there? If this is supposed to be a part of function declaration, include the whole function declaration thing in your question, since in general case the actual meaning of a declaration might depend on that. (Not in this one though.)
As for the difference... There is one * in the first and there are two *s in the second. Does it help? Probably not. The first one declares ias a pointer to int. The second one declares i as a pointer to int *. Does this help? Probably not much either. Without a more specific question, it is hard to provide a more meaningful answer.
Provide more context, please. Or, if this is actually as specific as it can get, read your favorite C or C++ book about pointers. Such broad generic questions is not something you ask on the net.
Note that
int *i
is not fully interchangeable with
int i[]
This can be seen in that the following will compile:
int *i = new int[5];
while this will not:
int i[] = new int[5];
For the second, you have to give it a constructor list:
int i[] = {5,2,1,6,3};
You also get some checking with the [] form:
int *i = new int[5];
int *j = &(i[1]);
delete j;
compiles warning free, while:
int i[] = {0,1,2,3,4};
int j[] = {i[1]};
delete j;
will give the warnings:
warning C4156: deletion of an array expression without using the array form of 'delete'; array form substituted
warning C4154: deletion of an array expression; conversion to pointer supplied
Both of these last two examples will crash the application, but the second version (using the [] declaration type) will give a warning that you're shooting yourself in the foot.
(Win32 console C++ project, Visual studio 2010)
Textual substitution is useful here, but beware of using it blindly as it can mislead you (as in the advanced example below).
T var; // var has type T
T* var; // var has type "pointer to T"
This works no matter what T is:
int* var; // pointer to int
char* var; // pointer to char
double* var; // pointer to double
// advanced (and not pure textual substitution):
typedef int int3[3]; // confusing: int3 has type "array (of size 3) of ints"
// also known as "int[3]"
int3* var; // pointer to "array (of size 3) of ints"
// aka "pointer to int[3]"
int (*var)[3]; // same as above, note how the array type from the typedef
// gets "unwrapped" around the declaration, using parens
// because [] has higher precedence than *
// ("int* var[3];" is an array (size 3) of pointers to int)
This works when T is itself a pointer type:
typedef int* T; // T is a synonym for "pointer to int"
T* var; // pointer to T
// which means pointer to pointer to int
// same as:
int** var;
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