tags:

views:

489

answers:

5
char **Data[70]={NULL};

What is the correct terminology for this? How else could it be written? What does it look like in memory? I am reading many tutorials on pointers but I don't see it in this syntax. Any help is appreciated. Thanks.

A: 

This is, effectively, a pointer to a pointer to pointers. However, since a "pointer" is nothing but a location in memory, there really isn't much use in doing this over just doing char* Data[70], other than making it obvious that each char* is a pointer to another char*, instead of a pointer to char.

Reed Copsey
+20  A: 

This structure

char **Data[70]={NULL};

is an array of 70 pointers to pointers to char. The compiler allocates 70 * sizeof(char**) bytes for this array, which assuming 32-bit pointers is 280 bytes.

If you internally think of a "pointer to char" as a string, which isn't true but it's close enough, then this is an array of 70 pointers to pointers to strings. To make some ASCII art and pretend that you have allocated and filled some values....

 Array of        One or more
 char **           char *
+---------+     +---------+
|    0    | --> |   ptr   | -->  "Hello, world"
+---------+     +---------+
|    1    |
+---------+       +---------+
|    2    | ----> |  ptr2   | -->  "Goodbye, cruel world"
+---------+       +---------+
|    3    |
+---------+         +---------+
|    4    | ------> | ptr3[0] | -->  "Message 0"
+---------+         +---------+
    ...             | ptr3[1] | -->  "Message 1"
+---------+         +---------+
|   69    |         | ptr3[2] | -->  "Message 2"
+---------+         +---------+

You could do the above with code like this (error checking malloc return values skipped):

char **Data[70]={NULL};
char **ptr, **ptr2, **ptr3;

ptr = (char **) malloc(sizeof(char *));
*ptr = "Hello, world";
Data[0] = ptr;

ptr2 = (char **) malloc(sizeof(char *));
*ptr2 = "Goodbye, cruel world";
Data[2] = ptr2;

ptr3 = (char **) malloc(10 * sizeof(char *));
Data[4] = ptr3;

ptr3[0] = "Message 0";
ptr3[1] = "Message 1";
 ...
ptr3[9] = "Message 9"; 

printf("%s\n", *Data[0]);
printf("%s\n", Data[2][0]);
printf("%s\n", Data[4][0]);
printf("%s\n", Data[4][1]);
      ...
printf("%s\n", Data[4][9]);

Think of it this way: Each entry in the array is a char **. Each entry can point to an arbitrary location in memory, said location(s) being char * and thus being able to point to a null-terminated character array aka "string."

Note carefully the distinction between this and what you get when you allocate a 2D array:

char *Data2[10][70]={NULL};

The allocation of Data2 above gives you a 2-dimensional array of char * pointers, said 2-d array being allocated in a single chunk of memory (10 * 70 * sizeof(char*) bytes, or 2800 bytes with 32-bit pointers). You don't have the ability to assign the char ** pointers to arbitrary locations in memory that you have with the single-dimensional array of char ** pointers.

Also note (given above declarations of Data and Data2) that the compiler will generate different code for the following array references:

Data[0][0]
Data2[0][0]

Here's another way to think about this: Imagine that you have several arrays of pointers to strings:

char *table0[] = { "Tree", "Bench", "Stream" };
char *table1[] = { "Cow", "Dog", "Cat" };
char *table2[] = { "Banana", "Carrot", "Broccoli" };
char **Data[3];

Data[0] = table0;
Data[1] = table1;
Data[2] = table2;

You have an array of pointers to "array of pointer to char". If you now print the value of data[1][1], think of it like this: data[1] gets you a pointer to the array table1. Then the value table1[1] equals "Dog".

Eddie
your great answer have shamelessly been linked to from this pet-peeve-answer: http://stackoverflow.com/questions/423823/whats-your-favorite-programmer-ignorance-pet-peeve/484900#484900 . cheers :)
Johannes Schaub - litb
thanks for taking the time to write such a good answer. That clears up a lot. As far as your first diagram, how do you know if the pointer is pointing to a single pointer (like ptr2) or an array of pointers, like ptr3[0-2] Can it be both as in the diagram? For example, your second diagram is consistent. Thanks.
Tommy
There is no way to know what, exactly, a char** pointer points to. It could point to a memory area of any size at all. Without looking at code where it is assigned, there is no way to know.
Eddie
+1  A: 

This isn't very obvious:

char **Data[70]={NULL};

but with an alternative declaration, like:

char* Data[2][3] = {
  {"Nick", "Tom", "Helen"},
  {"one", "two", "three"}
};

we can easily see that it's a 2 dimensional array of strings.

Edit: I used Data[2][3] to show that it's a 2D array. I used fixed size for the dimensions like 2 & 3 just for demonstration. Of course we can have:

char* Data[][3]={
  {"Nick", "Tom", "Helen"},
  {"one", "two", "three"},
  // ...
};

or char** Data[]

Ok, here is what I mean by 2-D array:

char** Data[2]={0};

