Ambiguity with const keyword in C - c

I have some code:
int main() {
const char *string1 = "foo";
char *string2 = "bar";
string1 = string2;
return 0;
}
When I build it, no warnings are raised. However, with
int main() {
const char *string1 = "foo";
char *string2 = "bar";
string2 = string1;
return 0;
}
A warning is raised:
warning: assignment discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
string2 = string1;
^
With my current basic knowledge of C, I would have thought that string1 would not be reassignable since it is const. To my surprise, it was the code in the first snippet that built with no warnings and the second code snippet the opposite.
Any explanations of what is going on? Thanks!

That is perfectly ok to do. The (potential) problem here is that the strings you're pointing at are string literals, so modifying them would invoke undefined behavior.
What you're mixing it up with is probably this:
const char *str1 - str1 is a pointer to const char
char * const str2 - str2 is a const pointer to char
const char * const str3 - str3 is a const pointer to const char
str1 can be reassigned to point at something else, but since it is a pointer to const char it cannot be used as an l-value, so *str1 = <something> is forbidden. str2 on the other hand cannot be reassigned to point at something else, but it can be used as an l-value. With str3 you cannot do any of those.
Also, note that const char *str and char const *str are equivalent. To find out what a declaration does, or to find out how to declare something, use this site: https://cdecl.org/

I would have thought that string1 would not be reassignable since it is const.
But it isn't!
It's a non-const pointer, pointing to a const thing (specifically, const chars).
The only reason your code is troublesome, is that you should not implicitly convert a const char* to char*, for fairly obvious reasons — it would defeat the purpose of const-safety to just drop const on a pointee type whenever you liked! That's why you're getting a warning.
Going the other way around, to add constness, as in your first example, is fine.
tl;dr: it's not your assignment; it's what you're assigning.

Related

Re-Initializing a const char array in C is not giving error

While initializing a const char array, I tried to change the string and I was able to change it without any issue.
I was learning how to initialize a const char array.
I think I am doing some mistake here which I am not able to find.
int main(int argc, char const *argv[])
{
const char *strs[10];
strs[0] = "wwww.google.com";
printf("%s\n", strs[0]);
strs[1] = "https://wwww.google.com";
strs[0] = "ss";
printf("%s\n", strs[0]);
return 0;
}
Output:
1st init: wwww.google.com
2nd init: ss -> Here, I expect it to throw error
const char* s = "Hi";
tells the compiler that the content that the pointer points to is constant. This means that s[0] = 'P'; will result in a compilation error. But you can modify the pointer. On the other hand,
char* const s = "Hi";
tells the compiler that that the pointer is constant. This means that s = "Pi"; will result in a compilation error. But no compilation error will be thrown when you try to modify the string*
Your code depicts the former behaviour, not the latter as you seem to have thought
* Modifying string literals will invoke Undefined Behaviour
const char *strs[10];
strs is an array of 10 pointers to const char. You can change the pointers; you cannot change the chars
strs[2] = NULL; // ok: change the pointer
strs[0][0] = '#'; // wrong; cannot change the char
Maybe try
const char * const strs[10] = {"www.google.com",
"https://www.google.com",
"www.google.com/",
"https://www.google.com/",
NULL, NULL };
which makes strs an array of 10 read-only pointers to const char. You cannot change the pointers after initialization.
To put this in simple English(not necessarily 100% accurate but serves to conceptualise), this
const char *strs[10];
initialises a contant array strs which contains none constant elements. Thus, the elements in the array can be changed but the array itself cannot be changed

Difference between char* and const char* [duplicate]

