initializing char** with an array of strings [duplicate] - c

This question already has answers here:
Initialize array of strings
(3 answers)
Closed last month.
This is regarding single-char pointers and double-char pointers. Here I am trying to initialize a double-pointer with an array of strings while defining the new pointer.
I have tried
char arry[] = "Hello World"
char* cptr = "Hello World"
It works fine and the difference is the second line is only read-only. In the same way, I am trying to use double pointers
char *darry[] = {"Hello", "World", "C", "Ubuntu"};
char **da = {"Hello", "World", "C", "Ubuntu"};
I thought the double pointer(da) would work the same as the single pointer but it is giving me an error. Could anyone point me in the right direction?
Thanks

char *darry[] is an array.
{"Hello", "World", "C", "Ubuntu"} is a valid initializer for an array of char *.
char **da is a pointer.
char **da = {"Hello", "World", "C", "Ubuntu"}; fails as {"Hello", "World", "C", "Ubuntu"} is not a valid initializer for a pointer.
To initialize a non-char * pointer to the first element of an array, form the array of char * with a compound literal. The array is then converted to the address of its first element as part of the initialization - which is a char **.
// v-----------------------------------------v Array of char *.
char **da = (char *[]){"Hello", "World", "C", "Ubuntu"};

Related

Defining string arrays

Why is the following an acceptable way to initialize an array of strings:
char * strings[] = { "John", "Paul", NULL};
But this way will fail:
char ** strings = { "John", "Paul", NULL};
My thought was that it would work relatively the same as doing:
char string[] = "John";
char * string = "Paul";
Where both work. What's the difference between the two?
char * strings[] is an array of pointers. When you initialize it as
char * strings[] = { "John", "Paul", NULL};
the strings John Paul are string literals. They are constants that exist somewhere in the code or Read only memory. What is done is to copy the pointer to the string literal John into the strings[0] and so on. i.e.
strings[0] --> holds a pointer to "John".
strings[1] --> holds a pointer to "Paul"
Note that the string literals should not be modified by your program. If you do, it is undefined behaviour.
In case of char ** strings This is a pointer to a pointer. It is a single memory location and cannot hold many pointers on its own. So, you cannot initialize it as below.
char ** strings = { "John", "Paul", NULL}; // error
However, a pointer to pointer can be used along with dynamic memory allocation (malloc,calloc etc) to point to an array of strings.
char string[] = "John";
In this case, you have a char array into which the string literal is copied. This step is done by the compiler, generally in the start up code before the main starts.
char * string = "Paul";
Here you have a char pointer which points to a string literal.
Difference between the above two statements is that in the case of a char array, you can modify the elements of string but you cannot in the second case.

Why can you have an pointer to array of strings in C

why does
char *names [] = {"hello", "Jordan"};
work fine
but this does not
char names [] = {"hello", "Jordan"};
would appreciate if someone could explain this to me, thank you :).
Here
char *names [] = {"hello", "Jordan"};
names is array of char pointers i.e it can holds pointers i.e names each elements itself is one char array. But here
char names [] = {"hello", "Jordan"};
names is just a char array i.e it can hold only single char array like "hello" not multiple.
In second case like
int main(void) {
char names[] = {"hello", "Jordan"};
return 0;
}
when you compile(Suggest you to compile with -Wall -pedantic -Wstrict-prototypes -Werror flags), compiler clearly says
error: excess elements in char array initializer
which means you can't have more than one char array in this case. Correct one is
char names[] = {'h','e','l','l','o','\0'}; /* here names is array of characters */
Edit :- Also there is more possibility if syntax of names looks like below
char names[] = { "hello" "Jordan" }; /* its a valid one */
then here both hello and Jordan gets joined & it becomes single char array helloJordan.
char names[] = { "helloJordan" };
The first is an array of pointers to char. The second is an array of char and would have to look like char names[] = {'a', 'b', 'c'}
A string literal, such as "hello", is stored in static memory as an array of chars. In fact, a string literal has type char [N], where N is the number of characters in the array (including the \0 terminator). In most cases, an array identifier decays to a pointer to the first element of the array, so in most expressions a string literal such as "hello" will decay to a pointer to the char element 'h'.
char *names[] = { "hello", "Jordan" };
Here the two string literals decay to pointers to char which point to 'h' and 'J', respectively. That is, here the string literals have type char * after the conversion. These types agree with the declaration on the left, and the array names[] (which is not an array of character type, but an array of char *) is initialized using these two pointer values.
char names[] = "hello";
or similarly:
char names[] = { "hello" };
Here we encounter a special case. Array identifiers are not converted to pointers to their first elements when they are operands of the sizeof operator or the unary & operator, or when they are string literals used to initialize an array of character type. So in this case, the string literal "hello" does not decay to a pointer; instead the characters contained in the string literal are used to initialize the array names[].
char names[] = {"hello", "Jordan"};
Again, the string literals would be used to initialize the array names[], but there are excess initializers in the initializer list. This is a constraint violation according to the Standard. From §6.7.9 ¶2 of the C11 Draft Standard:
No initializer shall attempt to provide a value for an object not contained within the entity being initialized.
A conforming implementation must issue a diagnostic in the event of a constraint violation, which may take the form of a warning or an error. On the version of gcc that I am using at the moment (gcc 6.3.0) this diagnostic is an error:
error: excess elements in char array initializer
Yet, for arrays of char that are initialized by an initializer list of char values rather than by string literals, the same diagnostic is a warning instead of an error.
In order to initialize an array of char that is not an array of pointers, you would need a 2d array of chars here. Note that the second dimension is required, and must be large enough to contain the largest string in the initializer list:
char names[][100] = { "hello", "Jordan" };
Here, each string literal is used to initialize an array of 100 chars contained within the larger 2d array of chars. Or, put another way, names[][] is an array of arrays of 100 chars, each of which is initialized by a string literal from the initializer list.
char name[] is an array of characters so you can store a word in it:
char name[] = "Muzol";
This is the same of:
char name[] = {'M', 'u', 'z', 'o', 'l', '\0'}; /* '\0' is NULL, it means end of the array */
And char* names[] is an array of arrays where each element of the first array points to the start of the elements of the second array.
char* name[] = {"name1", "name2"};
It's the same of:
char name1[] = {'n', 'a', 'm', 'e', '1', '\0'}; /* or char name1[] = "name1"; */
char name2[] = {'n', 'a', 'm', 'e', '2', '\0'}; /* or char name2[] = "name2"; */
char* names[] = { name1, name2 };
So basically names[0] points to &name1[0], where it can read the memory until name1[5], this is where it finds the '\0' (NULL) character and stops. The same happens for name2[];

