In C/C++, Is Char* Arrayname[][] a Pointer to a Pointer to a Pointer or a Pointer to a Pointer

In C/C++, is char* arrayName[][] a pointer to a pointer to a pointer OR a pointer to a pointer?

The rule for C is as follows:

6.3.2.1 Lvalues, arrays, and function designators
...

3 Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

The language for C++ is a little different:

4.2 Array-to-pointer conversion [conv.array]
1 An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to an rvalue of type “pointer to T”. The result is a pointer to the first element of the array.

...

8.3.4 Arrays [dcl.array]
...

7 A consistent rule is followed for multidimensional arrays. If E is an n-dimensional array of rank i × j × ... × k, then E appearing in an expression is converted to a pointer to an (n−1)-dimensional array with rank j × ... × k. If the * operator, either explicitly or implicitly as a result of subscripting, is applied to this pointer, the result is the pointed-to (n−1)-dimensional array, which itself is immediately converted into a pointer.

So the following all hold true:

Declaration        Expression        Type             Decays to
----------- ---------- ---- ---------
T a[N] a T [N] T *
&a T (*)[N]
*a T
a[i] T

T a[M][N] a T [M][N] T (*)[N]
&a T (*)[M][N]
*a T [N] T *
a[i] T [N] T *
&a[i] T (*)[N]
*a[i] T
a[i][j] T

T a[M][N][O] a T [M][N][O] T (*)[M][N]
&a T (*)[M][N][O]
*a T [M][N] T (*)[N]
a[i] T [M][N] T (*)[N]
&a[i] T (*)[M][N]
*a[i] T [N] T *
a[i][j] T [N] T *
&a[i][j] T (*)[N]
*a[i][j] T
a[i][j][k] T

The pattern should be clear for higher-dimensional arrays.

So let's analyze your dictionary:

/* 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 */
};

This isn't going to set up your dictionary the way you want; you've basically set this up as a 1-element array of 40 pointers to char. If you want an array of pairs of strings, then the declaration should look like this:

char *dic[][2] = 
{
{"atlas", "A volume of maps"},
{"car", "A motorized vehicle"},
{"telephone", "A communication device"},
{"airplane" , "A flying machine"},
{NULL, NULL} // empty strings and NULLs are different things.
};

The type of dic is "5-element array of 2-element arrays of pointer to char", or char *[5][2]. Going by the rules above, the expression dic should decay to char *(*)[2] -- a pointer to a 2-element array of pointer to char.

A function to search this dictionary would then look like this:

char *definition(char *term, char *(*dictionary)[2]) // *NOT* char ***dictionary
{
while ((*dictionary)[0] != NULL && strcmp((*dictionary)[0], term) != 0)
dictionary++;
return (*dictionary)[1];
}

and you would call it from your main function like

char *def = definition(term, dic);

Note that we have to use parentheses around the *dictionary expression in the function. The array subscript operator [] has higher precedence than the dereference operator *, and we don't want to subscript into dictionary directly, we want to subscript into the array that dictionary points to.

Pointer pointing to char type array can be used to show the content of the array without a dereference operator?

Both specifiers %p and %s expect a pointer.

%p will print the value of the pointer.

But %s will use the pointer to print a C string, that is an array of char delimited by '\0'.

If you give *string2, it is not a pointer. It is the value of the object the pointer points to, in your case a char.

Raise the warning level of your compiler to the maximum, and hope that it is smart enough to recognize format strings and to check the arguments.

C pointer to an array of string and array name disambiguation

No. Array is not a constant pointer in C. It is dead wrong.

Array decays into pointer to first element in most cases. That doesn't mean it is a pointer.

Constant thing is coming in your mind because array is non-modifiable lvalue. So you can't use it as one where it is being modified.

From §6.3.2.1p3

Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ''array of type'' is converted to an expression with type ''pointer to type'' that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

This explains what happened - balance in that statement is converted into pointer to the first element. The first element is double and a pointer to it is double* and that pointer contains address of balance[0].

This paragraph also points out why the &array is char* (*)[] because array used as operand to & address of operator is a case where array is not converted to pointer. That is why the first statement is legal. (Note that char *(*ptr)[4] is a pointer to an array of 4 char*-s. Here the address of array is assigned to ptr).

from §6.3.2.1p1

...A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const- qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const- qualified type.

Whatever the source be, maybe it tried to show you this idea that arrays are not modifiable lvalues so statements like balance++ in C is illegal given that balance is an array name.

Without going awry or confusing the correct way to describe what array would be from standard §6.2.5p20

An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type. The element type shall be complete whenever the array type is specified. Array types are characterized by their element type and by the number of elements in the array. An array type is said to be derived from its element type, and if its element type is T , the array type is sometimes called ''array of T ''. The construction of an array type from an element type is called ''array type derivation''

Also note one thing, when you used char a[]={"aa","bb"}; - this is wrong in the sense that string literals are char arrays which decays into pointer - so it is an array of char*-s not array of char.

Is an array name a pointer?

An array is an array and a pointer is a pointer, but in most cases array names are converted to pointers. A term often used is that they decay to pointers.

Here is an array:

int a[7];

a contains space for seven integers, and you can put a value in one of them with an assignment, like this:

a[3] = 9;

Here is a pointer:

int *p;

p doesn't contain any spaces for integers, but it can point to a space for an integer. We can, for example, set it to point to one of the places in the array a, such as the first one:

p = &a[0];

What can be confusing is that you can also write this:

p = a;

This does not copy the contents of the array a into the pointer p (whatever that would mean). Instead, the array name a is converted to a pointer to its first element. So that assignment does the same as the previous one.

Now you can use p in a similar way to an array:

p[3] = 17;