What's the difference between
char* name
which points to a constant string literal, and
const char* name
char* is a mutable pointer to a mutable character/string.
const char* is a mutable pointer to an immutable character/string. You cannot change the contents of the location(s) this pointer points to. Also, compilers are required to give error messages when you try to do so. For the same reason, conversion from const char * to char* is deprecated.
char* const is an immutable pointer (it cannot point to any other location) but the contents of location at which it points are mutable.
const char* const is an immutable pointer to an immutable character/string.
char *name
You can change the char to which name points, and also the char at which it points.
const char* name
You can change the char to which name points, but you cannot modify the char at which it points.
correction: You can change the pointer, but not the char to which name points to (https://msdn.microsoft.com/en-us/library/vstudio/whkd4k6a(v=vs.100).aspx, see "Examples"). In this case, the const specifier applies to char, not the asterisk.
According to the MSDN page and http://en.cppreference.com/w/cpp/language/declarations, the const before the * is part of the decl-specifier sequence, while the const after * is part of the declarator.
A declaration specifier sequence can be followed by multiple declarators, which is why const char * c1, c2 declares c1 as const char * and c2 as const char.
EDIT:
From the comments, your question seems to be asking about the difference between the two declarations when the pointer points to a string literal.
In that case, you should not modify the char to which name points, as it could result in Undefined Behavior.
String literals may be allocated in read only memory regions (implementation defined) and an user program should not modify it in anyway. Any attempt to do so results in Undefined Behavior.
So the only difference in that case (of usage with string literals) is that the second declaration gives you a slight advantage. Compilers will usually give you a warning in case you attempt to modify the string literal in the second case.
Online Sample Example:
#include <string.h>
int main()
{
char *str1 = "string Literal";
const char *str2 = "string Literal";
char source[] = "Sample string";
strcpy(str1,source); //No warning or error, just Undefined Behavior
strcpy(str2,source); //Compiler issues a warning
return 0;
}
Output:
cc1: warnings being treated as errors
prog.c: In function ‘main’:
prog.c:9: error: passing argument 1 of ‘strcpy’ discards qualifiers from pointer target type
Notice the compiler warns for the second case but not for the first.
char mystring[101] = "My sample string";
const char * constcharp = mystring; // (1)
char const * charconstp = mystring; // (2) the same as (1)
char * const charpconst = mystring; // (3)
constcharp++; // ok
charconstp++; // ok
charpconst++; // compile error
constcharp[3] = '\0'; // compile error
charconstp[3] = '\0'; // compile error
charpconst[3] = '\0'; // ok
// String literals
char * lcharp = "My string literal";
const char * lconstcharp = "My string literal";
lcharp[0] = 'X'; // Segmentation fault (crash) during run-time
lconstcharp[0] = 'X'; // compile error
// *not* a string literal
const char astr[101] = "My mutable string";
astr[0] = 'X'; // compile error
((char*)astr)[0] = 'X'; // ok
In neither case can you modify a string literal, regardless of whether the pointer to that string literal is declared as char * or const char *.
However, the difference is that if the pointer is const char * then the compiler must give a diagnostic if you attempt to modify the pointed-to value, but if the pointer is char * then it does not.
CASE 1:
char *str = "Hello";
str[0] = 'M' //Warning may be issued by compiler, and will cause segmentation fault upon running the programme
The above sets str to point to the literal value "Hello" which is hard-coded in the program's binary image, which is flagged as read-only in memory, means any change in this String literal is illegal and that would throw segmentation faults.
CASE 2:
const char *str = "Hello";
str[0] = 'M' //Compile time error
CASE 3:
char str[] = "Hello";
str[0] = 'M'; // legal and change the str = "Mello".
The question is what's the difference between
char *name
which points to a constant string literal, and
const char *cname
I.e. given
char *name = "foo";
and
const char *cname = "foo";
There is not much difference between the 2 and both can be seen as correct. Due to the long legacy of C code, the string literals have had a type of char[], not const char[], and there are lots of older code that likewise accept char * instead of const char *, even when they do not modify the arguments.
The principal difference of the 2 in general is that *cname or cname[n] will evaluate to lvalues of type const char, whereas *name or name[n] will evaluate to lvalues of type char, which are modifiable lvalues. A conforming compiler is required to produce a diagnostics message if target of the assignment is not a modifiable lvalue; it need not produce any warning on assignment to lvalues of type char:
name[0] = 'x'; // no diagnostics *needed*
cname[0] = 'x'; // a conforming compiler *must* produce a diagnostic message
The compiler is not required to stop the compilation in either case; it is enough that it produces a warning for the assignment to cname[0]. The resulting program is not a correct program. The behaviour of the construct is undefined. It may crash, or even worse, it might not crash, and might change the string literal in memory.
The first you can actually change if you want to, the second you can't. Read up about const correctness (there's some nice guides about the difference). There is also char const * name where you can't repoint it.
Actually, char* name is not a pointer to a constant, but a pointer to a variable. You might be talking about this other question.
What is the difference between char * const and const char *?
I would add here that the latest compilers, VS 2022 for instance, do not allow char* to be initialized with a string literal. char* ptr = "Hello"; throws an error whilst const char* ptr = "Hello"; is legal.