What is the difference between char stringA[LEN] and char* stringB[LEN] in C

I read a few similar questions, C: differences between char pointer and array, What is the difference between char s[] and char *s?, What is the difference between char array[] and char *array? but none of them seem to clear my doubt.
I'm aware that
char *s = "Hello world";
makes the string immutable whereas
char s[] = "Hello world";
can be modified.
My doubt is if I do char stringA[LEN]; and char* stringB[LEN]; Are they any different? Or does stringB again becomes immutable as in the case before?
Let me give you a visual explanation:
As you can see, a is an array of 4 characters, whereas b is an array of 4 character pointers, each pointing to the beginning of a C string. Each one of those strings can have a different length.
Are they any different?
Yes.
Both variables stringA and stringB are arrays. stringA is an array of char of size LEN and stringB is an array of char * of size LEN.
char and char * are two different types. stringA can hold only one character string of length LEN while elements of stingB can point to LEN number of strings.
Or does stringB again becomes immutable as in the case before?
Whether strings pointed by elements of stringB is mutable or not will depend on how memory is allocated. If they are initialized with string literals
char* stringB[LEN] = { "Apple", "Bapple", "Capple"};
then they are immutable. In case of
for(int i = 0; i < LEN; i++)
stringB[i] = malloc(30) // Allocating 30 bytes for each element
strcpy(stringB[0], "Apple");
strcpy(stringB[1], "Bapple");
strcpy(stringB[2], "Capple");
they are mutable.
They are not the same.
Here stringA is an array of char, that means printing stringA[0] will show the letter S:
char stringA[] = "Something";
Whereas printing stringB[0] here will show Something (array of pointer to char):
char* stringB[] = { "Something", "Else" };
They are not having the same datatype, less being comparable.
char stringA[LEN]; is a char array of LEN length. (Array of chars)
char* stringB[LEN]; is a char * array of LEN length. (Array of char pointers)
FWIW, in case of char *s = "Hello world"; s is a pointer which points to a string literal which is non-modifiable. The pointer itself can certainly be changed. Only the content it points to (values) cannot be changed.
The reason
char* s = "Hello World!";
is immutable is because "Hello World!" is stored in RO(Read Only) memory, sow hen you try to change it, it throws an error. Declaring it as a pointer to the first element of an "array" is NOT the reason it's immutable. You seem to be slightly confused as well. Assuming your questions is exactly what you mean,
char stringA[LEN] = "ABC";
is a string in traditional C style, but stringB as you've defined isn't a string, it's an array of strings -
char* stringB[LEN] = {"ABC", "DEF"};
Assuming you mean what I think you mean,
char stringA[LEN] = "Hello World!";
char *stringB = malloc(LEN);
strcpy(stringB, stringA);
In this case, stringB IS mutable, since it refers to writable memory.

Why can't define string array using char **?

