Why can't multiple chars be assignable in an array? - c

Take the following example of trying to do character assignment to an array:
char newer_string[3][6];
newer_string[0] = "One";
newer_string[0] = "Two";
newer_string[0] = "Three";
This fails with the following error:
main.c:46:18: error: array type 'char [6]' is not assignable
Why is it necessary to add each individual one-at-a-time, for example"
char newer_string[3][6];
newer_string[0][0] = 'O';
newer_string[0][1] = 'n';
// ...etc...
What's the most common way to "append" items to an empty array?
Additionally, perhaps I'm misunderstanding the need for a 2D array (or what it even is), because I can also do something like:
char *newer_string[3];
newer_string[0] = "One";
newer_string[1] = "Two!!!";
newer_string[2] = "Three";
What would be the best way to assign multiple "words" to an array (a 2D character array? or 1D string-pointer array?)

Type Controls Everything
You cannot *assign" a string to an ARRAY. You can assign the address of a String-Literal to a pointer.
When you declare char newer_string[3][6]; you declare a 2D array of char (an array of 1D arrays). You can initialize the contents of an array at the time of declaration, but thereafter, you cannot assign the contents of the array. When you attempt to assign the address of the String-Literal "One" to an array, your compiler properly throws the error:
main.c:46:18: error: array type 'char [6]' is not assignable
(your compiler is correct, you are attempting to assign type char* to char[6])
Remember on access, except when used as the operand of sizeof or used to initialize an array, 6.3.2.1(p3) apples (see below) making a String-Literal type char* (which is a pointer to (the address of) the first character in the literal). A string-literal is immutable and any attempt to modify is Undefined C11 Standard - 6.4.5 String literals(p7), but a few non-conforming compilers provide for mutable string literals.
On access, your 2D array is converted to a pointer to the first 1D array. C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3) The type is char (*)[6] (pointer-to-array of char[6]). When you derefernce the pointer-to-array using the [..] operator, you are left with a simple array. You cannot assign the contents of an array.
To copy to the array, either use memcpy (or strcpy if a nul-terminated string is wanted), or loop over each index assigning each char.
Your Array of Pointers
With your edit, char *newer_string[3];, newer_string has type char *[3] (an array of pointers [3] -- e.g. an array of 3-pointers). You CAN assign the address of a String-Literal to a pointer. The types are compatible.
Look things over and let me know if you have further questions.

Related

Different Types Of Initializing Character Arrays in C