Difference between char* and const char*?

What's the difference between
char* name
which points to a constant string literal, and
const char* name
char* is a mutable pointer to a mutable character/string.
const char* is a mutable pointer to an immutable character/string. You cannot change the contents of the location(s) this pointer points to. Also, compilers are required to give error messages when you try to do so. For the same reason, conversion from const char * to char* is deprecated.
char* const is an immutable pointer (it cannot point to any other location) but the contents of location at which it points are mutable.
const char* const is an immutable pointer to an immutable character/string.
char *name
You can change the char to which name points, and also the char at which it points.
const char* name
You can change the char to which name points, but you cannot modify the char at which it points.
correction: You can change the pointer, but not the char to which name points to (https://msdn.microsoft.com/en-us/library/vstudio/whkd4k6a(v=vs.100).aspx, see "Examples"). In this case, the const specifier applies to char, not the asterisk.
According to the MSDN page and http://en.cppreference.com/w/cpp/language/declarations, the const before the * is part of the decl-specifier sequence, while the const after * is part of the declarator.
A declaration specifier sequence can be followed by multiple declarators, which is why const char * c1, c2 declares c1 as const char * and c2 as const char.
EDIT:
From the comments, your question seems to be asking about the difference between the two declarations when the pointer points to a string literal.
In that case, you should not modify the char to which name points, as it could result in Undefined Behavior.
String literals may be allocated in read only memory regions (implementation defined) and an user program should not modify it in anyway. Any attempt to do so results in Undefined Behavior.
So the only difference in that case (of usage with string literals) is that the second declaration gives you a slight advantage. Compilers will usually give you a warning in case you attempt to modify the string literal in the second case.
Online Sample Example:
#include <string.h>
int main()
{
char *str1 = "string Literal";
const char *str2 = "string Literal";
char source[] = "Sample string";
strcpy(str1,source); //No warning or error, just Undefined Behavior
strcpy(str2,source); //Compiler issues a warning
return 0;
}
Output:
cc1: warnings being treated as errors
prog.c: In function ‘main’:
prog.c:9: error: passing argument 1 of ‘strcpy’ discards qualifiers from pointer target type
Notice the compiler warns for the second case but not for the first.
char mystring[101] = "My sample string";
const char * constcharp = mystring; // (1)
char const * charconstp = mystring; // (2) the same as (1)
char * const charpconst = mystring; // (3)
constcharp++; // ok
charconstp++; // ok
charpconst++; // compile error
constcharp[3] = '\0'; // compile error
charconstp[3] = '\0'; // compile error
charpconst[3] = '\0'; // ok
// String literals
char * lcharp = "My string literal";
const char * lconstcharp = "My string literal";
lcharp[0] = 'X'; // Segmentation fault (crash) during run-time
lconstcharp[0] = 'X'; // compile error
// *not* a string literal
const char astr[101] = "My mutable string";
astr[0] = 'X'; // compile error
((char*)astr)[0] = 'X'; // ok
In neither case can you modify a string literal, regardless of whether the pointer to that string literal is declared as char * or const char *.
However, the difference is that if the pointer is const char * then the compiler must give a diagnostic if you attempt to modify the pointed-to value, but if the pointer is char * then it does not.
CASE 1:
char *str = "Hello";
str[0] = 'M' //Warning may be issued by compiler, and will cause segmentation fault upon running the programme
The above sets str to point to the literal value "Hello" which is hard-coded in the program's binary image, which is flagged as read-only in memory, means any change in this String literal is illegal and that would throw segmentation faults.
CASE 2:
const char *str = "Hello";
str[0] = 'M' //Compile time error
CASE 3:
char str[] = "Hello";
str[0] = 'M'; // legal and change the str = "Mello".
The question is what's the difference between
char *name
which points to a constant string literal, and
const char *cname
I.e. given
char *name = "foo";
and
const char *cname = "foo";
There is not much difference between the 2 and both can be seen as correct. Due to the long legacy of C code, the string literals have had a type of char[], not const char[], and there are lots of older code that likewise accept char * instead of const char *, even when they do not modify the arguments.
The principal difference of the 2 in general is that *cname or cname[n] will evaluate to lvalues of type const char, whereas *name or name[n] will evaluate to lvalues of type char, which are modifiable lvalues. A conforming compiler is required to produce a diagnostics message if target of the assignment is not a modifiable lvalue; it need not produce any warning on assignment to lvalues of type char:
name[0] = 'x'; // no diagnostics *needed*
cname[0] = 'x'; // a conforming compiler *must* produce a diagnostic message
The compiler is not required to stop the compilation in either case; it is enough that it produces a warning for the assignment to cname[0]. The resulting program is not a correct program. The behaviour of the construct is undefined. It may crash, or even worse, it might not crash, and might change the string literal in memory.
The first you can actually change if you want to, the second you can't. Read up about const correctness (there's some nice guides about the difference). There is also char const * name where you can't repoint it.
Actually, char* name is not a pointer to a constant, but a pointer to a variable. You might be talking about this other question.
What is the difference between char * const and const char *?
I would add here that the latest compilers, VS 2022 for instance, do not allow char* to be initialized with a string literal. char* ptr = "Hello"; throws an error whilst const char* ptr = "Hello"; is legal.

