Difference between char **p,char *p[],char p[][]
Some more differences description looking it from memory addressing view as follows,
I. char **p;
p
is double pointer of type char
Declaration:
char a = 'g';
char *b = &a;
char **p = &b;
p b a
+------+ +------+ +------+
| | | | | |
|0x2000|------------>|0x1000|------------>| g |
| | | | | |
+------+ +------+ +------+
0x3000 0x2000 0x1000
Figure 1: Typical memory layout assumption
In above declaration, a
is char
type containing a character g
. Pointer b
contains the address of an existing character variable a
. Now b
is address 0x1000
and *b
is character g
. Finally address of b
is assigned to p
, therefore a
is a character variable, b
is pointer and p
is pointer to pointer. Which implies a
contains value, b
contains address and p
contains address of address as shown below in the diagram.
Here, sizeof(p) = sizeof(char *)
on respective system;
II. char *p[M];
p
is array of strings
Declaration:
char *p[] = {"Monday", "Tuesday", "Wednesday"};
p
+------+
| p[0] | +----------+
0 | 0x100|------>| Monday\0 |
| | +----------+
|------| 0x100
| p[1] | +-----------+
1 | 0x200|------>| Tuesday\0 |
| | +-----------+
|------| 0x200
| p[2] | +-------------+
2 | 0x300|------>| Wednesday\0 |
| | +-------------+
+------+ 0x300
Figure 2: Typical memory layout assumption
In this declaration, p
is array of 3 pointers of type char
. Implies array p
can hold 3 strings. Each string (Monday, Tuesday & Wednesday)
is located some where in memory (0x100, 0x200 & 0x300)
, there addresses are in array p
as (p[0], p[1] & p[2])
respectively. Hence it is array of pointers.
Notes: char *p[3];
1. p[0], p[1] & p[2] are addresses of strings of type `char *`.
2. p, p+1 & p+2 are address of address with type being `char **`.
3. Accessing elements is through, p[i][j] is char; p[i] is char *; & p is char **
Here sizeof(p) = Number of char array * sizeof(char *)
III. char p[M][N];
p
is array of fixed length strings with dimensions as M x N
Declaration:
char p[][10] = {Monday, Tuesday, Wednesday};
p 0x1 2 3 4 5 6 7 8 9 10
+-------------------------+
0 | M o n d a y \0 \0 \0 \0|
1 | T u e s d a y \0 \0 \0|
2 | W e d n e s d a y \0|
+-------------------------+
Figure 3: Typical memory layout assumption
In this case array p
contain 3 strings each containing 10 characters. Form the memory layout we can say p
is a two dimensional array of characters with size MxN
, which is 3x10
in our example. This is useful for representing strings of equal length since there is a possibility of memory wastage when strings contains lesser than 10 characters compared to declaration char *p[]
, which has no memory wastage because string length is not specified and it is useful for representing strings of unequal length.
Accessing elements is similar as above case, p[M]
is M'th string & p[M][N] is N'th character of M'th string.
Here sizeof(p) = (M rows * N columns) * sizeof(char)
of two dimensional array;
Normal Declarations (Not Function Parameters)
char **p;
declares a pointer to a pointer to char
. It reserves space for the pointer. It does not reserve any space for the pointed-to pointers or any char
.
char *p[N];
declares an array of N pointers to char
. It reserves space for N pointers. It does not reserve any space for any char
. N
must be provided explicitly or, in a definition with initializers, implicitly by letting the compiler count the initializers.
char p[M][N];
declares an array of M arrays of N char
. It reserves space for M•N char
. There are no pointers involved. N
must be provided explicitly. M
must be provided explicitly or, in a definition with initializers, implicitly by letting the compiler count the initializers.
Declarations in Function Parameters
char **p
declares a pointer to a pointer to char
. When the function is called, space is provided for that pointer (typically on a stack or in a processor register). No space is reserved for the pointed-to-pointers or any char
.
char *p[N]
is adjusted to be char **p
, so it is the same as above. The value of N
is ignored, and N
may be absent. (Some compilers may evaluate N
, so, if it is an expression with side effects, such as printf("Hello, world.\n")
, these effects may occur when the function is called. The C standard is unclear on this.)
char p[M][N]
is adjusted to be char (*p)[N]
, so it is a pointer to an array of N char
. The value of M
is ignored, and M
may be absent. N
must be provided. When the function is called, space is provided for the pointer (typically on a stack or in a processor register). No space is reserved for the array of N char
.
argv
argv
is created by the special software that calls main
. It is filled with data that the software obtains from the “environment”. You are allowed to modify the char
data in it.
In your definition char *p = "some string";
, you are not permitted to modify the data that p
points to because the C standard says that characters in a string literal may not be modified. (Technically, what it says is that it does not define the behavior if you try.) In this definition, p
is not an array; it is a pointer to the first char
in an array, and those char
are inside a string literal, and you are not permitted to modify the contents of a string literal.
In your definition char p[] = "some string";
, you may modify the contents of p
. They are not a string literal. In this case, the string literal effectively does not exist at run-time; it is only something used to specify how the array p
is initialized. Once p
is initialized, you may modify it.
The data set up for argv
is set up in a way that allows you to modify it (because the C standard specifies this).