I was learning about pointers and strings.
I understood that,
Pointers and Arrays/Strings have similar behaviours.
array[] , *array , &array[0]. They all are one and the same.
Why does the three statements in this code work, and char * help one does not ?
#include <stdio.h>
void display(char*help){
for(int i=0; help[i]!='\0'; i++){
printf("%c", help[i]);
}
}
int main(){
// char help[] = "Help_Me"; //Works
// char help[] = {'H','e','l','p','_','M','e','\0'}; //Works
// char *help = "Help_Me"; //Works
char *help = {'H','e','l','p','_','M','e','\0'}; //Error
display(help);
}
Error Messages :
warning: initialization of 'char *' from 'int' makes pointer from integer without a cast
warning: excess elements in scalar initializer
Pointers and Arrays/Strings have similar behaviours.
Actually, no, I wouldn't agree with that. It is an oversimplification that hides important details. The true situation is that arrays have almost no behaviors of their own, but in most contexts, an lvalue designating an array is automatically converted to pointer to the first array element. The resulting pointer behaves like a pointer, of course, which is what may present the appearance that pointers and arrays have similar behaviors.
Additionally, arrays are objects, whereas strings are certain configurations of data that char arrays can contain. Although people sometimes conflate strings with the arrays containing them or with pointers to their first elements, that is not formally correct.
array[] , *array , &array[0]. They all are one and the same.
No, not at all, though the differences depend on the context in which those appear:
In a declaration of array (other than in a function prototype),
type array[] declares array as an array of type whose size will be determined from its initializer;
type *array declares array as a pointer to type; and
&array[0] is not part of any valid declaration of array.
In a function prototype,
type array[] is "adjusted" automatically as if it were type *array, and it therefore declares array as a pointer to type;
type *array declares array as a pointer to type; and
&array[0] is not part of any valid declaration of array.
In an expression,
array[] is invalid;
*array is equivalent to array[0], which designates the first element of array; and
&array[0] is a pointer to array[0].
Now, you ask,
Why does the three statements in this code work, and char * help one does not ?
"Help_Me" is a string literal. It designates a statically-allocated array just large enough to contain the specified characters plus a string terminator. As an array-valued expression, in most contexts it is converted to a pointer to its first element, and such a pointer is of the correct type for use in ...
// char *help = "Help_Me"; //Works
But the appearance of a string literal as the initializer of a char array ...
// char help[] = "Help_Me"; //Works
... is one of the few contexts where an array value is not automatically converted to a pointer. In that context, the elements of the array designated by the string literal are used to initialize the the array being declared, very much like ...
// char help[] = {'H','e','l','p','_','M','e','\0'}; //Works
. There, {'H','e','l','p','_','M','e','\0'} is an array initializer specifying values for 8 array elements. Note well that taken as a whole, it is not itself a value, just a syntactic container for eight values of type int (in C) or char (in C++).
And that's why this ...
char *help = {'H','e','l','p','_','M','e','\0'}; //Error
... does not make sense. There, help is a scalar object, not an array or a structure, so it takes only one value. And that value is of type char *. The warnings delivered by your compiler are telling you that eight values have been presented instead of one, and they have, or at least the one used for the initialization has, type int instead of type char *.
array[] , *array , &array[0]. They all are one and the same.
No. Presuming array names some array, array[] cannot be used in an expression (except where it might appear in some type description, such as a cast).
array by itself in an expression is automatically converted to a pointer to its first element except when it is the operand of sizeof or the operand of unary &. (Also, a string literal, such as "abc", denotes an array, and this array has another exception to when it is converted: When it is used to initialize an array.)
In *array, array will be automatically converted to a pointer, and then * refers to the element it points to. Thus *array refers to an element in an array; it is not a pointer to the array or its elements.
In &array[0], array[0] refers to the first element of the array, and then & takes its address, so &array[0] is a pointer to the first element of the array. This makes it equivalent to array in expressions, with the exceptions noted above. For example, void *p = array; and void *p = &array[0]; will initialize p to the same thing, a pointer to the first element of the array, because of the automatic conversion. However, size_t s = sizeof array; and size_t s = sizeof &array[0]; may initialize s to different values—the first to the size of the entire array and the second to the size of a pointer.
// char help[] = "Help_Me"; //Works
help is an array of char, and character arrays can be initialized with a string literal. This is a special rule for initializations.
// char help[] = {'H','e','l','p','_','M','e','\0'}; //Works
help is an array, and the initializer is a list of values for the elements of the array.
// char *help = "Help_Me"; //Works
help is a pointer, and "Help_Me" is a string literal. Because it is not in one of the exceptions—operand of sizeof, operand of unary &, or used to initialize an array—it is automatically converted to a pointer to its first element. Then help is initialized with that pointer value.
char *help = {'H','e','l','p','_','M','e','\0'}; //Error
help is a pointer, but the initializer is a list of values. There is only one thing to be initialized, a pointer, but there are multiple values listed for it, so that is an error. Also, a pointer should be initialized with a pointer value (an address or a null pointer constant), but the items in that list are integers. (Character literals are integers; their values are the codes for the characters.)
{'H','e','l','p','_','M','e','\0'} is not a syntax that creates a string or an array. It is a syntax that can be used to provide a list of values when initializing an object. So the compiler does not recognize it as a string or array and does not use it to initialize the pointer help.
Pointer is not the array and it cant be initialized like an array. You need to create an object, then you can assign its reference to the pointer.
char *help = (char[]){'H','e','l','p','_','M','e','\0'};

What is the difference between char*str={"foo",...} and char str[][5]={"foo",...} array definitions?