The reason that this works is that the array dereferencing operator in C, [ ], is defined in terms of pointers. x[y] means: start with the pointer x, step y elements forward after what the pointer points to, and then take whatever is there. Using pointer arithmetic syntax, x[y] can also be written as *(x+y).

For this to work with a normal array, such as our a, the name a in a[3] must first be converted to a pointer (to the first element in a). Then we step 3 elements forward, and take whatever is there. In other words: take the element at position 3 in the array. (Which is the fourth element in the array, since the first one is numbered 0.)

So, in summary, array names in a C program are (in most cases) converted to pointers. One exception is when we use the sizeof operator on an array. If a was converted to a pointer in this context, sizeof a would give the size of a pointer and not of the actual array, which would be rather useless, so in that case a means the array itself.

If an array_name is a pointer how isnt int *ptr = array_name not a pointer to a pointer

An array is not a pointer.

But first, let’s look at the assignment. Suppose you have:

int SomeInt;
int *a = &SomeInt;

At this point a points to SomeInt. Then we do:

int *b;
b = a;

What does this do? It sets b equal to a. It does not make b point to a. After this assignment, b has the same value as a, so b points to the same place a points to; it points to SomeInt. It does not point to a.

Similarly, your ptr = x; does not make ptr point to x. It makes ptr equal to the value of the expression x.

However, in this case, that value is not the array, because there is an automatic conversion occurring. We will discuss that below.

Getting back to arrays not being pointers, after int x[4];, x is the name of the array of four int. If you print the size of the array, with printf("%zu\n", sizeof x);, you will get 16 on systems where int is four bytes, because the size of the array is 16 bytes. This is because x is the array, so sizeof x is the size of the array.

However, when you write ptr = x;, it does not assign the array to ptr. It assigns a pointer to the first element to ptr. How does that work?

Because early C did not have any support for working with whole arrays, such as assigning one to another, you had to work with them only through pointers. So, to set a pointer to point to the first element of an array, you would have to write ptr = &x[0];. To make this easier, the language was designed to allow you to write ptr = x; instead. When you use an array in this way, it is automatically converted to a pointer to its first element, as if you had written &array[0] instead of array.

That automatic conversion occurs whenever an array is used in an expression except when it is the operand of sizeof, is the operand of unary &, or is a string literal used to initialize an array.

If arr[3] is an array and ptr is a pointer, then why do arr and &arr give same result but ptr and &ptr don't

Arrays are not pointers!

Arrays do decay to a pointer to their first element in all sorts of circumstances. For example std::cout << arr; actually prints the memory address of the first element of the array. std::cout << &arr; prints the memory address of the array. As the address of the first element is the same as the address of the array you see the same value.

However, just because they have the same value, does not mean they are the same. arr can decay to a int*, while &arr is a pointer to an array, a int(*)[3].

I hope the following will help to clear things up a little:

#include <iostream>
#include <type_traits>

void make_it_decay(int x[]) {
std::cout << std::is_same_v< decltype(x), int*> << "\n";
}

int main() {
int arr[3] = {1,2,3};

//std::cout << (arr == &arr) << "\n"; // does not compile !
std::cout << (arr == &(arr[0])) << "\n";

std::cout << std::is_same_v< decltype(arr), int[3]> << "\n";
std::cout << std::is_same_v< decltype(&arr),int(*)[3]> << "\n";
std::cout << std::is_same_v< decltype(&arr[0]), int* > << "\n";
make_it_decay(arr);
}

output:

1
1
1
1
1

I use decltype to infer the type of certain expressions and std::is_same_v to see if the expressions are of same type.

arr is of type int[3]. It is an array. It is not a pointer.

&arr is the address of the array. It is a pointer to an array with three elements, a int(*)[3].

&arr[0] even though it has the same value as &arr is of different type. It is a pointer to int, an int*.

When we pass arr to a function then it decays to a pointer to the first element of the array. And we can see that inside the function x is int*.


Now to your quesiton...

Above I tried to lay out what happens when you write std::cout << arr. Pointers are different, because ... well arrays are not pointers.

std::cout << ptr;  // prints the value ptr
std::cout << &ptr; // prints the address of ptr

Perhaps some visualization helps. The difference in types gets most apparent when incrementing the pointers

 -------------------
| arr |
-------------------
| 1 | 2 | 3 |
-------------------
^ ^ ^
&arr | &arr + 1
&arr[0] |
&arr[0] + 1

why there is no use of & in case of adding pointer to array of character?

In this version:

int n = 50;
int *p = &n;

you have a single int n. To make a pointer to it, you need to take its address with &.

On the other hand, in this version:

int n[50];
int *p = n;

the variable n is an array of int. This has the property that the name of the array actually refers to the first element (i.e. the array decays to a pointer when used like this). So no & is needed to refer to the address.

The exact same logic applies to the char case:

char name[] = "Hello";
char *ptr = name;

where name is an array, so you can take a pointer to the first element by just using the variable name without using &.

what is the relationship between char *[] and char ** when talking about array of character pointers

The difference between char *argv[] and char **argv is that:

  • char *argv[] is a array of char * pointers.

  • char **argv is a pointer to another pointer which points to a char.

char *argv[] can be visualized like this:

p1 -> "hello"   
p2 -> "world"
p3 -> "!"

// p1, p2 and p3 are
// pointers to strings
// they have type char *
_________________
| p1| p2 | p3 |
—————————————————
// argv looks like this
// it is an array of all the pointers

when referencing the name of the array argv in an expression it will yield a pointer to the first element in the array.

The type of the array name argv when used in an expression is char **. This is because:

  • The array name argv decays to a pointer to the first element of the array.

  • The first element also happens to be a pointer, so argv is essentially a pointer to another pointer hence the type is char **



Related Topics



Leave a reply



Submit