modify const char * vs char * content in easy way - c++

im realy confused about const char * and char *.
I know in char * when we want to modify the content, we need to do something like this
const char * temp = "Hello world";
char * str = new char[strlen(temp) + 1];
memcpy(str, temp, strlen(temp));
str[strlen(temp) + 1] = '\0';
and if we want to use something like this
char * str = "xxx";
char * str2 = "xts";
str = str2;
we get compiler warning. it's ok I know when i want to change char * I have to use something memory copy. but about const char * im realy confused. in const char * I can use this
const char * str = "Hello";
const char * str2 = "World";
str = str2; // and now str is Hello
and I have no compiler error ! why ? why we use memory copy when is not const and in const we only use equal operator ! and done !... how possible? is it ok to just use equal in const? no problem happen later?

As other answers say, you should distinguish pointers and bytes they point to.
Both types of pointers, char * and const char *, can be changed, that is, "redirected" to point to different bytes. However, if you want to change the bytes (characters) of the strings, you cannot use const char *.
So, if you have string literals "Hello" and "World" in your program, you can assign them to pointers, and printing the pointer will print the corresponding literal. However, to do anything non-trivial (e.g. change Hello to HELLO), you will need non-const pointers.
Another example: with some pointer manipulation, you can remove leading bytes from a string literal:
const char* str = "Hello";
std::cout << str; // Hello
str = str + 2;
std::cout << str; // llo
However, if you want to extract a substring, or do any other transformation on a string, you should reallocate it, and for that you need a non-const pointer.
BTW since you are using C++, you can use std::string, which makes it easier to work with strings. It reallocates strings without your intervention:
#include <string>
std::string str("Hello");
str = str.substr(1, 3);
std::cout << str; // ell

This is a confusing hangover from the days of early C. Early C didn't have const, so string literals were "char *". They remained char * to avoid breaking old code, but they became non-modifiable, so const char * in all but name. So modern C++ either warns or gives an error (to be strictly conforming) when the const is omitted.
Your memcpy missed the trailing nul byte, incidentally. Use strcpy() to copy a string, that's the right function with the right name. You can create a string in read/write memory by use of the
char rwstring[] = "I am writeable";
syntax.

That is cause your variables are just a pointers *. You're not modifiying their contents, but where they are pointing to.
char * a = "asd";
char * b = "qwe";
a = b;
now you threw away the contents of a. Now a and b points to the same place. If you modify one, both are modified.
In other words. Pointers are never constants (mostly). your const predicate in a pointer variable does not means nothing to the pointer.
The real difference is that the pointer (that is not const) is pointing to a const variable. and when you change the pointer it will be point to ANOTHER NEW const variable. That is why const has no effect on simple pointers.
Note: You can achieve different behaviours with pointers and const with more complex scenario. But with simple as it, it mostly has no effect.

Citing Malcolm McLean:
This is a confusing hangover from the days of early C. Early C didn't have const, so string literals were "char *". They remained char * to avoid breaking old code, but they became non-modifiable, so const char * in all but name.
Actually, string literals are not pointers, but arrays, this is why sizeof("hello world") works as a charm (yields 12, the terminating null character is included, in contrast to strlen...). Apart from this small detail, above statement is correct for good old C even in these days.
In C++, though, string literals have been arrays of constant characters (char const[]) right from the start:
C++ standard, 5.13.5.8:
Ordinary string literals and UTF-8 string literals are also referred to as narrow string literals. A narrow string literal has type “array of n const char”, where n is the size of the string as defined below, and has static storage duration.
(Emphasised by me.) In general, you are not allowed to assign pointer to const to pointer to non-const:
char const* s = "hello";
char ss = s;
This will fail to compile. Assigning string literals to pointer to non-const should normally fail, too, as the standard explicitly states in C.1.1, subclause 5.13.5:
Change: String literals made const.
The type of a string literal is changed from “array of char” to “array of const char”.
[...]char* p = "abc"; // valid in C, invalid in C++
Still, string literal assignement to pointer to non-const is commonly accepted by compilers (as an extension!), probably to retain compatibility to C. As this is, according to the standard, invalid, the compiler yields a warning, at least...

Related

How does conversion of string literals to char arrays actually work in C++?

