Shyam Prasanna
Shyam Prasanna

Reputation: 111

char **s vs char *s[] initialization

I am trying hard to understand the difference between char *s[] and char s** initialization. My char *s[] works fine, whereas my char s1** throws an error [Error] scalar object 's1' requires one element in initializer. I don't get the meaning of that error.

How can we initialize char s1** properly?

#include<stdio.h>
int main(void)
{
    char *s[]={"APPLE","ORANGE"};
    char **s1={"APPLE","ORANGE"};
    return 0;
}

Upvotes: 2

Views: 260

Answers (1)

Peter - Reinstate Monica
Peter - Reinstate Monica

Reputation: 16017

TLDR: char **s1=(char*[]){"apple","orange"};.

You can, of course, initialize pointers with the address of an element in an array. This is more common with simpler data types: Given int arr[] = {1,2};you can say int *p = &arr[0];; a notation which I hate and have only spelled out here in order to make clear what we are doing. Since arrays decay to pointers to their first elements anyway, you can simpler write int *p = arr;. Note how the pointer is of the type "pointer to element type".

Now your array s contains elements of type "pointer to char". You can do exactly the same as before. The element type is pointer to char, so the pointer type must be a pointer to that, a pointer to pointer to char, as you have correctly written:

char **s2= &s[0];, or simpler char **s2= s;.

Now that's a bit pointless because you have s already and don't really need a pointer any longer. What you want is an "array literal". C99 introduced just that with a notation which prefixes the element list with a kind of type cast. With a simple array of ints it would look like this: int *p = (int []){1, 2};. With your char pointers it looks like this:

char **s1=(char*[]){"apple","orange"};.

Caveat: While the string literals have static storage duration (i.e., pointers to them stay valid until the program ends), the array object created by the literal does not: Its lifetime ends with the enclosing block. That's probably OK if the enclosing block is the main function like here, but you cannot, for example, initialize a bunch of pointers in an "initialize" routine and use them later.

Caveat 2: It would be better to declare the arrays and pointers as pointing to const char, since the string literals typically are not writable on modern systems. Your code compiles only for historical reasons; forbidding char *s = "this is constant"; would break too much existing code. (C++ does forbid it, and such code cannot be compiled as C++. But in this special case C++ does not have the concept of compound literals in this way, and the program below is not valid C++.) I adjusted the types accordingly in the complete program below which demonstrates the use of a compound literal. You can even take its address, like that of any other array!

#include<stdio.h>
int main(void)
{
    /// array of pointers to char.
    const char *arrOfCharPtrs[2]={"APPLE","ORANGE"}; 
    
    /// pointer to first element in array
    const char **ptrToArrElem= &arrOfCharPtrs[0];
    
    /// pointer to element in array literal
    const char **ptrToArrLiteralElem=(const char*[]){"apple","orange"};

    /// pointer to entire array. 
    /// Yes, you can take the address of the entire array!
    const char *(*ptrToArr)[2] = &arrOfCharPtrs;
    
    /// pointer to entire array literal. Note the parentheses around
    /// (*ptrToArrLiteral)- Yes, you can take the address of an array literal!
    const char *(*ptrToArrLiteral)[2] = &(const char *[]){"apples", "pears"};
    
    printf("%s, %s\n", ptrToArrElem[0], ptrToArrElem[1]);
    printf("%s, %s\n", ptrToArrLiteralElem[0], ptrToArrLiteralElem[1]);
    printf("%s, %s\n", (*ptrToArr)[0], (*ptrToArr)[1]);
    
    // In order to access elements in an array pointed to by ptrToArrLiteral,
    // you have to dereference the pointer first, yielding the array object,
    // which then can be indexed. Note the parentheses around (*ptrToArrLiteral)
    // which force dereferencing *before* indexing, here and in the declaration.
    printf("%s, %s\n", (*ptrToArrLiteral)[0], (*ptrToArrLiteral)[1]);
    return 0;
}

Sample session:

$ gcc -Wall -pedantic -o array-literal array-literal.c &&  ./array-literal
APPLE, ORANGE
apple, orange
APPLE, ORANGE
apples, pears

Upvotes: 4

Related Questions