void test()
{
  char* d1[] = {"1", "2"};
  char* d2[] = {"10", "20", "30"};
  Data[0] = d1;
  Data[1] = d2;

  printf("%s\n", Data[0][0]);
  printf("%s\n", Data[0][1]);
  printf("%s\n", Data[1][0]);
  printf("%s\n", Data[1][1]);
  printf("%s\n", Data[1][2]);
}
Nick D
It isn't actually a 2-D array of strings. Your declaration gives you something different than his does.
Eddie
So it's not a 2d array, of pointers?
Tommy
What's the difference?
Nick D
A 2-D array of pointers is just m*n pointers, and Data[1][1] is understood by the compiler to refer to the 1*m + 1 th of those. This is why the last, and only the last, dimension of a multi-dimensional array, is "lost" when passed through a function signature. A char** is a completely different thing - even if it does represent an array of arrays, it needn't be "rectangular". You could have a pointer to a 3-element array in the first slot, and a pointer to a 9-element array in the second.
Steve Jessop
The difference is that a 2-D array is an array of arrays. That means that all its memory is contiguous stored. I.e, a[0][4] is in the same memory blop as a[1][4] if data were a 2-D array. But with his Data, Data[0][4] can be on a whole other planet than Data[1][4]. Thus, things like memset work on a 2-D array as a whole, but can't work on a pointer-to-pointer linked data-structure.
Johannes Schaub - litb
So, if you have a char** foo and a char[3]* bar, then foo[1][2] adds 4 bytes (or however many bytes in a pointer) to foo, then dereferences, then adds 8 bytes, dereferences again. bar[1][2] adds 20 bytes to bar and dereferences.
Steve Jessop
char *Data[][3] is still a very different beast from char **Data[70]. Its memory layout is entirely different. This confusion is the source of many bugs among beginner and even intermediate C programmers.
Eddie
Look at my answer to see what the difference is. With char **Data[70], you can point your 70 char** pointers to any arbitrary locations in memory. When you allocate **Data[70], the compiler allocates 70 * sizeof(char **) bytes and that's it. Each of those 70 areas, you can assign a char ** pointer. When you allocate *Data[10][70], the compiler allocates 10 * 70 * sizeof(char *) bytes. For each of those 700 array locations, you can assign a char * pointer.
Eddie
Anyway, erasing the 2 out of [2] didn't change anything :) after the definition, the type of "Data" will still be "char*[2][3]" . The erasure of the number just tells the compiler "count the number of elements in the initializer yourself" :)
Johannes Schaub - litb
My point is that, it's about a 2-D array (not 2-D matrix), where dimensions degree can be fixed or variable. Of course you are right. Tommy asked for variable size arrays, and I should had mentioned their differences.
Nick D
@Nick D, it is *NOT* a 2-D array! It is a 1-D array. Seriously. char **Data[70] is a 1-D array of pointers to which you can assign any value, but the values must be a "char **". Your code samples show char *d1[], not char **d1[]. Try writing some code and you'll see what I mean.
Eddie
The only reason I call it a 2-D array is that "TYPE*" is also *sometimes* a way to declare arrays. Aka, TYPE*[ ] is like TYPE [ ][ ]. And, YES, it's just an 1-D array of pointers! I agree with you :)
Nick D
TYPE* never declares an array. It declares a pointer to TYPE. TYPE*[] is also not like TYPE[][]: While TYPE*[] is an incomplete array type, TYPE[][] is an invalid type. and TYPE*[N] is a one-dimensional array of pointer to TYPE, while TYPE[N][M] is a two dimensional array of TYPE. That's very different, even though many people don't like the distinction and say they are the same :)
Johannes Schaub - litb
@litb, I know it very well :) I said "like" an array. For example, we *often* treat "char*" like an array of chars, right? I prefer to work in a higher level of abstraction. I "poke" with the actual implementation only when there is a need. BTW, I asked "What's the difference?" in purpose :)
Nick D
+2  A: 

It's slightly tricky to think of a practical use for an array of char**. Especially one with 70 elements.

However, suppose that I'm going to run 70 programs. As you probably know, program arguments are commonly passed as a char** argv parameter to main() (or char*[] argv, which in a function signature is the same thing). So if I wanted to store the argv pointers for all those programs, I'd used an array just like Data. Obviously I'd also need some more memory elsewhere, for the actual strings and argv arrays to occupy, but it's a start.

Initializing an array with {NULL} sets all its elements to NULL. This is a useful shorthand: you can initialize an array with {firstelement, secondelement, ...}, but if you don't provide enough terms, all the rest are treated as 0.

Just like any other array of pointers, initialized with {NULL}, what this looks like in memory is 70 NULL pointers sitting in a row. There's (usually) no difference in memory between a char** and any other object pointer. I think it is legal to write a weird implementation in which there is a difference, but don't hold your breath waiting to come across one.

So, the difference between 70 NULL char** in a row and 70 NULL char* in a row is what would be on the other end of the pointer, if they weren't NULL. On the other end of a char** is a pointer to a char. On the other end of a char* is a char. Either the pointer, or the char, might be the first in an array, depending on how it's being used.

Steve Jessop
+1  A: 

What you've got is an array of 70 pointers, each of which points to another pointer, each of those pointers point to a char. On an interesting note, arrays themselves are pointers so you have three levels of pointers.

indyK1ng
Arrays are NOT pointers.
Tim Ring