I'm not making a specific program. I was curious about some kind of syntax things in C language.
I made these declarations.
int main()
{
char *titles[] = {"NUDGE", "DECOUPLEING", "WORLD WAR Z"};
char *letters[] = {{'a', 'b', 'c'}, {'x', 'y', 'z'}};
}
In the second declaration, there were 3 kinds of warnings.
braces around scalar initializer
initialization makes pointer from integer without a cast
excess elements in scalar initializer
I got that *numbers[] kind of things should have pointer values in it.
Is the first declaration doesn't show warning because string type is a pointer?
and character is not a string?
+) What does 'scalar initializer' means in first, and third warning?
+)
char titles[][] = {"NUDGE", "DECOUPLEING", "WORLD WAR Z"};
What this shows error and the first one doesn't show error?
I'm a beginner at C pointer. Please explain :)
the second initialization is invalid as {'a', 'b', 'c'} is not the array which can dacal to pointer
you should use compound literals instead:
char *letters[] = {(char[]){'a', 'b', 'c'}, (char []){'x', 'y', 'z'}};
which defines array of two (2) pointers to arrays of three chars.
Related
Strings can be initialized with a string literal
char word1[] = "abc";
or as a char array with a null terminator.
char word2[] = {'a', 'b', 'c', '\0'};
Instead of writing word1[], word1 can also be written with a pointer notation
char *word1 = "abc";
However, when trying to write word2 with a pointer notation
char *word2 = {'a', 'b', 'c', '\0'};
it shows me a bunch of warnings, such as
warning: excess elements in scalar initializer char
*word2 = {'a', 'b', 'c', '\0'};
and when I run the program, I get Segmentation fault (core dumped).
Why is that? Why can you write char *word = "abc" but not char *word = {'a', 'b', 'c', '\0'} ?
Why can you initialize a string pointer as a string literal, but not as an array?
Because {'a', 'b', 'c', '\0'} is not an array; it is a list of values to put in the thing being initialized.
The syntax {'a', 'b', 'c', '\0'} does not stand for an array in C. People see it being used to initialize arrays, but, when used in that way, it is just a list of values. It could also be used to initialize a structure, because it is just listing values to put into the thing being initialized. It is not, by itself, an array.
In char *word2 = {'a', 'b', 'c', '\0'};, it does not make sense to initialize word2 with the values 'a', 'b', 'c', and '\0'. It is just one pointer and should be initialized with one value. Giving a list of four values to initialize one thing does not make sense.
In char *word2 = "abc";, "abc" is not a list of values. It is a string literal. A string literal defines a static array that is filled with the characters of the string. And then the string literal is automatically converted to a pointer to its first element, and it is this pointer that is used to initialize word2.
So char *word2 = "abc"; does two things: The string literal defines an array, and the initialization sets word2 to point to the first element of that array. In contrast, in char *word2 = {'a', 'b', 'c', '\0'};, there is nothing to define an array; the list of values is just a list of values.
Comparing this to array initializations, in char word2[] = {'a', 'b', 'c', '\0'};, the array is initialized with a list of values, which is fine. However, in char word1[] = "abc";, something special happens. C 2018 6.7.9 14 says we can initialize an array of character type with a string literal, and the characters of the string will be used to initialize the elements of the array.
There's no fundamental reason for this -- it's just the way the language was originaly defined.
The basic syntax for array initialization is
type array[] = {value, value, value};
The basic syntax for pointer initialization is
type *pointer = value;
But then we have string literals. And it turns out that, deep down inside, the compiler does two almost completely different things with string literals.
If you say
char array[] = "string";
the compiler treats it just about exactly as if you had said
char array[] = { 's', 't', 'r', 'i', 'n', 'g', '\0' };
But if you say
char *p = "string";
the compiler does something quite different. It quietly creates an array for you, containing the string, more or less as if you had written
char __hidden_unnamed_array[] = "string";
char *p = __hidden_unnamed_array;
But the point -- the answer to your question -- is that the compiler does this special thing only for string literals. In the original definition of C, at least, there was no way to use the {value, value, value} syntax to create a hidden, unnamed array that you could do something else with. The {value, value, value} syntax was only defined as working as the direct initializer for an explicitly-declared array.
As #pmg mentions in a comment, however, newer versions of C have a new syntax, the compound literal, which does let you, basically, "use the {value, value, value} syntax to create a hidden, unnamed array to do something else with". So you can in fact write
char *word2 = (char[]){'a', 'b', 'c', '\0'};
and this works just fine. It works in other contexts, too: for example, you can say things like
printf("%s\n", (char[]){'d', 'e', 'f', '\0'});
Going back to a side question you asked: when you wrote
char *word2 = {'a', 'b', 'c', '\0'};
the compiler said to itself, "Wait a minute, word2 is one thing, but the initializer has four things. So I'll throw away three, and warn the programmer that I'm doing so." It then did the equivalent of
char *word2 = {'a'};
and if you later tried something like
printf("%s", word2);
you got a crash when printf tried to access address 0x00000061.
In general, the type of the initializer must match the type of what is being initialized.
This works:
char *word1 = "abc";
Because a string constant has type array of char and such an array decays to type char * when used in an expression or initialization, so this matches the declared type.
This works:
char word2[] = {'a', 'b', 'c', '\0'};
Because an array of char is being initialized with an initializer list of characters (technically they have type int but are converted to char).
This gives a warning:
char *word2 = {'a', 'b', 'c', '\0'};
Because an initializer list is being used to initialize a type which is not an array or struct.
And this is OK:
char word1[] = "abc";
Because the C standard specifically allows initializing a char array with a string literal, as specified in section 6.7.9p14:
An array of character type may be initialized by a character string
literal or UTF−8 string literal, optionally enclosed in braces.
Successive bytes of the string literal (including the terminating null
character if there is room or if the array is of unknown size)
initialize the elements of the array.
I'm trying to create an array of char pointers in different ways, but only the first method works:
#include <stdio.h>
int main(){
char* a[] = {"hello"};
// works
char** b = {"hello"};
// warning: incompatible pointer types initializing
// 'char **' with an expression of type 'char [6]'
char c[][] = {"hello"};
// error: array has incomplete element type 'char []'
return 0;
}
What am I doing wrong?
Since neither element is an array, the C compiler does not recognize the {"hello"} syntax as an array, which will cause the code to break. If you do char** b = a you can observe that the syntax does in fact work.
When working with multidimensional arrays in C, every dimension except the first must be given a length, since the compiler cannot infer it. If you change it to char c[][6] = {"hello"} you can observe that it works.
All of these are incorrect at some extent.
a initializes an array of 1 single pointer char*, pointing at the string literal "hello". But since we aren't allowed to modify string literals, this should have been written as const char* a[] = {"hello"};.
b attempts to set a pointer-to-a-char-pointer to point at a string literal, which is not valid C. A conforming compiler must give you a message saying that the code isn't correct.
c is nonsense syntax, we cannot declare arrays of arrays with no dimensions known. Only the left-most dimension can be left blank, for the compiler to set implicitly.
This char* a[]; is an array of char*, aka array of char pointers.
This char** b; is a pointer to char*, or, in other words, a pointer to pointer to char. The initializer {"hello"} is wrong because its type is "array of char*".
This char c[][]; is an invalid declaration. You are declaring an array of arrays of char, but you don't specify the size of the subarray. You have to change it to something like char c[][N]; so that the sub-array has a known size, or even char c[M][N]; so that you know the entire size. Using the initializer here is OK, but note that it is interpreted as a completely different type than the first.
Correct initializations would be:
char* a[] = {"hello"};, this will create an array a with one element pointing to some memory location chosen by the compiler where the "hello" string is stored.
char** b = a; or char** b = &a[0];, this simply creates a pointer to pointer to char, so assigning a or &a[0] to it is the only reasonable thing here. There could be other applications, but it depends on the case.
char c[][10] = {"hello"};, this will create an array c with one sub-array of size 10 holding the elements {'h', 'e', 'l', 'l', 'o', '\0', '\0', '\0', '\0', '\0'}.
This question already has answers here:
What is the type of string literals in C and C++?
(4 answers)
Closed 3 years ago.
In C, why we are declaring arrays that has strings using
char* arr[] = {"PYTHON","JAVA","RUBY","C++"};
instead of
char arr[] = {"PYTHON","JAVA","RUBY","C++"};
Why it returns error "excess elements in char array initializer" and what does it means? Also what really happens underneath the first one?
Fundamentally, a string in C is an array of char.
Because of the correspondence between arrays and pointers in C, it is very common and very convenient to refer to an array using a pointer to its first element.
So although strings are fundamentally arrays of char, it is very common to refer to them using pointers to char.
So char *arr[] (which has type "array of pointer to char") is a good way to implement an array of strings.
You can't write
char arr[] = {"PYTHON", "JAVA", "RUBY", "C++"};
because it's a type mismatch (and correspondingly meaningless). If you declare an array of char, the initializer for it must be characters. So you could do
char arr[] = { 'P', 'Y', 'T', 'H', 'O', 'N' };
Or, as a special shortcut, you could do
char arr[] = "PYTHON";
where the initializer is a single string literal. This string literal is an array of char, so it's a fine initializer for arr which is an array of char. But there's no direct way to pack multiple strings (as in your original question) into a single array of char.
Of course, there's one more issue, and that's null termination. More precisely, a string in C is a null terminated array of char. So the "special shortcut"
char arr[] = "PYTHON";
is actually equivalent to
char arr[] = { 'P', 'Y', 'T', 'H', 'O', 'N', '\0' };
When I run the code:
char *abc="Goodbye";
for(i=0; i<=7; i++){
printf("%c\n",*(abc+i)); }
it runs without problem, but when I run the following:
char *der={'a','a','a','a','a'};
for(i=0; i<=4; i++){
printf("%c\n",*(der+i)); }
it doesn't show the correct results and I receive warnings.
So why is this happening since "Goodbye" and {'a','a','a','a','a'} are arrays of chars?
{'a','a','a','a','a'} is not an array but an initializer list, and it can be used to initialize either an aggregate or a scalar.
(Yes, int x = {'a'}; is valid.)
If it's used to initialize a scalar, such as a pointer, only the first value is used, so your declaration of der is equivalent to
char *der = 'a';
You can probably see what the problem is.
So today's programming lessons:
When your compiler warns you that something might be wrong, it probably is.
(Most experienced programmers treat warnings as errors, -Werr. It's an even more important habit for the inexperienced.)
If you don't understand what a warning means, don't do anything until you've found out.
One way to initialize the array - which is related to your second example - is by doing:
char foo[] = {'a', 'b', 'c'};
In this syntax, to the right side of = you provide the array elements as comma separeted values inside a { }. So in the above example, array foo has three elements. First is a, second is b, and third is c. If you wanted a \0 at the end, you need to do it explicitly as char foo[] = {'a', 'b', 'c', '\0'};.
Regarding your second example, answer by #molbdnilo already explains what is semantically wrong with your statment char *der={'a','a','a','a','a'};. If you want to define der as array and initialize it using { }, you can do:
char der[] = {'a','a','a','a','a'};
This way you are actually defining a char array and initializing it with the content you want.
Note that, you NEED to mention the array size if you are not initializing it while defining it. Which means:
char foo[]; // Will give error because no size is mentioned
char foo[10]; // Fine because size is given as 10
However, mentioning size is optional if you initialize the array while defining it, as we saw in the examples above. But, if you mention the size and if your initializer is smaller than the array size, remaining elements will be initialized to 0. Like:
char bar[10] = {'a', 'b', 'c', '\0'};
/* Your arrays' content will be 'a', 'b', 'c', '\0',
* and all remaning 6 elements will be 0
*/
What's wrong with the initialization of s2 in the code below?
#include <stdio.h>
int main()
{
char *s1 = "foo";
char *s2 = {'f', 'o', 'o', '\0'};
printf("%c\n", s1[1]);
printf("%c\n", s2[1]);
return 0;
}
I thought because I could initialize s1 the way I did above, the initialization of s2 should work fine as well.
But this code leads to compile-time warnings as well as run-time segmentation fault.
$ gcc foo.c
foo.c: In function ‘main’:
foo.c:6: warning: initialization makes pointer from integer without a cast
foo.c:6: warning: excess elements in scalar initializer
foo.c:6: warning: (near initialization for ‘s2’)
foo.c:6: warning: excess elements in scalar initializer
foo.c:6: warning: (near initialization for ‘s2’)
foo.c:6: warning: excess elements in scalar initializer
foo.c:6: warning: (near initialization for ‘s2’)
$ ./a.out
o
Segmentation fault (core dumped)
A better question to ask is why you can initialize a pointer with a string literal, because inability to initialize a pointer using an array initializer should come as no surprise: after all, arrays are not pointers.
String literals, however, are special. C lets you use them to initialize both arrays and pointers in order to provide a convenient syntax for creating null-terminated strings. That is why your first syntax works with both an array of characters and a character pointer.
Yes, because {'f', 'o', 'o', '\0'}; is a brace-enclosed intializer list. It cannot be used to initialize a pointer variable, in any way. However, it can be used to initialize a char array, like
char s2[] = {'f', 'o', 'o', '\0'};
because arrays are not pointers.
For your case, you can try using a compound literal instead as a workaround.
Something like
char *s2 = (char []){'f', 'o', 'o', '\0'};
Just to add some additional info, which can arise after reading this answer, like then why / how it is possible to initialize an array with string literal, like
char s3[] = "Hello";
is possible, because as mentioned in C11, chapter §6.7.9, initialization
An array of character type may be initialized by a character string literal or UTF−8 string
literal, optionally enclosed in braces. Successive bytes of the string literal (including the
terminating null character if there is room or if the array is of unknown size) initialize the
elements of the array.
Designated initializers {} initialize the data on the left of =. For example
int a[6] = { 0, 0, 15, 0, 29, 0 };
initializes array of 6 integers, that is why each of elements in initializer list is integer.
In your example of
char *s2 = {'f', 'o', 'o', '\0'};
you would therefore attempt to initialize pointer to char with integer values of characters which is not what you wanted. To initialize character array you can use
char s2[] = {'f', 'o', 'o', '\0'};
syntax, or
char s2[] = "foo"; /* also allowed in C, as dasblinkenlight noted - the better
* question would be why this works and Sourav Ghosh gave
* the answer */