Why is the first line valid but the rest invalid. I though the first was a shorthand for the second.
const char *c = "abc"; // Why valid?
const char *b = { 'a' , 'b', 'c', '\0' }; // invalid
const int *a = { 1, 2, 3, 0 }; // invalid
The real difference here is that "abc" is a string literal of type char[], whereas {'a', 'b', 'c', 0} is not. You could easily use it to initialize a completely different type, for example:
struct s{
char c;
int i, j;
float f;
} x = {'a', 'b', 'c', 0};
So when you write const char *b = { 'a' , 'b', 'c', '\0' };, the compiler picks the implicit conversion {'a'} -> 'a' and tries to initialize your pointer with that. This may or may not fail depending on the compiler and the actual value, for example many compilers would interpret const char *b = { '\0' }; as initializing b to a NULL pointer instead of an empty string as one could expect.
If you want to initialize the pointer to the address of an array (or any other type) created with list initialization, you should cast explicitly:
const char *b = (const char[]){'a', 'b', 'c', 0};
const int *a = (const int[]){'a', 'b', 'c', 0};
struct s *x = &(struct s){'a', 'b', 'c', 0};
In the first case you have a string literal which are arrays of char, it will be converted to a pointer to char in this context.
In the next two cases you are attempting to use list initialization to initialize a pointer which will attempt to convert the first element of the list to a pointer which generates a warning since neither a char or an int are pointers, the same way this would:
const char *b = 'a' ;
If you had valid pointers in the list it would work fine for the first element but would be ill-formed since you have more initializers than variables.
Array and pointer isn't the same thing.
const char *c = "abc";
Initialise pointer with address of string constant. Sting constant contained elsewhere (not on stack, usually special global constant area).
const char c[] = "abc";
Initialise array of chars with given characters (with contents of given string). This one would be on stack.
The first line is valid because the C standard allows for the creation of constant strings, because their length can be determined at compile time.
The same does not apply to pointers: the compiler can't decide whether it should allocate the memory for the array in the heap (just like a normal int[], for instance) or in regular memory, as in malloc().
If you initialize the array as:
int a[] = { 1, 2, 3, 0 };
then it becomes valid, because now the compiler is sure that you want an array in the heap (temporary) memory, and it will be freed from memory after you leave the code section on which this is declared.
Related
I have a question regarding my code here:
char a = 'A';
char b = 'B';
char c = 'C';
char *ad = &a;
char *ab = &b;
char *ac = &c;
char* cp[] = {ad, ab, ac};
char **d = &(cp[2]); // okay
// char **d[0] = &(cp[2]); //variant 1: error: invalid initializer
// char **d[] = &(cp[2]); //variant 2: error: invalid initializer
I am not sure why variant1 and variant2 would leads to error. For the original initialization (i.e. the line with comment okay), there was no error. My understanding is I have initialize a pointer (i.e. d) which is pointing to a pointer that pointing to a character. So this seems fine.
For variant1, I thought (based on my understanding) I am initializing an array of pointers where each of them will point to a pointer that points to a character. So in this case, I only initialize the very first element in that array.
Similarly for variant2, I initialize an empty array of pointers that each would points to a character.
Could someone tells me why there is error of coming from the compiler here?
And is my understanding of variant 1 correct? i.e. d is an array of pointers where each entry in the array would points to a pointer that points to a character??
I thought this is correct. So why is it that I cannot initialize variant 1?
For variant2, I also thought that I have an array of pointers each pointing to a pointer that points to a character.
This compiles cleanly...
char a = 'A';
char b = 'B';
char c = 'C';
char *ad = &a;
char *ab = &b;
char *ac = &c;
char *cp[] = {ad, ab, ac}; // be consistent
char **d = &cp[2];
char **e[1] = { &cp[2] }; // cannot dimension an array to have 0 elements
char **f[] = { &cp[2] }; // compilers count more accurately than people
Note the absence of unnecessary "()".
Array initialisers are listed within enclosing braces. "{}"
Exception to last statement: char foo[] = "bar";... "string" array elements do not require braces unless one gets silly:
char foo[] = { 'b', 'a', 'r', '\0', };
I've tried this: char **foo, which is is a string array,
but warnings generate, when I try to assign values to it:
char **baz = {"Hello,", "world!"}
So, ...
how to create a dynamic string (char *) array in C, without specifying the size?
how do to assign values to a char ** array?
NOTE:
char *not_this[50] isn't the answer
this is my 2nd question on Stackoverflow
C is willing to automatically size, allocate, and initialize arrays for you. With one exception, however, there is no way to automatically initialize a pointer that points to some other array.
Here is automatic sizing and allocation of an array:
int a[] = {1, 2, 3};
The brackets [] are empty -- you don't have to explicitly specify the size -- because the compiler can see from the initializer that the array should have size 3.
Here is another example, using char:
char s1[] = {'H', 'e', 'l', 'l', 'o', 0};
Here is a much more convenient shortcut form:
char s2[] = "world";
Here is the exception I was talking about:
char *s3 = "hidden";
In this case, the compiler allocates a hidden, unnamed array containing the string "hidden", then initializes s3 to point to it. This is just about perfectly equivalent to the more explicit setup
char a2[] = "hidden";
char *s3a = a2;
except for the visibility of the intermediate array a2.
Putting all this together, you can initialize an array of pointers to char like this:
char *strarray[] = { "Hello", "world" };
This is equivalent (again, except for the visibility of the actual arrays holding the strings) to the more explicit
char a3[] = "Hello";
char a4[] = "world";
char *strarray2[] = { a3, a4 };
But you can not directly do anything along the lines of
char **dynstrarray = { ... };
The reason you can't do it is because dynstrarray is fundamentally a pointer, and there's no general mechanism in C to automatically allocate something for a pointer like this to point to. Again, the only time C will automatically allocate something for a pointer to point to is the special case for pointer to char, as demonstrated by s3 above.
So if you want to initialize a pointer like dynstrarray, your only choice is to do it explicitly, yourself. You could initialize it to point to an array of pointers to char that you had already constructed, like this:
char a3[] = "Hello";
char a4[] = "world";
char *strarray2[] = { a3, a4 };
char **dynstrarray[] = strarray2;
Or, more commonly, you would use malloc to dynamically allocate some memory for dynstrarray to point to. In that case (but only in that case) you would also have the option of resizing dynstrarray later, using realloc.
You can create an array of char * as follows:
char *baz[] = {"Hello,", "world!"};
have a look at this answer.
You then only need to swap every occurence of int for char*.
Natively there are no dynamically sized arrays in C (if this is what you want) but you can create them on your own as shown by the linked answer.
The following is accepted as valid c code by gcc version 6.3:
char white[] = { 'a', 'b', 'c' };
char blue[] = "abc";
char *red = "abc";
However the following fails:
char *green = { 'a', 'b', 'c' }; // gcc error
I am sure there is a perfectly rational reason for this to be the case, but I am wondering what it is. This question is motivated by the case when having to initialize an array of bytes (so unsigned char rather than char), it is very tempting to write something like { '\x43', '\xde', '\xa0' } rather than "\x43\xde\xa0", and as soon as you forget to write my_array[] instead of *my_array, you get caught by the compiler.
The following will produce an error
char *green = { 'a', 'b', 'c' };
Because the initializer for green isn't an array of characters as you believe. It doesn't have a type, it's just a brace-enclosed initializer list. The thing that it initializes in the previous samples (i.e. white) determines how it's interpreted. The same initialzier can be used to initialize any aggregate that is capable of holding 3 characters.
But green is a pointer, and not an aggregate, so you can't use a brace-enclosed initializer list as it's initial value.1
Now, the following two work but with very different semantics:
char blue[] = "abc";
char *red = "abc";
blue is an array. It will hold the same contents as the literal "abc". red is a pointer that points at the literal "abc".
You can use a compound literal expression:
char *green = (char[]){ 'a', 'b', 'c' };
It tells the compiler to create an unnamed object (the life time of which depends on the scope of the declaration), that is of character array type and is initialized with those three characters. The pointer is then assigned the address of that object.
These three declarations
char white[] = { 'a', 'b', 'c' };
char blue[] = "abc";
char *red = "abc";
are different.
The first one declares a character array that contains exactly three characters corresponding to the number of the initializers.
The second one declares a character array of four characters because it is initialized by a string literal that has four characters including the terminating zero. So this character array contains a string.
The third one defined a string literal that is a character array and declares a pointer of type char * that is initialized by the address of the first character of the character array corresponding to the string literal.
You can imagine this declaration like
char unnamed = { 'a', 'b', 'c', '\0' };
char *red = unnamed;
This declaration
char *green = { 'a', 'b', 'c' };
is invalid because the left object is a scalar and may not be initialized by a list that contains more than one initializer.
Take into account that you could use a compound literal to initialize the pointer. For example
char *green = ( char[] ){ 'a', 'b', 'c' };
I've recently started to try learn the C programming language. In my first program (simple hello world thing) I came across the different ways to declare a string after I realised I couldn't just do variable_name = "string data":
char *variable_name = "data"
char variable_name[] = "data"
char variable_name[5] = "data"
What I don't understand is the difference between them. I know they are different and one of them specifically allocates an amount of memory to store the data in but that's about it, and I feel like I need to understand this inside out before moving onto more complex concepts in C.
Also, why does using *variable_name let me reassign the variable name to a new string but variable_name[number] or variable_name[] does not? Surely if I assign, say, 10 bytes to it (char variable_name[10] = "data") and try reassigning it to something that is 10 bytes or smaller it should work, so why doesn't it?
What are the empty brackets and the asterix doing?
In this declaration
char *variable_name = "data";
there is declared a pointer. This pointer points to the first character of the string literal "data". The compiler places the string literal in some region of memory and assigns the pointer by the address of the first character of the literal.
You may reassign the pointer. For example
char *variable_name = "data";
char c = 'A';
variable_name = &c;
However you may not change the string literal itself. An attempt to change a string literal results in undefined behaviour of the program.
In these declarations
char variable_name[] = "data";
char variable_name[5] = "data";
there are declared two arrays elements of which are initialized by characters of used for the initialization string literals. For example this declaration
char variable_name[] = "data";
is equivalent to the following
char variable_name[] = { 'd', 'a', 't', 'a', '\0' };
The array will have 5 elements. So this declaration is fully euivalent to the declaration
char variable_name[5] = "data";
There is a difference if you would specify some other size of the array. For example
char variable_name[7] = "data";
In this case the array would be initialized the following way
char variable_name[7] = { 'd', 'a', 't', 'a', '\0', '\0', '\0' };
That is all elements of the array that do not have explicit initializers are zero-initialized.
Pay attention to that in C you may declare a character array using a string literal the following way
char variable_name[4] = "data";
that is the terminating zero of the string literal is not placed in the array.
In C++ such a declaration is invalid.
Of course you may change elements of the array (if it is not defined as a constant array) if you want.
Take into account that you may enclose a string literal used as an initializer in braces. For example
char variable_name[5] = { "data" };
In C99 you may also use so-called destination initializers. For example
char variable_name[] = { [4] = 'A', [5] = '\0' };
Here is a demonstrative program
#include <stdio.h>
#include <string.h>
int main(void)
{
char variable_name[] = { [4] = 'A', [5] = '\0' };
printf( "%zu\n", sizeof( variable_name ) );
printf( "%zu\n", strlen( variable_name ) );
return 0;
}
The program output is
6
0
When ypu apply standard C function strlen declared in header <string.h> you get that it returns 0 because the first elements of the array that precede the element with index 4 are zero initialized.
What is the difference between
char ch [ ] = "hello";
and
char ch [ ] = { 'h','e','l','l','o','\0'};
and why we can only do
char *p = "hello";
But cant do
char *p = {'h','e','l','l','o','\0'};
char ch [ ] = "hello";
char ch [ ] = { 'h','e','l','l','o','\0'};
There is no difference. ch object will be exactly the same in both declarations.
On why we cannot do:
char *p = {'h','e','l','l','o','\0'};
An initializer list of more than one value can only be used for objects of aggregate type (structures or array). You can only initialize a char * with a pointer value.
Actually:
char ch [ ] = "hello";
and
char *p = "hello";
are not the same. The first initializes an array with the elements of a string literal and the second is a pointer to a string literal.
The two array declarations are the same. As for the pointer declaration, the form char *p = {'h','e','l','l','o','\0'}; is not valid, simply because it was not included in the compiler design.
There is no theoretical reason, in my knowledge, why that declaration should not be valid, when char *p = "hello"; is.
Open upon a time they made a mistake about const
The bit "hello" should be a const char * const but they where lazy and just used char *. But to keep the faith alive they let that one slip.
Then they said. Ok. They can be equal.
Then they had char [] = { 'a', 'b', ...}; and all was good in the world
The the evil monster came and thrust upon them char *p = "hello". But the evil monster was in a good mood and said it should be const char *p = "hello" but I would be happy with that.
He went home and but the evil monster was not amused. He dictated over his realm char *p = {'h','e','l','l','o','\0'}; is the sign of a heretic.
Basically there was a cock-up. Just do it right from now and there is old code kicking around that needs to be satisfied.
Let's take it one by one. This following is initializing a char array ch using the string literal "hello".
char ch[] = "hello";
The following is initializing the array ch using an array initialization list. This is equivalent to the above statement.
char ch[] = {'h', 'e', 'l', 'l', 'o', '\0'};
The following is initializing a char pointer p to point to the memory where the string literal "hello" is stored. This is read-only memory. Attempting to modify its contents will not give compile error because string literal in C are not const qualified unlike in C++, but will cause undefined behaviour or even program crash.
char *p = "hello";
const char *p = "hello"; // better
The following last statement is plain wrong.
char *p = {'h','e','l','l','o','\0'};
p is a char pointer here, not an array and can't be initialized using array initialization list. I have highlighted the words array and pointer above to emphasize that array and pointer are different types. In some cases, an array is implicitly converted to a pointer to its first element like when an array is passed to a function or assigned to a pointer of the same type. This does not mean they are the same. They have different pointer arithmetic and different sizeof values.
There is no difference in
char ch [ ] = "hello";
char ch [ ] = { 'h','e','l','l','o','\0'};
But check below
char *p = "hello"; //This is correct
char *p = {'h','e','l','l','o','\0'}; //This is wrong
If you want to make it correct you need to use
char *p[]={'h','e','l','l','o','\0'}; //This works