changing one char in a c string

I am trying to understand why the following code is illegal:
int main ()
{
char *c = "hello";
c[3] = 'g'; // segmentation fault here
return 0;
}
What is the compiler doing when it encounters char *c = "hello";?
The way I understand it, its an automatic array of char, and c is a pointer to the first char. If so, c[3] is like *(c + 3) and I should be able to make the assignment.
Just trying to understand the way the compiler works.
String constants are immutable. You cannot change them, even if you assign them to a char * (so assign them to a const char * so you don't forget).
To go into some more detail, your code is roughly equivalent to:
int main() {
static const char ___internal_string[] = "hello";
char *c = (char *)___internal_string;
c[3] = 'g';
return 0;
}
This ___internal_string is often allocated to a read-only data segment - any attempt to change the data there results in a crash (strictly speaking, other results can happen as well - this is an example of 'undefined behavior'). Due to historical reasons, however, the compiler lets you assign to a char *, giving you the false impression that you can modify it.
Note that if you did this, it would work:
char c[] = "hello";
c[3] = 'g'; // ok
This is because we're initializing a non-const character array. Although the syntax looks similar, it is treated differently by the compiler.
there's a difference between these:
char c[] = "hello";
and
char *c = "hello";
In the first case the compiler allocates space on the stack for 6 bytes (i.e. 5 bytes for "hello" and one for the null-terminator.
In the second case the compiler generates a static const string called "hello" in a global area (aka a string literal, and allocates a pointer on the stack that is initialized to point to that const string.
You cannot modify a const string, and that's why you're getting a segfault.
You can't change the contents of a string literal. You need to make a copy.
#include <string.h>
int main ()
{
char *c = strdup("hello"); // Make a copy of "hello"
c[3] = 'g';
free(c);
return 0;
}

Do these statements about pointers have the same effect?

Does this...
char* myString = "hello";
... have the same effect as this?
char actualString[] = "hello";
char* myString = actualString;
No.
char str1[] = "Hello world!"; //char-array on the stack; string can be changed
char* str2 = "Hello world!"; //char-array in the data-segment; it's READ-ONLY
The first example creates an array of size 13*sizeof(char) on the stack and copies the string "Hello world!" into it.
The second example creates a char* on the stack and points it to a location in the data-segment of the executable, which contains the string "Hello world!". This second string is READ-ONLY.
str1[1] = 'u'; //Valid
str2[1] = 'u'; //Invalid - MAY crash program!
No. The first one gives you a pointer to const data, and if you change any character via that pointer, it's undefined behavior. The second one copies the characters into an array, which isn't const, so you can change any characters (either directly in array, or via pointer) at will with no ill effects.
No. In the first one, you can't modify the string pointed by myString, in the second one you can. Read more here.
It isn't the same, because the unnamed array pointed to by myString in the first example is read-only and has static storage duration, whereas the named array in the second example is writeable and has automatic storage duration.
On the other hand, this is closer to being equivalent:
static const char actualString[] = "hello";
char* myString = (char *)actualString;
It's still not quite the same though, because the unnamed arrays created by string literals are not guaranteed to be unique, whereas explicit arrays are. So in the following example:
static const char string_a[] = "hello";
static const char string_b[] = "hello";
const char *ptr_a = string_a;
const char *ptr_b = string_b;
const char *ptr_c = "hello";
const char *ptr_d = "hello";
ptr_a and ptr_b are guaranteed to compare unequal, whereas ptr_c and ptr_d may be either equal or unequal - both are valid.

Resources