views:

179

answers:

5

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;

}
A: 

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.

Preet Sangha
+3  A: 

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

Alf P. Steinbach
That code is not valid. `typeid` is only required to be valid on polymorphic types. `char *[40]` is not polymorphic. Just because it works in MSVC doesn't mean it's standards compliant. Also, the question is tagged with both C and C++, but the OP's code is C, not C++, so it probably would have been better to post a C solution.
Billy ONeal
Alf P. Steinbach
@Alf: C++0x is not yet a standard.
Billy ONeal
@Billy: It's in about the same place in C++98. Argh, you force me to mount an unreliable USB-drive! OK, it's defined by C++98 §5.2.8/3. Huh, talk about coincidence! :-) By the way, Billy, was it you who scored my answer down? Then how about scoring it up again? ;-)
Alf P. Steinbach
@Alf: It's absolutely **not** valid in C++98. At least, `std::type_info::name` isn't required to be anything sensible.
Billy ONeal
Alf P. Steinbach
THe problem is that the array notation has a different meaning if it's in a function argument list. a local variable `char *arg[10];` is a different type than the `arg` in `void func(char *arg[10]);`
nos
@nos: yes, I should have mentioned that. And it's a bit more general than that. In both C and C++ the array type decays to pointer to first element in any context where a pointer is required, e.g. writing `a+0`. In C++ also function declarations decay to pointers when used as formal argument type (I don't know whether C supports that syntax). And in C++ you can avoid the array-to-pointer type decay for a formal argument by passing an array by reference, as in the `tellMeAbout` :-) formal argument above. Cheers,
Alf P. Steinbach
+4  A: 

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.

Billy ONeal
+4  A: 

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)

Chubsdad
A: 
John Bode