I am trying to understand how pointers,arrays and string literals work in C++.
Suppose we have the following line of code:
const char* const letters[] = {"A+","A"};
If I understand correctly, this declaration declares letters to be an array of constant pointers to constant characters. From my understanding, the compiler will actually convert each string literal to a null terminated char array and each element of letters is actually a constant pointer to the first element of that array.
So, for instance, letters[0] is actually a pointer to the "A" of "A+". However
std::cout<< letters[0];
actually outputs "A+" to the standard output. How can this be? Especially since letters[0] is a constant pointer?
My second question is related to the declaration above: if string literals are actually const char arrays, then why does the following line of code
const char* const letters[] = {{'A','+','\0'},{'A','\0'}};
throws
error: braces around scalar initializer for type ‘const char* const’
const char* const letters[] = {{'A','+','\0'},{'A','\0'}};
^
Thank you!
The standard specifies that a string literal is represented - as far as your program is concerned - as an array of const characters of static storage duration with a trailing '\0' terminator. The standard doesn't specify HOW a compiler achieves this effect, only that your program can treat the string literal in that way.
So modifying a string literal is either prevented (e.g. passing a string literal to a function expecting a char * is a diagnosable error, and the code will not compile) or - if code works around the type system to modify any character in a string literal - involves undefined behaviour.
In your example, letters[0] is of type const char *, and has a value equal to the address of the first character in the string literal "A+".
std::cout, being of type std::ostream, has an operator<<() that accepts a const char *. This function is called by the statement std::cout << letters[0] and the function assumes the const char * points at a zero-terminated array of char. It iterates over that array, outputting each character individually, until it encounters the trailing '\0' (which is not output).
The thing is, a const char * means that the pointer is to a const char, not that the pointer cannot be changed (that would be char * const). So it is possible to increment the pointer, but not change the value it points at. So, if we do
const char *p = letters[0];
while (*p != '\0')
{
std::cout << *p;
++p;
}
which loops over the characters of the string literal "A+", printing each one individually, and stopping when it reaches the '\0' (the above produces the same observable output std::cout << letters[0]).
However, in the above
*p = 'C';
will not compile, since the definition of p tells the compiler that *p cannot be changed. However, incrementing p is still allowed.
The reason that
const char* const letters [] = {{'A','+','\0'},{'A','\0'}};
does not compile is that an array initialiser cannot be used to initialise pointers. For example;
const int *nums = {1,2,3}; // invalid
const * const int nums2 [] = {{1,2,3}, {4,5,6}}; // invalid
are both illegal. Instead, one is required to define arrays, not pointers.
const int nums[] = {1,2,3};
const int nums2[][3] = {{1,2,3}, {4,5,6}};
All versions of C and C++ forbid initialising pointers (or arrays of pointers in your example) in this way.
Technically, the ability to use string literals to initialise pointers is actually the anomaly, not the prohibition on initialising pointers using arrays. The reasons C introduced that exemption for string literals are historical (in very early days of C, well before K&R C, string literals could not be used to initialise pointers either).
As for your first question, the type of letters[0] is const char * const. This is a pointer to a character, but not a character itself. When passing a pointer to a character to std::cout, it will treat it as a NUL-terminated C string, and writes out all characters from the start of the memory pointed to until it encounters a NUL-byte. So that is why the output will be A+. You can pass the first character of the first string by itself by writing:
std::cout << letters[0][0];
The fact that the pointers and/or the C strings themselves are const doesn't matter here, since nothing is writing to them.
As for your second question, const char * const declares a single array, but you are providing a nested array on the right-hand side of that statement. If you really wanted two arrays of characters, write:
const char *const letters[] = {{'A', '+', '\0'}, {'A', '\0'}};
That is equal to your code form the first question. Or if you want a single array:
const char *const letters = {'A', '+', '\0', 'A', '\0'};
That line is equal to:
const char *const letters = "A+\0A";

Passing a string to a char array, then modify the char array at position x in C

