0

Why does this work:

char *name = "steven";

but this doesn't:

char **names = {"steven", "randy", "ben"};

Or, why does this work:

char *names[] = {"steven", "randy", "ben"};

but, again, this doesn't:

char **names = {"steven", "randy", "ben"};
wrosen01
  • 105
  • 5
  • 7
    TLDR: because `char **names` **does not refer to a 2D array**, no matter what you've been told. See [**Correctly allocating multi-dimensional arrays**](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays) – Andrew Henle Sep 08 '21 at 12:59
  • 3
    Does this answer your question? [Double pointer vs array of pointers(\*\*array vs \*array\[\])](https://stackoverflow.com/questions/33746434/double-pointer-vs-array-of-pointersarray-vs-array) – Jorengarenar Sep 08 '21 at 13:01
  • Thanks, yes, both these links are helpful. So when you have char – wrosen01 Sep 08 '21 at 13:16
  • So when you have char *names[] = {"bob", ...}, is names a pointer? – wrosen01 Sep 08 '21 at 13:17
  • 1
    No, the `[]` means that `names` is an array, and the `char *` means that the array contains pointers to `char` – Kevin Sep 08 '21 at 13:18
  • Thanks! I think that is what I was missing – wrosen01 Sep 08 '21 at 13:25

3 Answers3

2

A char **p is not a 2D array, it is a pointer to a pointer to a character. However, you can have more pointers and more characters following, resembling a kind of model of a 2D structure of characters.

C compiler interpret { "steven" } as a 1D array of characters, because the braces are optional (standard chapter 6.7.9 paragraph 14).

As you tried, you can declare an array of pointers to a character by char *p[].

But if you want to have that pointer (to pointers to characters), you need to tell your compiler. The address of an array can be assigned to the pointer.

char **p = (char *[]){ "steven", "randy", "ben", };

Additional note: Since string literals are unmutable, you better add a const for the characters. And since the address of these unnamed string literals are constant, too, you can provide another one.

const char * const *p = (const char * const []){ "steven", "randy", "ben", };
the busybee
  • 10,755
  • 3
  • 13
  • 30
  • Thanks. Am I right in saying the (char *[]){ "steven", "randy", "ben", } is type casting? – wrosen01 Sep 08 '21 at 13:28
  • 1
    Well, it's a _compound literal_, see chapter 6.5.2.5. The standard differentiates it from a cast. – the busybee Sep 08 '21 at 14:07
  • 1
    It's very different from a cast. It *creates* (allocates memory for) an object; that object has a defined lifetime which you must be aware of. – rici Sep 08 '21 at 14:45
2

I also wondered, what if I could answer you in the simplest way possible.

Why are you confused?

A simple pointer to integer for example allocated with 8 cells, acts in the same way as an array has a dimension of 8 cells. The only difference, that you can't see, is that a pointer that has 8 cells allocated is on a part of the memory that is called the HEAP, while a variable of type int tab[8] is allocated on the STACK. Indeed, since the cells are linked in memory, it is easy to imagine that a pointer and an array whose first cell address is sent are the same thing.

Why it doesn't work in the other case

However, when the idea comes to associate (** and [][]) Let's take the example of an int ** ;

int **tab;

tab = malloc(sizeof(int *) * 4);
//secure malloc do not forget
for (int i = 0; i < 4; i++)
{
   tab[i] = malloc(sizeof(int) * 3);
   //secure malloc do not forget
}

and an

int[4][3];

You have a problem. To imagine, a double array type follows itself in memory, because it is the very principle of arrays.

Memoy double array on Stack

While a double pointer has first 4 cells of type int * allocated (which follow each other in memory) and then each pointer of these 4 cells, each points to a memory area of 3 ints which follow each other. But the whole thing does not follow each other in the memory!

Memory double pointer on Heap

A way that may interest you

One thing you can do instead is to create an int ptr(*)[3]; which can point to the first element of an array of size 3, i.e. the address of an array [4][3] for example.

sapristi12
  • 82
  • 9
0

The initializer for a scalar object may not contain more than one item.

6.7.9 Initialization
...
Constraints
2     No initializer shall attempt to provide a value for an object not contained within the entity being initialized.
...
11     The initializer for a scalar shall be a single expression, optionally enclosed in braces. The initial value of the object is that of the expression (after conversion); the same type constraints and conversions as for simple assignment apply, taking the type of the scalar to be the unqualified version of its declared type
C 2011 Online Draft

char **names declares a single, scalar object, not an array, so any initializer for it must only contain a single item. That initializer may be a single string ("steven"), optionally enclosed in braces ({ "steven" }). However, it may not be a list of initializers.

John Bode
  • 119,563
  • 19
  • 122
  • 198