Case 1: When I write
char*str={"what","is","this"};
then str[i]="newstring"; is valid whereas str[i][j]='j'; is invalid.
Case 2: When I write
char str[][5]={"what","is","this"};
then str[i]="newstring"; is not valid whereas str[i][j]='J'; is valid.
Why is it so? I am a beginner who already get very confused after reading the other answers.
First of all: A suggestion: Please read about arrays are not pointers and vice-versa!!
That said, to enlighten this particular scenario,
In the first case,
char*str={"what","is","this"};
does not do what you think it does. It is a constraint violation, requiring a diagnostic from any conforming C implementation, as per chapter§6.7.9/P2:
No initializer shall attempt to provide a value for an object not contained within the entity
being initialized.
If you enable warnings, you'd (at least) see
warning: excess elements in scalar initializer
char*str={"what","is","this"};
However, a(ny) compiler with strict conformance turned on, should refuse to compile the code. In case, the compiler chose to compile and produce a binary anyway, the behavior is not withing the scope of definition of C language, it's up to the compiler implementation (and thus, can vary widely).
In this case, compiler decided this statement to make functionally only same as char*str= "what";
So, here str is a pointer to a char, which points to a string literal.
You can re-assign to the pointer,
str="newstring"; //this is valid
but, a statement like
str[i]="newstring";
would be invalid, as here, a pointer type is attempted to be converted and stored into a char type, where the types are not compatible. The compiler should throw a warning about the invalid conversion in this case.
Thereafter, a statement like
str[i][j]='J'; // compiler error
is syntactically invalid, as you're using the Array subscripting [] operator on something which is not "pointer to complete object type", like
str[i][j] = ...
^^^------------------- cannot use this
^^^^^^ --------------------- str[i] is of type 'char',
not a pointer to be used as the operand for [] operator.
On the other hand, in second case,
str is an array of arrays. You can change individual array elements,
str[i][j]='J'; // change individual element, good to go.
but you cannot assign to an array.
str[i]="newstring"; // nopes, array type is not an lvalue!!
Finally, considering you meant to write (as seen in comments)
char* str[ ] ={"what","is","this"};
in your first case, the same logic for arrays hold. This makes str an array of pointers. So, the array members, are assignable, so,
str[i]="newstring"; // just overwrites the previous pointer
is perfectly OK. However, the pointers, which are stored as array members, are pointers to string literal, so for the very same reason mentioned above, you invoke undefined behavior, when you want to modify one of the elements of the memory belonging to the string literal
str[i][j]='j'; //still invalid, as above.
The memory layout is different:
char* str[] = {"what", "is", "this"};
str
+--------+ +-----+
| pointer| ---> |what0|
+--------+ +-----+ +---+
| pointer| -------------> |is0|
+--------+ +---+ +-----+
| pointer| ----------------------> |this0|
+--------+ +-----+
In this memory layout, str is an array of pointers to the individual strings. Usually, these individual strings will reside in static storage, and it is an error to try to modify them. In the graphic, I used 0 to denote the terminating null bytes.
char str[][5] = {"what", "is", "this"};
str
+-----+
|what0|
+-----+
|is000|
+-----+
|this0|
+-----+
In this case, str is a contiguous 2D array of characters located on the stack. The strings are copied into this memory area when the array is initialized, and the individual strings are padded with zero bytes to give the array a regular shape.
These two memory layout are fundamentally incompatible with each other. You cannot pass either to a function that expects a pointer to the other. However, access to the individual strings is compatible. When you write str[1], you get a char* to the first character of a memory region containing the bytes is0, i.e. a C string.
In the first case, it is clear that this pointer is simply loaded from memory. In the second case, the pointer is created via array-pointer-decay: str[1] actually denotes an array of exactly five bytes (is000), which immediately decays into a pointer to its first element in almost all contexts. However, I believe that a full explanation of the array-pointer-decay is beyond the scope of this answer. Google array-pointer-decay if you are curious.
With the first you define a variable that is a pointer to a char, which is usually used as just a single string. It initializes the pointer to point to the string literal "what". The compiler should also complain that you have too many initializers in the list.
The second definition makes str an array of three arrays of five char. That is, it's an array of three five-character strings.
A little differently it can be seen something like this:
For the first case:
+-----+ +--------+
| str | --> | "what" |
+-----+ +--------+
And for the second you have
+--------+--------+--------+
| "what" | "is" | "this" |
+--------+--------+--------+
Also note that for the first version, with the pointer to a single string, the expression str[i] = "newstring" should also lead to warnings, as you try to assign a pointer to the single char element str[i].
That assignment is invalid in the second version as well, but for another reason: str[i] is an array (of five char elements) and you can't assign to an array, only copy to it. So you could try doing strcpy(str[i], "newstring") and the compiler will not complain. It's wrong though, because you try to copy 10 characters (remember the terminator) into an array of 5 characters, and that will write out of bounds leading to undefined behavior.
In the first declaration
char *str={"what","is","this"};
declares str a pointer to a char and is a scalar. The standard says that
6.7.9 Initialization (p11):
The initializer for a scalar shall be a single expression, optionally enclosed in braces. [...]
That said a scalar type can have braced enclosed initializer but with a single expression, but in case of
char *str = {"what","is","this"}; // three expressions in brace enclosed initializer
it is upto compilers that how it is going to handle this. Note that what happen to rest of the initializers is a bug. A confirming complier should give a diagnostic message.
[Warning] excess elements in scalar initializer
5.1.1.3 Diagnostics (P1):
A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint, even if the behavior is also explicitly specified as undefined or implementation-defined
You claim "str[i]="newstring"; is valid whereas str[i][j]='j'; is invalid."
str[i] is of char type and can hold only a char data type. Assigning "newstring" (which is of char *) is invalid. The statement str[i][j]='j'; is invalid as subscript operator can only be applied to an array or pointer data type.
You can make str[i]="newstring"; working by declaring str as an array of char *
char *str[] = {"what","is","this"};
In this case str[i] is of char * type and a string literal can be assigned to it but modifying the string literal str[i] points to will invoke undefined behavior. That said you can't do str[0][0] = 'W'.
The snippet
char str[][5]={"what","is","this"};
declare str as an array of arrays of chars. str[i] is actually an array and as arrays are non modifiable lvalues so you can't use them as a left operand of assignment operator. This makes str[i]="newstring"; invalid. While str[i][j]='J'; works because elements of an array can be modified.
Just because you said other answers are confusing me, lets see what is happening with a simpler example first
char *ptr = "somestring";
Here "somestring" is a string literal which is stored in read only data section of the memory. ptr is a pointer (allocated just like other variables in the same section of code) which is pointing to the first byte of that allocated memory.
Hence cnosider these two statements
char *ptr2 = ptr; //statement 1 OK
ptr[1] = 'a'; //statement 2 error
Statement 1 is doing a perfectly valid operation (assigning 1 pointer to another), but statement 2 is not a valid operation (trying to write into a read only location).
On the other hand if we write:
char ptr[] = "somestring";
Here ptr is not actually a pointer, but the name of an array(unlike the pointer it doesn't take extra space in the memory). It allocates the same number of bytes as required by "somestring" (not read only) and that's it.
Hence consider the same two statements and one extra statement
char *ptr2 = ptr; //statement 1 OK
ptr[1] = 'a'; //statement 2 OK
ptr = "someotherstring" //statement 3 error
Statement 1 is doing a perfectly valid operation (assigning array name to a pointer, array name returns the address of the 1st byte), statement 2 is also valid because the memory is not readonly.
Statement 3 is not a valid operation because here ptr is not a pointer, It can not point to some other memory location.
Now in this code,
char **str={"what","is","this"};
*str is a pointer (str[i] is same as *(str+i))
but in this code
char str[][] = {"what", "is", "this"};
str[i] is not a pointer. It is the name of an array.
The same thing as above follows.
To begin with
char*str={"what","is","this"};
is not even valid C code 1), so discussing it isn't very meaningful. For some reason, the gcc compiler lets this code through with only a warning. Do not ignore compiler warnings. When using gcc, make sure to always compile using -std=c11 -pedantic-errors -Wall -Wextra.
What gcc seems to do when encountering this non-standard code, is to treat it as if you had written char*str={"what"};. Which in turn is the same thing as char*str="what";. This is by no means guaranteed by the C language.
str[i][j] tries to indirect a pointer twice, even though it only has one level of indirection, and therefore you get a compiler error. It makes as little sense as typing
int array [3] = {1,2,3}; int x = array[0][0];.
As for the difference between char* str = ... and char str[] = ..., see FAQ: What is the difference between char s[] and char *s?.
Regarding the char str[][5]={"what","is","this"}; case, it creates an array of arrays (2D array). The inner-most dimension is set to 5 and the outer-most dimension is set automatically by the compiler depending on how many initializers the programmer provided. In this case 3, so the code is equivalent to char[3][5].
str[i] gives you array number i in the array of arrays. You cannot assign to arrays in C, because that's how the language is designed. Furthermore, it would be incorrect to do so for a string anyway, FAQ: How to correctly assign a new string value?
1) This is a constraint violation of C11 6.7.9/2. Also see 6.7.9/11.
To do away with the confusion, you must have proper understanding of pointers, arrays and initializers.
A common misconception amongst C programming beginners is that an array is equivalent to a pointer.
An array is a collection of items of the same type. consider the following declaration:
char arr[10];
This array contains 10 elements, each of type char.
An initializer list may be used to initialize an array in a convenient manner. The following initializes the array elements with the corresponding values of the initializer list:
char array[10] = {'a','b','c','d','e','f','g','h','i','\0'};
Arrays are not assignable, thus the use of initializer list is valid upon array declaration only.
char array[10];
array = {'a','b','c','d','e','f','g','h','i','\0'}; // Invalid...
char array1[10];
char array2[10] = {'a','b','c','d','e','f','g','h','i','\0'};
array1 = array2; // Invalid...; You cannot copy array2 to array1 in this manner.
After the declaration of an array, assignments to array members must be via the array indexing operator or its equivalent.
char array[10];
array[0] = 'a';
array[1] = 'b';
.
.
.
array[9] = 'i';
array[10] = '\0';
Loops are a common and convenient way of assigning values to array members:
char array[10];
int index = 0;
for(char val = 'a'; val <= 'i'; val++) {
array[index] = val;
index++;
}
array[index] = '\0';
char arrays may be initialized via string literals which are constant null terminated char arrays:
char array[10] = "abcdefghi";
However the following is not valid:
char array[10];
array = "abcdefghi"; // As mentioned before, arrays are not assignable
Now, let us get to pointers...
Pointers are variables that can store the address of another variable, usually of the same type.
Consider the following declaration:
char *ptr;
This declares a variable of type char *, a char pointer. That is, a pointer that may point to a char variable.
Unlike arrays, pointers are assignable. Thus the following is valid:
char var;
char *ptr;
ptr = &var; // Perfectly Valid...
As a pointer is not an array, a pointer may be assigned a single value only.
char var;
char *ptr = &var; // The address of the variable `var` is stored as a value of the pointer `ptr`
Recall that a pointer must be assigned a single value, thus the following is not valid, as the number of initializers is more than one:
char *ptr = {'a','b','c','d','\0'};
This is a constraint violation, but your compiler might just assign 'a' to ptr and ignore the rest. But even then, the compiler will warn you because character literals such as 'a' have int type by default, and is incompatible with the type of ptr which is char *.
If this pointer has been dereferenced at runtime, then it will result in a run-time error for accessing invalid memory, causing the program to crash.
In your example:
char *str = {"what", "is", "this"};
again, this is a constraint violation, but your compiler may assign the string what to str and ignore the rest, and simply display a warning:
warning: excess elements in scalar initializer.
Now, here is how we eliminate the confusion regarding pointers and arrays:
In some contexts, an array may decay to a pointer to the first element of the array. Thus the following is valid:
char arr[10];
char *ptr = arr;
by using the array name arr in an assignment expression as an rvalue, the array decays to a pointer to it's first element, which makes the previous expression equivalent to:
char *ptr = &arr[0];
Remember that arr[0] is of type char, and &arr[0] is its address that is of type char *, which is compatible with the variable ptr.
Recall that string literals are constant null terminated char arrays, thus the following expression is also valid:
char *ptr = "abcdefghi"; // the array "abcdefghi" decays to a pointer to the first element 'a'
Now, in your case, char str[][5] = {"what","is","this"}; is an array of 3 arrays, each contain 5 elements.
Since arrays are not assignable, str[i] = "newstring"; is not valid as str[i] is an array, but str[i][j] = 'j'; is valid since
str[i][j] is an array element that is NOT an array by itself, and is assignable.
Case 1:
When I write
char*str={"what","is","this"};
then str[i]="newstring"; is valid whereas str[i][j]='j'; is invalid.
Part I.I
>> char*str={"what","is","this"};
In this statement, str is a pointer to char type.
When compiling, you must be getting a warning message on this statement:
warning: excess elements in scalar initializer
char*str={"what","is","this"};
^
Reason for the warning is - You are providing more than one initializer to a scalar.
[Arithmetic types and pointer types are collectively called scalar types.]
str is a scalar and from C Standards#6.7.9p11:
The initializer for a scalar shall be a single expression, optionally enclosed in braces. ..
Furthermore, giving more than one initializer to a scalar is undefined behavior.
From C Standards#J.2 Undefined behavior:
The initializer for a scalar is neither a single expression nor a single expression enclosed in braces
Since it is undefined behavior as per the standard, there is no point in discussing it further. Discussing Part I.II and Part I.III with an assumption - char *str="somestring", just for better understanding of char * type.
Seems that you want to create an array of pointers to string. I have added a brief about the array of pointers to string, below in this post, after talking about both the cases.
Part I.II
>> then str[i]="newstring"; is valid
No, this is not valid.
Again, the compiler must be giving a warning message on this statement because of incompatible conversion.
Since str is a pointer to char type. Therefore, str[i] is a character at i places past the object pointed to by str [str[i] --> *(str + i)].
"newstring" is a string literal and a string literal decays into a pointer, except when used to initialize an array, of type char * and here you are trying to assign it to a char type. Hence the compiler reporting it as a warning.
Part I.III
>> whereas str[i][j]='j'; is invalid.
Yes, this is invalid.
The [] (subscript operator) can be used with array or pointer operands.
str[i] is a character and str[i][j] means you are using [] on char operand which is invalid. Hence the compiler reporting it as an error.
Case 2:
When I write
char str[][5]={"what","is","this"};
then str[i]="newstring"; is not valid whereas str[i][j]='J'; is valid.
Part II.I
>> char str[][5]={"what","is","this"};
This is absolutely correct.
Here, str is a 2D-array. Based on the number of initializers, the compiler will automatically set the first dimension.
The in-memory view of str[][5], in this case, would be something like this:
str
+-+-+-+-+-+
str[0] |w|h|a|t|0|
+-+-+-+-+-+
str[1] |i|s|0|0|0|
+-+-+-+-+-+
str[2] |t|h|i|s|0|
+-+-+-+-+-+
Based on initializer list, the respective elements of 2D-array will be initialized and the rest of the elements are set to 0.
Part II.II
>> then str[i]="newstring"; is not valid
Yes, this is not valid.
str[i] is a one-dimensional array.
As per the C Standards, an array is not a modifiable lvalue.
From C Standards#6.3.2.1p1:
An lvalue is an expression (with an object type other than void) that potentially designates an object;64) if an lvalue does not designate an object when it is evaluated, the behavior is undefined. When an object is said to have a particular type, the type is specified by the lvalue used to designate the object. A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const- qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const- qualified type.
Also, an array name convert to pointer that point to initial element of the array object except when it is the operand of the sizeof operator, the _Alignof operator or the unary & operator.
From C Standards#6.3.2.1p3:
Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ''array of type'' is converted to an expression with type ''pointer to type'' that points to the initial element of the array object and is not an lvalue.
Since str is already initialized and when you assign some other string literal to ith array of str, the string literal convert to a pointer which makes the assignment incompatible because you have lvalue of type char array and rvalue of type char *. Hence the compiler reporting it as an error.
Part II.III
>> whereas str[i][j]='J'; is valid.
Yes, this is valid as long as the i and j are valid values for given array str.
str[i][j] is of type char, so you can assign a character to it.
Beware, C does not check array boundaries and accessing an array out of bounds is undefined behavior which includes - it may fortuitously do exactly what the programmer intended or segmentation fault or silently generating incorrect results or anything can happen.
Assuming that in the Case 1, you want to create an array of pointers to string.
It should be like this:
char *str[]={"what","is","this"};
^^
The in-memory view of str will be something like this:
str
+----+ +-+-+-+-+--+
str[0]| |--->|w|h|a|t|\0|
| | +-+-+-+-+--+
+----+ +-+-+--+
str[1]| |--->|i|s|\0|
| | +-+-+--+
+----+ +-+-+-+-+--+
str[2]| |--->|t|h|i|s|\0|
| | +-+-+-+-+--+
+----+
"what", "is" and "this" are string literals.
str[0], str[1] and str[2] are pointers to the respective string literal and you can make them point to some other string as well.
So, this is perfectly fine:
str[i]="newstring";
Assuming i is 1, so str[1] pointer is now pointing to string literal "newstring":
+----+ +-+-+-+-+-+-+-+-+-+--+
str[1]| |--->|n|e|w|s|t|r|i|n|g|\0|
| | +-+-+-+-+-+-+-+-+-+--+
+----+
But you should not do this:
str[i][j]='j';
(assuming i=1 and j=0, so str[i][j] is first character of second string)
As per the standard attempting to modify a string literal results in undefined behavior because they may be stored in read-only storage or combined with other string literals.
From C standard#6.4.5p7:
It is unspecified whether these arrays are distinct provided their elements have the appropriate values. If the program attempts to modify such an array, the behavior is undefined.
Additional:
There is no native string type in C language. In C language, a string is a null-terminated array of characters. You should know the difference between arrays and pointers.
I would suggest you read following for better understanding about arrays, pointers, array initialization:
Array Initialization, check this.
Equivalence of pointers and arrays, check this and this.
case 1 :
char*str={"what","is","this"};
First of all above statement is not valid, read the warnings properly. str is single pointer, it can points to single char array at a time not to multiple char array.
bounty.c:3:2: warning: excess elements in scalar initializer [enabled by default]
str is a char pointer and it's stored in section section of RAM but it's contents are stored in code(Can't modify the content section of RAM because str is initialized with string(in GCC/linux).
as you stated str[i]="newstring"; is valid whereas str[i][j]='j'; is invalid.
str= "new string" is not causing modifying code/read-only section, here you are simply assigning new address to str that's why it's valid but
*str='j' or str[0][0]='j' is not valid because here you are modifying the read only section, trying to change first letter of str.
Case 2 :
char str[][5]={"what","is","this"};
here str is 2D array i.e str and str[0],str[1],str[2] itself are stored in stack section of RAM that means you can change each str[i] contents.
str[i][j]='w'; it's valid because you are trying to stack section contents which is possible. but
str[i]= "new string"; it's not possible because str[0] itself a array and array is const pointer(can't change the address), you can't assign new address.
Simply in first case str="new string" is valid because str is pointer, not an array and in second case str[0]="new string" is not valid because str is array not a pointer.
I hope it helps.