So I have a string which contains "RGGB" and I need it to be in a char array to perform some operations. Then I need to replace certain characters for a blank space, for example the first 'G', so that my char array remains "R GB".
How can I do this? So far I tried this solution:
int main()
{
string problem="RGGB";
const char *p=problem.c_str();
p[1]=' ';
return p;
}
I get the error:
assignment of read only location *(p + ((sizetype)i))
To access the "interal string" (I mean a const char*) of a std::string, there are two member functions provided: std::string::c_str and std::string::data. Until C++11, the difference was that std::string::data wasn't bound to return a pointer to a null-terminated const char* while std::string::c_str was. Now, they are equivalent. And both return a const char*, even before C++11.
There are several approaches to your problem:
Use std::strdup or std::str(n)cpy to duplicate the string and write to the duplicate.
Use a const_cast. Pretty drastic, but, if it doesn't hurt any rules (FYI, it does), works.
Don't use std::string at all. Do what you want with a char* and then optionally convert it to a std::string later.
Just use the functionality of std::string.
string, a C++ class, does not provide directly alterable access to its innerds via a char *. While you could cast away the const, this is dangerous because compilers may use the const as an optimization path.
If you absolutely need to do this just use a char array, not a string or alter the contents of the string using string methods.
p[1]=' ';
is not valid as const char * is read-only pointer.
in other words, it is a const pointer.
remember:
When const appears to the left of the *, what's pointed to is constant, and if const appears to the right of the *, the pointer itself is constant. If const appears on both sizes, both are constants.
this code might work:
char problem[]="RGGB";
char* p = problem;
p[1]=' ';
cout<<problem;
The answer is in your question:
I need it to be in a char array
Then put it in a char array:
char problem[] = "RGGB";
problem[1] = ' ';
Problem solved.
If, on the other hand, you want to solve the problem using actual C++:
std::string problem = "RGGB";
problem.at(1) = ' ';

c++ char * initialization in constructor

