tags:

views:

346

answers:

3

For pointers, I'm getting confused with declarations and function parameters on when to use char ** or char * or *array[n], etc. Like if a function takes a (*array[n]) parameter, do I pass it a **type?

I try using the Right-Left rule and know that p would be a pointer to a pointer to a char (char **p), and p is an array of n pointers (*p[n]), but someone said that *p[n] and **p are essentially equivalent. Is that true?

A: 

If n==0 then they reference the same memory. Array indexing is basically a pointer plus an offset. *(p[n]) would be the same as **(p+n). You can see for yourself how simple this is in C, because array[4] and 4[array] will give you the same thing.

David Kanarek
+2  A: 

Reading C declarators (that's the part of the variable with the * and []) is fairly nuanced. There are some websites with tips:

A char** is a pointer to (possible multiple) pointer(s) to (possibly multiple) char(s). For example, it might be a pointer to a string pointer, or a pointer to an array of string pointers.

A char*[] is an array of pointers to char. When you have a function that takes this as a parameter, the C compiler makes it "decay" into a char**. This only happens to the first layer... so, taking a complicated example, char*[4][] becomes char*(*)[4]. Read the links above so you can understand what the heck that means.

Or you can do a (very sensible) thing and make a bunch of typedefs. I don't do this, but until you're good at reading declarators, it's a good idea.

typedef char * stringp;
void func(stringp array[]) { ... }
static stringp FOUR_STRINGS[4] = { ... };
Dietrich Epp
Also, if you're not the only person reading your code, the extra typedefs can be good for other peoples sanity too - but don't overdo it, of course, as clutter and fragmentation of relevant information (having to combine information from lots of places in the source to decode what one construct means) are also enemies of readability.
Steve314
+2  A: 

In the correct context (namely, arguments to a function), then the following declarations are equivalent:

int main(int argc, char *argv[]);
int main(int argc, char **argv);
int main(int argc, char *argv[12]);  // Very aconventional!

Similar comments apply to the function definitions (which have a block enclosed in braces in place of the semi-colon).

In any other context, there are important differences between the notations. For example:

extern char *list1[];
extern char **list2;
extern char *list3[12];

The first says that somewhere there is an array of indeterminate size containing 'char *' values. The second says that somewhere - possibly here - there is a single value containing a pointer to a char pointer. The third says that somewhere - possibly here - there is an array of 12 character pointers.

However, all the three lists can be referenced in somewhat the same way - assuming that they actually have been defined and initialized.

list1[0][0] = '1';
list2[0][0] = '2';
list3[0][0] = '3';

Further, if they are passed into a function like this:

function(list1, list2, list3);

then the function can be declared as:

void function(char **list1, char **list2, char **list3);

The arrays (list1, list3) decay from the array to the pointer to the first element of the array; list2, of course, is already a pointer to a pointer.

One detail to note in a function such as:

void otherfunction(char *list[12])
{
    ...
}

The C compiler does not treat that declaration any differently from:

void otherfunction(char **list)
{
    ...
}

or

void otherfunction(char *list[])
{
    ...
}

In particular, it does no array bounds checking, and as far as the function is concerned, the 12 may as well be absent.


C99 introduces VLA (variable length array) types and also introduces a notation with 'static' and a size in the array bounds. You would need to read the standard to understand those fully.

Suffice to say in a function like the following the size of the array does matter, and is determined at run-time. With two-dimensional arrays in general, all the dimensions except the first need to be specified.

void vla_function(size_t m, int vla[m][m]);

Quoting from the standard (section 6.7.5.3):

void f(double (* restrict a)[5]);
void f(double a[restrict][5]);
void f(double a[restrict 3][5]);
void f(double a[restrict static 3][5]);

(Note that the last declaration also specifies that the argument corresponding to a in any call to f must be a non-null pointer to the first of at least three arrays of 5 doubles, which the others do not.)

Jonathan Leffler