Char not working in multi dimensional array in C. Need clarification

Im trying to create a 2d array for multiple data types but it seems to not be accepting the char data type. Why is this?
struct {
union {
int ival;
float fval;
char cval[50];
} val;
} as[120][4];
as[0][1].val.cval = "Testtttt"; ***This does not work***
as[1][1].val.ival = 3; ***This works***
You are in c, thus you should use string.h when it comes to string handling!
Change this:
as[0][1].val.cval = "Testtttt";
to this:
strcpy(as[0][1].val.cval, "Testtttt");
by using strcpy(), instead of the assignment operator (this would work in c++, not in c).
Of course, alternative functions exist, such as strncpy()* and memcpy().
Moreover, since C string handling seems new to you, you must read about null terminated strings in C.
*Credits to #fukanchik who reminded me that
In C, this code
as[0][1].val.cval
can not be assigned to. Per the C Standard, 6.3.2.1 Lvalues, arrays, and function designators:
Except when it is the operand of the sizeof operator, the _Alignof
operator, or the unary & operator, or is a string literal used to
initialize an array, an expression that has type "array of type"
is converted to an expression with type "pointer to type" that
points to the initial element of the array object and is not an
lvalue.
Without getting too in-depth into the C Standard, an lvalue is something you can assign something to. Thus, this code
as[1][1].val.ival
represents an lvalue and you can assign 3 to it.
The reason an array can't be assigned to is because it decays to "an expression with type ‘‘pointer to type". In other words, a bare array like
as[0][1].val.cval
is treated as the address of the array.
And the address of the array is where it is and is not something that can be assigned to.
Your val.cval members are arrays of char. String literals also represent arrays of char. C does not support whole-array assignment, regardless of the type of the array elements.
You can copy the contents of one array to another in various ways. strcpy() will do it for null-terminated arrays of char. memcpy() and / or memmove() will do it more generally, and of course you can always write an element-by-element copy loop.
You cannot copy the contents of one array to another using the = operator; you must use a library function like strcpy (for strings) or memcpy (for anything else), or you must assign each element individually:
as[0][1].val.cval[0] = 'T';
as[0][1].val.cval[1] = 'e';
as[0][1].val.cval[2] = 's';
...
as[0][1].val.cval[7] = 't';
as[0][1].val.cval[8] = 0;
Remember that in C, a string is a sequence of character values terminated by a 0-valued byte. Strings (including string literals like "Testtttt") are stored as arrays of char, but not all arrays of char store a string.

