So here is the code:
#include <stdio.h>
int main() {
char str1[] ="Hello", str2[20] ="Hi";
char *p ="Hello", *s ="Hi";
str1 = "Adieu";
return 0;
}
Now my Book gives this reason
error, constant pointer cannot change
And when I run it, I get error as :
error: assignment to expression with array type
My question is why does my book says so ?, From where did pointers come here ?
The book is Let us C 18th edition (latest edition at the time the question was posted) by Yashavant P. Kanetkar incase you need refence.
In the line
str1 = "Adieu";
the array str will decay to a pointer to the first element of the array, which is an rvalue (not an lvalue) and therefore cannot be modified.
This behavior is specified in §6.3.2.1 ¶3 of the ISO C11 standard:
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. [...]
Note that in the quote above, "initialize an array" means initialize it inside a declaration, not assign it a value outside a declaration (which would be an "assignment", not an "initialization").
For this reason, the error message in your book
error, constant pointer cannot change
is correct. An rvalue cannot be modified and it is therefore not wrong to describe it as "constant".
However, the error message from your compiler
error: assignment to expression with array type
is also correct and probably more useful.
str1[] = "Hello";
str1 is a character array which has been initialized from a string literal. It has automatic storage duration and is modifiable. The string literal "hello" on the other hand, is placed in the text segment(read-only-memory), although the compiler may choose not to, and as such it's illegal to modify it, and doing so results in undefined behaviour. str1[ ] on the other hand, stores a copy of the literal. So you're only modifying the copy, not the literal itself. But C doesn't allow you to modify it like this:
str2 = str1;
You can modify it by assigning a single element at a time or by using string functions such as strcpy, strnpcy, et cetera declared in the string.h header file to copy one string to another string. Or you may use a pointer to a character, which is modifiable and can be changed to point to some other location in memory.
As of that book, it's infamous here, being the cause of too many errors on stackoverflow. The author has an incomplete understanding of the language. Consider picking up another book, preferably from The Definitive C Book Guide and List.
Related
I use to code pointers like this when I need to change the original memory address of a pointer.
Example:
static void get_line_func(struct data_s *data,
char **begin)
{
data->slot_number = strsep(&(*(begin)), "/");
data->protocol = *begin;
strsep(&(*begin), ">");
data->service_name = strsep(&(*begin), "\n");
}
I mean, isn't &(*foo) == foo?
There is no reason to do that directly. However, the combination can arise in machine-generated code (such as the expansion of a preprocessor macro).
For instance, suppose we have a macro do_something_to(obj) which expects the argument expression obj to designate an object. Suppose somewhere in its expansion, this macro takes the address of the object using &(obj). Now suppose we would like to apply the macro to an object which we only hold via a pointer ptr. To designate the object, we must use the expression *ptr so that we use the macro as do_something_to(*ptr). That of course means that&(*ptr) now occurs in the program.
The status of the expression &*ptr has changed over the years. I seem to remember that in the ANSI C 89 / ISO C90 dialect, the expression produced undefined behavior if ptr was an invalid pointer.
In ISO C11 the following is spelled out (and I believe nearly the same text is in C99), requiring &* not to dereference the pointer: "if the operand [of the address-of unary & operator] is the result of a unary * operator,
neither that operator nor the & operator is evaluated and the result is as if both were
omitted, except that the constraints on the operators still apply and the result is not an lvalue". Thus in the modern C dialect, the expression &*ptr doesn't dereference ptr, hence has defined behavior even if that value is null.
What does that mean? "constraints still apply" basically means that it still has to type check. Just because &*P doesn't dereference P doesn't mean that P can be a double or a struct; it has to be a pointer.
The "result is not an lvalue" part is potentially useful. If we have a pointer P which is an value, if we wrap it in the expression &*P, we obtain the same pointer value as a non-lvalue. There are other ways to obtain the value of P as a non-lvalue, but &*P is a "code golfed" solution to the problem requiring only two characters, and having the property that it will remain correct even if P changes from one pointer type to another.
This question already has answers here:
problems with char array = char array
(2 answers)
Closed 7 years ago.
Well here is my first post. I've been trying to do this choice choosing thing and I want the user to choose only numbers instead of typing them down (easier) but when I want the numbers to equal a string, it says "array type char[30] is not assignable". Even if at the back I put semi-colon or not.
#include <stdio.h>
int main() {
int choice1;
char word[30];
printf("You have three choice.\n");
printf("[1] Jump [2] Run [3] Dance\n");
scanf("%d",&choice1);
if (choice1 == 1)
{
word = "Jump" //Error #1
}
else if (choice1 == 2)
{
word = "Eat" //Error #2
}
else if (choice1 == 3)
{
word = "Sleep"; //Error #3
}
printf("You will now be %sing",word);
}
You can't assign to an array, only copy to it.
Use strcpy instead, like
strcpy(word, "Jump");
TL;DR answer : An array name is not a modifiable lvalue. So, you cannot use the assignment operator (=) on that.
To copy the content into the array, you need to use strcpy() from string.h (char array) or memcpy() in general.
Now, to elaborate the actual reason behind the error message, quoting C11, chapter §6.5.16, Assignment operators
assignment operator shall have a modifiable lvalue as its left operand.
and then, quoting chapter §6.3.2.1 from the same standard,
A modifiable lvalue is an lvalue that does not have array type, [....]
So, an array name is not a modifiable lvalue hence, you cannot assign anything to it. This is the reason behind the error message.
The = operator cannot be used to copy the contents of one array to the other; you must use a library function like strcpy or strcat for strings, memcpy for non-strings (or assign array elements individually).
This is a consequence of how C treats array expressions. An array expression is defined by the language standard to be a non-modifiable lvalue; it's an lvalue because it refers to an object in memory, but it may not be the target of an assignment.
The array subscript operation a[i] is defined as *(a + i); that is, given the array address a, offset i elements from that address and dereference the result. Since the array expression a is treated as a pointer, most people think a variable stores a pointer to the first element of the array, but it doesn't. All that gets stored are the array elements themselves.
Instead, whenever the compiler sees an array expression in a statement, it converts that expression from type "N-element array of T" to "pointer to T", and the value of the expression becomes the address of the first element of the array (unless the expression is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration).
And this is why an array expression like word cannot be the target of an assignment; there's nothing to assign to. There's no object word that exists independently of word[0], word[1], etc.
When you write
word = "Jump";
the type of the expression "Jump" is converted from "5-element array of char" to "pointer to char", and the value of the expression is the address of the first element of the array. And you're trying to assign that pointer value to an array object, which a) isn't a pointer, and b) cannot be assigned to anyway.
Use strcpy from <string.h>-
strcpy(word,"Jump");
And similar for rest of them.
You just can't do word ="Jump". As the contents are modifiable, the arrays themselves are not.
This question already has answers here:
problems with char array = char array
(2 answers)
Closed 7 years ago.
Well here is my first post. I've been trying to do this choice choosing thing and I want the user to choose only numbers instead of typing them down (easier) but when I want the numbers to equal a string, it says "array type char[30] is not assignable". Even if at the back I put semi-colon or not.
#include <stdio.h>
int main() {
int choice1;
char word[30];
printf("You have three choice.\n");
printf("[1] Jump [2] Run [3] Dance\n");
scanf("%d",&choice1);
if (choice1 == 1)
{
word = "Jump" //Error #1
}
else if (choice1 == 2)
{
word = "Eat" //Error #2
}
else if (choice1 == 3)
{
word = "Sleep"; //Error #3
}
printf("You will now be %sing",word);
}
You can't assign to an array, only copy to it.
Use strcpy instead, like
strcpy(word, "Jump");
TL;DR answer : An array name is not a modifiable lvalue. So, you cannot use the assignment operator (=) on that.
To copy the content into the array, you need to use strcpy() from string.h (char array) or memcpy() in general.
Now, to elaborate the actual reason behind the error message, quoting C11, chapter §6.5.16, Assignment operators
assignment operator shall have a modifiable lvalue as its left operand.
and then, quoting chapter §6.3.2.1 from the same standard,
A modifiable lvalue is an lvalue that does not have array type, [....]
So, an array name is not a modifiable lvalue hence, you cannot assign anything to it. This is the reason behind the error message.
The = operator cannot be used to copy the contents of one array to the other; you must use a library function like strcpy or strcat for strings, memcpy for non-strings (or assign array elements individually).
This is a consequence of how C treats array expressions. An array expression is defined by the language standard to be a non-modifiable lvalue; it's an lvalue because it refers to an object in memory, but it may not be the target of an assignment.
The array subscript operation a[i] is defined as *(a + i); that is, given the array address a, offset i elements from that address and dereference the result. Since the array expression a is treated as a pointer, most people think a variable stores a pointer to the first element of the array, but it doesn't. All that gets stored are the array elements themselves.
Instead, whenever the compiler sees an array expression in a statement, it converts that expression from type "N-element array of T" to "pointer to T", and the value of the expression becomes the address of the first element of the array (unless the expression is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration).
And this is why an array expression like word cannot be the target of an assignment; there's nothing to assign to. There's no object word that exists independently of word[0], word[1], etc.
When you write
word = "Jump";
the type of the expression "Jump" is converted from "5-element array of char" to "pointer to char", and the value of the expression is the address of the first element of the array. And you're trying to assign that pointer value to an array object, which a) isn't a pointer, and b) cannot be assigned to anyway.
Use strcpy from <string.h>-
strcpy(word,"Jump");
And similar for rest of them.
You just can't do word ="Jump". As the contents are modifiable, the arrays themselves are not.
i was trying to reassign a string to a pre-initialized array a[], and all i could get was an error
main()
{
char a[] = "Sunstroke";
char *b = "Coldwave";
a = "Coldwave";
b = "Sunstroke";
printf("\n %s %s",a,b);
}
[Error]: incompatible types when assigning to type 'char[10]' from type 'char *'.. i searched for this but was unable to find any reason.. i also tried to re-assign it by redeclaration like
char a[] = "Sunstroke";
but it didnt worked...
but in case of a pointer it was possible as in above program..
To understand what's going on here, two language rules are important:
Arrays are not assignable.
An array can be converted to a pointer to its first element.
It's also important to understand what a string literal like "Sunstroke" is. It's a static array of constant characters, large enough to hold all the characters of a string with a terminator at the end. So in this case, it's a const char[10] array, containing the nine characters followed by the zero-valued terminator. Being static, the array is stored somewhere in memory for the lifetime of the program.
char a[] = "Sunstroke";
This creates a local array, and initialises it by copying the characters from the string literal.
char *b = "Coldwave";
This creates a pointer, and initialises it to point to the literal itself. Note that this is dangerous: the literal is const, but the pointer isn't, so you can write code that attempts to modify the literal, giving undefined behaviour. This conversion is deprecated (certainly in C++, I'm not sure about C), so the compiler should give you a warning. You have enabled all the compiler warnings you can, haven't you?
a = "Coldwave";
This attempts to reassign the array, but fails because arrays aren't assignable. There's no particularly good reason why they aren't; that's just the way the languages evolved.
b = "Sunstroke";
This reassigns the pointer to point to a different literal. That's fine (apart from the lack of const noted above).
If you need to manipulate strings, then:
in C you'll need to carefully create arrays large enough for your needs, and use the library functions in <string.h> (or your own handcrafted code) to manipulate the characters in those arrays;
in C++, use the std::string class to handle memory management, assignment, etc. for you.
Hard-coded string literals such as "Coldwave" are actually char[] (char array) types -- but it is undefined behavior to modify them (C99:6.4.5.6). Note that below, however, b is still a char* (char pointer):
char *b = "Coldwave";
To which a char[] has been assigned. That's okay. It is different than this though:
char a[] = "Coldwave";
Which is an initialization of a char[]. You can only initialize a variable once, when it is declared, and initialization is the only circumstance in which you can populate an array or other compound type (such as a struct) via assignment like this. You could not do this, however:
char c[] = a;
Because when used on the right hand side of an assignment, array variables function as pointers to the array they represent, which is why char *b = a works.
So the reason you can't do this with the variables from above:
a = b;
// or
a = "Sunstroke";
Is because that would be assigning a char* to a char[] -- no good; you can only do it the other way around.
In the case of C if we look at c99 draft standard section 6.5.16 Assignment operators paragraph 2 says:
An assignment operator shall have a modifiable lvalue as its left operand.
and section 6.3.2.1 Lvalues, arrays, and function designators paragraph 1 says:
[...]A modifiable lvalue is an lvalue that does not have array type[...]
So since arrays are not modifiable lvalues you can not assign to them. As for initialization section 6.7.8 Initialization paragraph 14 says:
An array of character type may be initialized by a character string literal[...]
In the C++ draft standard the relevant sections are 4.2 Array-to-pointer conversion paragraph 1 which says:
An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to a prvalue of type “pointer to T”. The result is a pointer to the first element of the array.
a prvalue is a pure rvalue and section 5.17 Assignment and compound assignment operators paragraph 1 which says:
[...]All require a modifiable lvalue as their left operand[...]
Let me simplify the program to:
char a[] = "Sunstroke";
char *b = a;
Assume that the address of a is 100, then in memory, it looks like this (illustrating only the size of pointer and endian-ness etc. may vary):
[S] [u] [n] [s] [t] [r] [o] [k] [e] [\0] ... [0] [0] [0] [100]
100 101 102 103 104 105 106 107 108 109 200
^ ^
| |
a b
As long as the life cycle of the array, a will always be the same place, you can't modify it.
b, on the other hand, is a pointer that contains the address of the array, you can modify the value of b to point to other places.
The following compiles and prints "string" as an output.
#include <stdio.h>
struct S { int x; char c[7]; };
struct S bar() {
struct S s = {42, "string"};
return s;
}
int main()
{
printf("%s", bar().c);
}
Apparently this seems to invokes an undefined behavior according to
C99 6.5.2.2/5 If an attempt is made to modify the result of a function
call or to access it after the next sequence point, the behavior is
undefined.
I don't understand where it says about "next sequence point". What's going on here?
You've run into a subtle corner of the language.
An expression of array type is, in most contexts, implicitly converted to a pointer to the first element of the array object. The exceptions, none of which apply here, are:
When the array expression is the operand of a unary & operator (which yields the address of the entire array);
When it's the operand of a unary sizeof or (as of C11) _Alignof operator (sizeof arr yields the size of the array, not the size of a pointer); and
When it's a string literal in an initializer used to initialize an array object (char str[6] = "hello"; doesn't convert "hello" to a char*.)
(The N1570 draft incorrectly adds _Alignof to the list of exceptions. In fact, for reasons that are not clear, _Alignof can only be applied to a type name, not to an expression.)
Note that there's an implicit assumption: that the array expression refers to an array object in the first place. In most cases, it does (the simplest case is when the array expression is the name of a declared array object) -- but in this one case, there is no array object.
If a function returns a struct, the struct result is returned by value. In this case, the struct contains an array, giving us an array value with no corresponding array object, at least logically. So the array expression bar().c decays to a pointer to the first element of ... er, um, ... an array object that doesn't exist.
The 2011 ISO C standard addresses this by introducing "temporary lifetime", which applies only to "A non-lvalue expression with structure or union type, where the structure or union
contains a member with array type" (N1570 6.2.4p8). Such an object may not be modified, and its lifetime ends at the end of the containing full expression or full declarator.
So as of C2011, your program's behavior is well defined. The printf call gets a pointer to the first element of an array that's part of a struct object with temporary lifetime; that object continues to exist until the printf call finishes.
But as of C99, the behavior is undefined -- not necessarily because of the clause you quote (as far as I can tell, there is no intervening sequence point), but because C99 doesn't define the array object that would be necessary for the printf to work.
If your goal is to get this program to work, rather than to understand why it might fail, you can store the result of the function call in an explicit object:
const struct s result = bar();
printf("%s", result.c);
Now you have a struct object with automatic, rather than temporary, storage duration, so it exists during and after the execution of the printf call.
The sequence point occurs at the end of the full expression- i.e., when printf returns in this example. There are other cases where sequence points occur
Effectively, this rule states that function temporaries do not live beyond the next sequence point- which in this case, occurs well after it's use, so your program has quite well-defined behaviour.
Here's a simple example of not well-defined behaviour:
char* c = bar().c; *c = 5; // UB
Here, the sequence point is met after c is created, and the memory it points to is destroyed, but we then attempt to access c, resulting in UB.
In C99 there is a sequence point at the call to a function, after the arguments have been evaluated (C99 6.5.2.2/10).
So, when bar().c is evaluated, it results in a pointer to the first element in the char c[7] array in the struct returned by bar(). However, that pointer gets copied into an argument (a nameless argument as it happens) to printf(), and by the time the call is actually made to the printf() function the sequence point mentioned above has occurred, so the member that the pointer was pointing to may no longer be alive.
As Keith Thomson mentions, C11 (and C++) make stronger guarantees about the lifetime of temporaries, so the behavior under those standards would not be undefined.