I'm just curious, I want to know what's going on here:
class Test
{
char * name;
public:
Test(char * c) : name(c){}
};
1) Why won't Test(const char * c) : name(c){} work? Because char * name isn't const? But what about this:
main(){
char * name = "Peter";
}
name is char*, but "Peter" is const char*, right? So how does that initialization work?
2) Test(char * c) : name(c){ c[0] = 'a'; } - this crashes the program. Why?
Sorry for my ignorance.
Why won't Test(const char * c) : name(c) {} work? Because char * name isn't const?
Correct.
how does this initialization work: char * name = "Peter";
A C++ string literal is of type char const[] (see here, as opposed to just char[] in C, as it didn't have the const keyword1). This assignment is considered deprecated in C++, yet it is still allowed2 for backward compatibility with C.
Test(char * c) : name(c) { c[0] = 'a'; } crashes the program. Why?
What are you passing to Test when initializing it? If you're passing a string literal or an illegal pointer, doing c[0] = 'a' is not allowed.
1 The old version of the C programming language (as described in the K&R book published in 1978) did not include the const keyword. Since then, ANSI C borrowed the idea of const from C++.
2 Valid in C++03, no longer valid in C++11.
A conversion to const is a one-way street, so to speak.
You can convert from T * to T const * implicitly.
Conversion from T const * to T * requires an explicit cast. Even if you started from T *, then converted to T const *, converting back to T * requires an explicit cast, even though it's really just "restoring" the access you had to start with.
Note that throughout, T const * and const T * are precisely equivalent, and T stands for "some arbitrary type" (char in your example, but could just as easily be something else like int or my_user_defined_type).
Initializing a char * from a string literal (e.g., char *s = "whatever";) is allowed even though it violates this general rule (the literal itself is basically const, but you're creating a non-const pointer to it). This is simply because there's lots of code that depends on doing this, and nobody's been willing to break that code, so they have a rule to allow it. That rule's been deprecated, though, so at least in theory some future compiler could reject code that depends on it.
Since the string literal itself is basically const, any attempt at modifying it results in undefined behavior. On most modern systems, this will result in the process being terminated, because the memory storing the string literal will be marked at 'read only'. That's not the only possible result. Just for example, back in the days of MS-DOS, it would often succeed. It could still have bizarre side-effects though. For one example, many compilers "knew" that string literals were supposed to be read-only, so they'd "merge" identical string literals. Therefore if you had something like:
char *a = "Peter"; a[1] = 'a';
char *b = "Peter";
cout << b;
The compiler would have "merged" a and b to actually point at the same memory -- so when you modified a, that change would also affect b, so it would print out "Pater" instead of "Peter".
Note that the string literals didn't need to be entirely identical for this to happen either. As long as one was identical to the end of another, they could be merged:
char *a = "this?";
char *b = "What's this?";
a[2] = 'a';
a[3] = 't';
cout << b; // could print "What's that?"
Mandating one behavior didn't make sense, so the result was (and is) simply undefined.
First of all this is C++, you have std::string. You should really consider using it.
Regarding your question, "Peter" is a char literal, hence it is unmodifiable and surely you can't write on it. You can:
have a const char * member variable and initialize it like you are doing name(c), by declaring "Peter" as const
have a char * member variable and copy the content, eg name(strdup(c)) (and remember to release it in destructor.
Correct.
"Peter" is typically stored in a read-only memory location (actually, it depends on what type of device we are on) because it is a string literal. It is undefined what happens when you attempt to modify a string literal (but you can probably guess that you shouldn't).
You should use std::string anyways.
1a) Right
1b) "Peter" is not const char*, its is char* but it may not be modified. The reason is for compatibility with times before const existed in the language. A lot of code already existed that said char* p = "fred"; and they couldn't just make that code illegal overnight.
2) Can't say why that would crash the program without seeing how you are using that constructor.

Why can't I write to a string literal while I *can* write to a string object?

If i define something like below,
char *s1 = "Hello";
why I can't do something like below,
*s1 = 'w'; // gives segmentation fault ...why???
What if I do something like below,
string s1 = "hello";
Can I do something like below,
*s1 = 'w';
Because "Hello" creates a const char[]. This decays to a const char* not a char*. In C++ string literals are read-only. You've created a pointer to such a literal and are trying to write to it.
But when you do
string s1 = "hello";
You copy the const char* "hello" into s1. The difference being in the first example s1 points to read-only "hello" and in the second example read-only "hello" is copied into non-const s1, allowing you to access the elements in the copied string to do what you wish with them.
If you want to do the same with a char* you need to allocate space for char data and copy hello into it
char hello[] = "hello"; // creates a char array big enough to hold "hello"
hello[0] = 'w'; // writes to the 0th char in the array
string literals are usually allocated in read-only data segment.
Because Hello resides in read only memory. Your signature should actually be
const char* s1 = "Hello";
If you want a mutable buffer then declare s1 as a char[]. std::string overloads operator [], so you can index into it, i.e., s1[index] = 'w'.
Time to confuse matters:
char s0[] = "Hello";
s0[0] = 'w';
This is perfectly valid! Of course, this doesn't answer the original question so here we go: string literals are created in read-only memory. That is, their type is char const[n] where n is the size of the string (including the terminating null character, i.e. n == 6 for the string literal "Hello". But why, oh, why can this type be used to initialize a char const*? The answer is simply backward compatibility, respectively compatibility to [old] C code: by the time const made it into the language, lots of places already initialized char* with string literals. Any decent compiler should warn about this abuse, however.

C++ deprecated conversion from string constant to 'char*'

I have a class with a private char str[256];
and for it I have an explicit constructor:
explicit myClass(char *func)
{
strcpy(str,func);
}
I call it as:
myClass obj("example");
When I compile this I get the following warning:
deprecated conversion from string constant to 'char*'
Why is this happening?
This is an error message you see whenever you have a situation like the following:
char* pointer_to_nonconst = "string literal";
Why? Well, C and C++ differ in the type of the string literal. In C the type is array of char and in C++ it is constant array of char. In any case, you are not allowed to change the characters of the string literal, so the const in C++ is not really a restriction but more of a type safety thing. A conversion from const char* to char* is generally not possible without an explicit cast for safety reasons. But for backwards compatibility with C the language C++ still allows assigning a string literal to a char* and gives you a warning about this conversion being deprecated.
So, somewhere you are missing one or more consts in your program for const correctness. But the code you showed to us is not the problem as it does not do this kind of deprecated conversion. The warning must have come from some other place.
The warning:
deprecated conversion from string constant to 'char*'
is given because you are doing somewhere (not in the code you posted) something like:
void foo(char* str);
foo("hello");
The problem is that you are trying to convert a string literal (with type const char[]) to char*.
You can convert a const char[] to const char* because the array decays to the pointer, but what you are doing is making a mutable a constant.
This conversion is probably allowed for C compatibility and just gives you the warning mentioned.
As answer no. 2 by fnieto - Fernando Nieto clearly and correctly describes that this warning is given because somewhere in your code you are doing (not in the code you posted) something like:
void foo(char* str);
foo("hello");
However, if you want to keep your code warning-free as well then just make respective change in your code:
void foo(char* str);
foo((char *)"hello");
That is, simply cast the string constant to (char *).
There are 3 solutions:
Solution 1:
const char *x = "foo bar";
Solution 2:
char *x = (char *)"foo bar";
Solution 3:
char* x = (char*) malloc(strlen("foo bar")+1); // +1 for the terminator
strcpy(x,"foo bar");
Arrays also can be used instead of pointers because an array is already a constant pointer.
Update: See the comments for security concerns regarding solution 3.
A reason for this problem (which is even harder to detect than the issue with char* str = "some string" - which others have explained) is when you are using constexpr.
constexpr char* str = "some string";
It seems that it would behave similar to const char* str, and so would not cause a warning, as it occurs before char*, but it instead behaves as char* const str.
Details
Constant pointer, and pointer to a constant. The difference between const char* str, and char* const str can be explained as follows.
const char* str : Declare str to be a pointer to a const char. This means that the data to which this pointer is pointing to it constant. The pointer can be modified, but any attempt to modify the data would throw a compilation error.
str++ ; : VALID. We are modifying the pointer, and not the data being pointed to.
*str = 'a'; : INVALID. We are trying to modify the data being pointed to.
char* const str : Declare str to be a const pointer to char. This means that point is now constant, but the data being pointed too is not. The pointer cannot be modified but we can modify the data using the pointer.
str++ ; : INVALID. We are trying to modify the pointer variable, which is a constant.
*str = 'a'; : VALID. We are trying to modify the data being pointed to. In our case this will not cause a compilation error, but will cause a runtime error, as the string will most probably will go into a read only section of the compiled binary. This statement would make sense if we had dynamically allocated memory, eg. char* const str = new char[5];.
const char* const str : Declare str to be a const pointer to a const char. In this case we can neither modify the pointer, nor the data being pointed to.
str++ ; : INVALID. We are trying to modify the pointer variable, which is a constant.
*str = 'a'; : INVALID. We are trying to modify the data pointed by this pointer, which is also constant.
In my case the issue was that I was expecting constexpr char* str to behave as const char* str, and not char* const str, since visually it seems closer to the former.
Also, the warning generated for constexpr char* str = "some string" is slightly different from char* str = "some string".
Compiler warning for constexpr char* str = "some string": ISO C++11 does not allow conversion from string literal to 'char *const'
Compiler warning for char* str = "some string": ISO C++11 does not allow conversion from string literal to 'char *'.
Tip
You can use C gibberish ↔ English converter to convert C declarations to easily understandable English statements, and vice versa. This is a C only tool, and thus wont support things (like constexpr) which are exclusive to C++.
In fact a string constant literal is neither a const char * nor a char* but a char[]. Its quite strange but written down in the c++ specifications; If you modify it the behavior is undefined because the compiler may store it in the code segment.
Maybe you can try this:
void foo(const char* str)
{
// Do something
}
foo("Hello")
It works for me
I solve this problem by adding this macro in the beginning of the code, somewhere. Or add it in <iostream>, hehe.
#define C_TEXT( text ) ((char*)std::string( text ).c_str())
I also got the same problem. And what I simple did is just adding const char* instead of char*. And the problem solved. As others have mentioned above it is a compatible error. C treats strings as char arrays while C++ treat them as const char arrays.
For what its worth, I find this simple wrapper class to be helpful for converting C++ strings to char *:
class StringWrapper {
std::vector<char> vec;
public:
StringWrapper(const std::string &str) : vec(str.begin(), str.end()) {
}
char *getChars() {
return &vec[0];
}
};
The following illustrates the solution, assign your string to a variable pointer to a constant array of char (a string is a constant pointer to a constant array of char - plus length info):
#include <iostream>
void Swap(const char * & left, const char * & right) {
const char *const temp = left;
left = right;
right = temp;
}
int main() {
const char * x = "Hello"; // These works because you are making a variable
const char * y = "World"; // pointer to a constant string
std::cout << "x = " << x << ", y = " << y << '\n';
Swap(x, y);
std::cout << "x = " << x << ", y = " << y << '\n';
}