Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pointer / Array syntax (char **p, *p[n]) in C/C++

Tags:

c

pointers

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?

like image 673
Crystal Avatar asked Dec 12 '22 23:12

Crystal


2 Answers

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.)

like image 187
Jonathan Leffler Avatar answered Dec 23 '22 13:12

Jonathan Leffler


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

  • http://www.antlr.org/wiki/display/CS652/How+To+Read+C+Declarations
  • http://www.ericgiguere.com/articles/reading-c-declarations.html

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] = { ... };
like image 30
Dietrich Epp Avatar answered Dec 23 '22 11:12

Dietrich Epp