Array syntax and pointers in C

Is my understanding of arrays in C correct?
Arrays are nothing more than a syntactic convenience such that, for instance, when you declare in your C code an array:
type my_array[x];
the compiler sees it as something equivalent to:
type *my_array = malloc(sizeof(*my_array) * x);
with a free system call that releases my_array once we leave the scope of my_array.
Once my_array is declared
my_array[y];
is nothing more but:
*(my_array + y)
Transposing this to character strings; I was also wondering what was happening behind the curtain with
char *my_string = "Hello"
and
my_string = "Hello"
No, an array object is an array object. C has some odd rules that make it appear that arrays and pointers are the same thing, or at least very similar, but they very definitely are not.
This declaration:
int my_array[100];
creates an array object; the object's size is 100 * sizeof (int). It does not create a pointer object.
There is no malloc(), even implicitly. Storage for my_array is allocated the same way as storage for any object declared in the same scope.
What may be confusing you is that, in most but not all contexts, an expression of array type is implicitly converted to a pointer to the array's first element. (This gives you a pointer value; there's still no pointer object.) This conversion doesn't happen if the array expression is the operand of a unary & or sizeof. &my_array gives you the address of the array, not of some nonexistent pointer obejct. sizeof my_array is the size of the entire array (100 * sizeof (int)`), not the size of a pointer.
Also, if you define a function parameter with an array type:
void func(int param[]) { ... }
it's adjusted at compile time to a pointer:
void func(int *param) { ... }
This isn't a conversion; in that context (and only in that context), int param[] really means int *param.
Also, array indexing:
my_array[3] = 42;
is defined in terms of pointer arithmetic -- which means that the prefix my_array has to be converted to a pointer before you can index into it.
The most important thing to remember is this: Arrays are not pointer. Pointers are not arrays.
Section 6 of the comp.lang.c FAQ explains all this very well.
Once my_array is declared
my_array[y];
is nothing more but :
*(my_array + y)
Yes, because my_array is converted to a pointer, and the [] operator is defined so that x[y] means *(x+y).
Transposing this to character strings; i was also wondering what was
happening behind the curtain with
char *my_string = "Hello"
and
my_string = "Hello"
"Hello" is a string literal. It's an expression of type char[6], referring to an anonymous statically allocated array object. If it appears on the RHS of an assignment or initializer, it's converted, like any array expression, to a pointer. The first line initializes my_string so it points to the first character of "Hello". The second is a pointer assignment that does the same thing.
So what about this?
char str[] = "Hello";
This is the third context in which array-to-pointer conversion doesn't happen. str takes its size from the size of the string literal, and the array is copied to str. It's the same as:
char str[] = { 'H', 'e', 'l', 'l', 'o', '\0' };
No!
type array[n] is a variable stored on stack
type *array is a pointer variable stored on the stack too. But after array = malloc(sizeof(*array) * n); it'll point to some data on the heap
If it walks like a duck, swims like a duck and flies like a duck, then it is a duck.
So, let's see. Arrays and pointers have some common attributes as you correctly described, however, you can see there are some differences. Read more here.