We can assign a string constant to char * or char [ ] just like:
char *p = "hello";
char a[] = "hello";
Now for string array, naturally it'll be like this:
char **p = {"hello", "world"}; // Error
char *a[] = {"hello", "world"};
the first way will generate a warning when compiling, and has a Segmentation fault when I'm trying to print the string constant with printf("%s\n", p[0]);
Why ?
char **p = {"hello", "world"};
Here, p is a pointer to pointer to char which can't be initialized with an an array initializer with an array of pointers (each of the string literals gets converted into a pointer during initialization -- actual type of a string literal in C is char[n]).
The types are incompatible i.e. p is of type char ** and RHS is of type char *[]. Hence, the diagnostic is issued by the compiler.
Whereas,
char *a[] = {"hello", "world"};
is valid as a is an array of pointers to char and the types match. Hence this is a valid initialization.
Since C99, the C language supports Compound literals (6.5.2.5, C99) using which you can initialize:
char **p = (char *[]) {"hello", "world"};
So either use compound literals if C99 or later is supported by your compiler. Otherwise, stick to the array of pointers initialization (char *a[]).
You can read about various examples on compound literals here (gcc manual).
To summarize, because char **p points to a pointer, not to an array of pointers.
That happens because you need to build the array your self. For each level (or dimension) you need to reserve memory for the pointer holders.
What you need to do is:
// this holds you pointers
char **p = malloc( sizeof( char *) * nr of elements);
// to set the elements, you need to:
p[0] = "hello";
p[1] = "world";
And this needs to be done for as many levels (or dimensions) you have.
because char **p is a pointer to pointer not an array of pointers, where char* a[] is an array of pointers
char *ptr ="hello";
defines ptr to be a pointer to the (read-only) string "hello", and thus contains the address of string say 100. ptr must itself be stored somewhere: say location 200.
char **p = &ptr;
Now p points to ptr, that is, it contains the address of ptr (which is 200).
printf("%s",**p);
So your creating a pointer to another pointer but not extra memory to store more than one string but char *a[] creates array of pointers depends on the size you have given
an advice
Do not use char *p = "hello";
use const char *p = "hello";
because string literals are saved in read only memory
First, char *p = "hello" is different from char a[] = "hello".
char *p = "hello"
"hello" is actually a pointer to literal constant. You assign a pointer to literal constant to p. So It's better to use const char *p = "hello".
char a[] = "hello"
The characters in "hello" were copied to array a with a '\0' end.
Second,
char *a[]
defines a array of pointers, so it's OK to use char *a[] = {"hello", "world"};
char **p
defines a pointer to a pointer to a char, so it makes no sense to using char **p = {"hello", "world"};

C pointers and array declaration

I'm currently learning C through "Learning C the Hard Way"
I am a bit confused in some sample code as to why some arrays must be initialized with a pointer.
int ages[] = {23, 43, 12, 89, 2};
char *names[] = {
"Alan", "Frank",
"Mary", "John", "Lisa"
};
In the above example, why does the names[] array require a pointer when declared? How do you know when to use a pointer when creating an array?
A string literal such as "Alan" is of type char[5], and to point to the start of a string you use a char *. "Alan" itself is made up of:
{ 'A', 'L', 'A', 'N', '\0' }
As you can see it's made up of multiple chars. This char * points to the start of the string, the letter 'A'.
Since you want an array of these strings, you then add [] to your declaration, so it becomes: char *names[].
Prefer const pointers when you use string literals.
const char *names[] = {
"Alan", "Frank",
"Mary", "John", "Lisa"
};
In the declaration, name is a array of const char pointers which means it holds 5 char* to cstrings. when you want to use a pointer, you use a pointer, as simple as that.
Example:
const char *c = "Hello world";
So, when you use them in an array, you're creating 5 const char* pointers which point to string literals.
Because the content of the array is a char*. The other example has an int. "Alan" is a string, and in C you declare strings as char pointers.
In the case of char *names[] you are declaring an array of string pointers.
a string in C e.g. "Alan" is a series of characters in memory ended with a \0 value marking the end of the string
so with that declaration you are doing this
names[0] -> "Alan\0"
names[1] -> "Frank\0"
...
then you can use names[n] as the pointer to the string
printf( "%s:%d", names[0], strlen(names[0]) );
which gives output "Alan:4"
The use of "array of pointer" is not required.
The following will work as well. It's an array of 20 byte character arrays. The compiler only needs to know the size of the thing in the array, not the length of the array. What you end up with is an array with 5 elements of 20 bytes each with a name in each one.
#include <stdio.h>
char names[][20] = {
"Alan", "Frank",
"Mary", "John", "Lisa"
};
int main(int argc, char *argv[])
{
int idx;
for (idx = 0; idx < 5; idx++) {
printf("'%s'\n", names[idx]);
}
}
In your example the size of the thing in the array is "pointer to char". A string constant can be used to initialize either a "pointer to char" or an "array of char".

Resources