c string basics, why unassigned?

I am trying to learn the basics, I would think that declaring a char[] and assigning a string to it would work.
thanks
int size = 100;
char str[size];
str = "\x80\xbb\x00\xcd";
gives error "incompatible types in assignment". what's wrong?
thanks
You can use a string literal to initialize an array of char, but you can't assign an array of char (any more than you can assign any other array). OTOH, you can assign a pointer, so the following would be allowed:
char *str;
str = "\x80\xbb\x00\xcd";
This is actually one of the most difficult parts of learning a programming language.... str is an array, that is, a part of memory (size times a char, so size chars) that has been reserved and labeled as str. str[0] is the first character, str[1] the second... str[size-1] is the last one. str itself, without specifiying any character, is a pointer to the memory zone that was created when you did
char str[size]
As Jerry so clearly said, in C you can not initialize arrays that way. You need to copy from one array to other, so you can do something like this
strncpy(str, "\x80\xbb\x00\xcd", size); /* Copy up to size characters */
str[size-1]='\0'; /* Make sure that the string is null terminated for small values of size */
Summarizing: It's very important to make a difference between pointers, memory areas and array.
Good luck - I am pretty sure that in less time than you imagine you will be mastering these concepts :)
A char-array can be implicitely cast to a char* when used as Rvalue, but not when used as Lvalue - that's why the assignment won't work.
You cannot assign array contents using the =operator. That's just a fact of the C language design. You can initialize an array in the declaration, such as
char str[size] = "\x80\xbb\x00\xcd";
but that's a different operation from an assignment. And note that in this case, and extra '\0' will be added to the end of the string.
The "incompatible types" warning comes from how array expressions are treated by the language. First of all, string literals are stored as arrays of char with static extent (meaning they exist over the lifetime of the program). So the type of the string literal "\x80\xbb\x00\xcd" is "4 5-element array of char". However, in most circumstances, an expression of array type will implicitly be converted ("decay") from type "N-element array of T" to "pointer to T", and the value of the expression will be the address of the first element in the array. So, when you wrote the statement
str = "\x80\xbb\x00\xcd";
the type of the literal was implicitly converted from "4 5-element array of char" to "pointer to char", but the target of the assignment is type "100-element array of char", and the types are not compatible (above and beyond the fact that an array expression cannot be the target of the = operator).
To copy the contents of one array to another you would have to use a library function like memcpy, memmove, strcpy, etc. Also, for strcpy to function properly, the source string must be 0-terminated.
Edit per R's comment below, I've struck out the more dumbass sections of my answer.
To assign a String Literal to the str Array you can use a the String copy function strcpy.
char a[100] = "\x80\xbb\x00\xcd"; OR char a[] = "\x80\xbb\x00\xcd";
str is the name of an array. The name of an array is the address of the 0th element. Therefore, str is a pointer constant. You cannot change the value of a pointer constant, just like you cannot change a constant (you can't do 6 = 5, for example).

Resources