I'm having troubles initializing a global variable. My C++ is a bit rusty so I can't remember the reason why my code isn't working.
file.cpp
const char * write_path = &(std::string(getenv("FIFO_PATH")) + "/pythonread_fifo")[0];
int main(int argc, char**argv)
{
std::cout << "printing a string: ";
std::cout << (std::string(getenv("FIFO_PATH")) + "/pythonread_fifo\n");
std::cout << "printing a const char*: ";
std::cout << &(std::string(getenv("FIFO_PATH")) + "/pythonread_fifo")[0] << std::endl;
std::cout << "printing write_path:";
std::cout << write_path;
std::cout << write_path << std::endl;
std::cout << "printing FIFO_PATH:" << std::string(getenv("FIFO_PATH"));
}
As a premise: FIFO_PATH has been correctly added to bashrc, and it works, however, when I launch this program this is the output:
printing a string: /home/Processes/FIFOs/pythonread_fifo
printing a const char*: /home/Processes/FIFOs/pythonread_fifo
printing write_path:
printing FIFO_PATH:/home/Processes/FIFOs
As you can see write_path is completely empty.
What's even stranger to me is that if I define write_path as:
const char * write_path = "/home/Processes/FIFOs/pythonread_fifo";
Then write_path is no longer empty, it's correctly initialized and printed.
How can I solve this? Or at the very least, why is this happening?
EDIT: The issue IS NOT related to write_path being global. I placed the definition inside the main and when I try to print write_path, it's still empty
write_path is initialized as a pointer pointing to the 1st element of a temporary std::string, which will be destroyed immediately after the full expression, left write_path dangled, dereference on it leads to UB.
You can use std::string directly, or use a named std::string, then get the pointer from it.
std::string s = std::string(getenv("FIFO_PATH")) + "/pythonread_fifo";
const char * write_path = &s[0]; // or s.c_str()
On the other hand,
const char * write_path = "/home/mverrocc/dss_cp/dss-cp/Processes/FIFOs/pythonread_fifo";
works fine, the c-style string literal has static storage duration and exists in memory for the life of the program, then write_path is always a valid pointer.
const char * write_path = &(std::string(getenv("FIFO_PATH")) + "/pythonread_fifo")[0];
constructs a temporary std::string, takes the address of it's first character then discards the string, thus deleting the underlying char array. This is UB.
Better just use std::string and use c_str() when you need a const char*
You're creating a temporary std::string object, and get a pointer to its first character. This pointer will become invalid as soon as the expression &(std::string(getenv("FIFO_PATH")) + "/pythonread_fifo")[0] ends, when the temporary object is destructed.
Use a std::string object for write_path as well, define it inside the main function, and use the c_str function of the string when you need a null-terminated string.
Related
I have a const char pointer which I know for sure came from a string. For example:
std::string myString = "Hello World!";
const char* myCstring = myString.c_str();
In my case I know myCstring came from a string, but I no longer have access to that string (I received the const char* from a function call, and I cannot modify the function's argument list).
Given that I know myCstring points to contents of an existing string, is there any way to safely access the pointer of the parent string from which it originated? For example, could I do something like this?
std::string* hackyStringPointer = myCstring - 6; //Along with whatever pointer casting stuff may be needed
My concern is that perhaps the string's contents possibly cannot be guaranteed to be stored in contiguous memory on some or all platforms, etc.
Given that I know myCstring points to contents of an existing string, is there any way to safely access the pointer of the parent string from which it originated?
No, there is no way to obtain a valid std::string* pointer from a const char* pointer to character data that belongs to a std::string.
I received the const char* from a function call, and I cannot modify the function's argument list
Your only option in this situation would be if you can pass a pointer to the std::string itself as the actual const char* pointer, but that will only work if whatever is calling your function does not interpret the const char* in any way (and certainly not as a null-terminated C string), eg:
void doSomething(void (*func)(const char*), const char *data)
{
...
func(data);
...
}
void myFunc(const char *myCstring)
{
std::string* hackyStringPointer = reinterpret_cast<std::string*>(myCstring);
...
}
...
std::string myString = "Hello World!";
doSomething(&myFunc, reinterpret_cast<char*>(&myString));
You cannot convert a const char* that you get from std::string::c_str() to a std::string*. The reason you can't do this is because c_str() returns a pointer to the string data, not the string object itself.
If you are trying to get std::string so you can use it's member functions then what you can do is wrap myCstring in a std::string_view. This is a non-copying wrapper that lets you treat a c-string like it is a std::string. To do that you would need something like
std::string_view sv{myCstring, std::strlen(myCstring)};
// use sv here like it was a std::string
Yes (it seems), although I agree that if I need to do this it's likely a sign that my code needs reworking in general. Nevertheless, the answer seems to be that the string pointer resides 4 words before the const char* which c_str() returns, and I did recover a string* from a const char* belonging to a string.
#include <string>
#include <iostream>
std::string myString = "Hello World!";
const char* myCstring = myString.c_str();
unsigned int strPtrSize = sizeof(std::string*);
unsigned int cStrPtrSize = sizeof(const char*);
long strAddress = reinterpret_cast<std::size_t>(&myString);
long cStrAddress = reinterpret_cast<std::size_t>(myCstring);
long addressDifference = strAddress - cStrAddress;
long estStrAddress = cStrAddress + addressDifference;
std::string* hackyStringPointer = reinterpret_cast<std::string*>(estStrAddress);
cout << "Size of String* " << strPtrSize << ", Size of const char*: " << cStrPtrSize << "\n";
cout << "String Address: " << strAddress << ", C String Address: " << cStrAddress << "\n";
cout << "Address Difference: " << addressDifference << "\n";
cout << "Estimated String Address " << estStrAddress << "\n";
cout << "Hacky String: " << *hackyStringPointer << "\n";
//If any of these asserts trigger on any platform, I may need to re-evaluate my answer
assert(addressDifference == -4);
assert(strPtrSize == cStrPtrSize);
assert(hackyStringPointer == &myString);
The output of this is as follows:
Size of String* 4, Size of const char*: 4
String Address: 15725656, C String Address: 15725660
Address Difference: -4
Estimated String Address: 15725656
Hacky String: Hello World!
It seems to work so far. If someone can show that the address difference between a string and its c_str() can change over time on the same platform, or if all members of a string are not guaranteed to reside in contiguous memory, I'll change my answer to "No."
This reference says
The pointer returned may be invalidated by further calls to other member functions that modify the object.
You say you got the char* from a function call, this means you do not know what happens to the string in the mean time, is that right? If you know that the original string is not changed or deleted (e.g. gets out of scope and thus is destructed) then you can still use the char*.
Your example code however has multiple problems. You want to do this:
std::string* hackyStringPointer = myCstring - 6;
but I think you meant
char* hackyStringPointer = myCstring;
One, you cannot cast the char* to a string* and second you do not want to go BEFORE the start of the char*. The char* points to the first character of the string, you can use it to access the characters up to the trailing 0 character. But you should not go before the first or after the trailing 0 character though, as you do not know what is in that memory or if it even exists.
If cstring is a pointer, then why can it get a value directly? Secondly, Why wasn't the *cstring's result equal to whole of string? Third, cstring is a non-constant pointer to a constant character, so why change its value and not change its address?
#include <cstdio>
#include <conio.h>
#include <iostream>
using namespace std;
int main()
{
const char* cstring = "string";
cout << cstring << endl << *cstring << endl << &cstring << endl;
cstring = "foo";
cout << cstring << endl << *cstring << endl << &cstring << endl;
_getch();
return 0;
}
If cstring is a pointer, then why can it get a value directly?
The operator << of cout for const char* is specialized to behave that way. It will treat the pointer as a NULL terminated string and will print it instead of the pointer value. For different types you get different behaviour. For char* you have the whole string printed.
Why wasn't the *cstring's result equal to whole of string?
That is because the type of *cstring is char and again, operator << behaves correctly by just printing a single char. a const char* is essentially an array of char.An array is essentially a pointer to the first element of the array. If you use the * operator on a pointer you are accessing whatever the pointer points to. If it points to the first element, well, you get the first element.
Third, cstring is a non-constant pointer to a constant character, so why change its value and not change its address?
As you said, cstring is a non-constant pointer to constant data. You cannot change the place it points to (it is a constant pointer), but you can substitute the content of the pointed data with other stuff. You point to the same location but the content of that cell changes.
If cstring is a pointer, then why can it get a value directly?
Anyone can get the value pointed at by a pointer by dereferencing the pointer. That's what happens when you do std::cout << cstring. The proper overload gets chosen that prints the string represented by cstring assuming that is correctly formed, null-terminated C-style string.
Secondly, Why wasn't the *cstring's result equal to whole of
string?
cstring is a const char*, so *cstring is a const char. Pass that to std::cout and it will call an overload that prints one char. The function that is called internally doesn't even know that this is just one char in a string.
Third, cstring is a non-constant pointer to a constant character, so
why change its value and not change its address?
You can't change the address of a variable. cstring is in a fixed place on the stack. You change the value of cstring, which is the address of the string that it's pointing to (it is now pointing to a different string, which has a different address, "string" of course stil has the same address).
What you probably wanted to try is this:
const char* cstring = "string";
std::cout << (void*)cstring << std::endl;
cstring = "foo";
std::cout << (void*)cstring << std::endl;
Now you can see the different addresses. One is the address of "string" and one is the address of "foo".
I need to convert number to string and store it into a const char* but problem is that const char* variable is blank after assignment.
In the following example code I expect to see number output converted to const char*
#include <iostream>
#include <string>
int main()
{
int number = 123;
const char* ptr_num_string = std::to_string(number).c_str();
std::cout << "number to string is: " << ptr_num_string << std::endl;
std::cin.get();
return 0;
}
Output is blank:
number to string is:
How do I convert number into a const char* ?
std::to_string returns a temporary std::string.
The pointer returned by std::string::c_str is invalidated by any non-const operation on the string itself - this is because it basically gives you a pointer to the string's internal buffer.
Destroying a std::string is definitely a non-const operation!
Therefore, you can't expect to take a pointer into the string returned by to_string and ever be able to use that. You need to save a copy of that string in the first place:
int number = 123;
std::string const numAsString = std::to_string(number);
char const* ptrToNumString = numAsString.c_str(); // use this as long as numAsString is alive
What you are doing is called Undefined Behaviour - this means that your program is invalid, and anything could happen. It could crash, it could print out some garbage, it could appear to work normally, it could do a different one of these each time you run your program...
It is not "blank" it points to the buffer of string that went out of scope. So accessing it causes Undefined Behavior. You need to keep string alive:
auto str{std::to_string(number)};
auto ptr_num_string{str.c_str()};
std::cout << "number to string is: " << ptr_num_string << std::endl;
This question already has answers here:
What is the lifetime of the result of std::string::c_str()?
(7 answers)
Closed 5 years ago.
Consider the MWE below,
std::string return_string()
{
return "this is a string"
}
int main()
{
const char *y = return_string().c_str();
std::string str = return_string();
const char *x = str.c_str();
std::cout << return_string() << std::endl; //Prints "this is a string"
std::cout << y << std::endl; // Prints Weird characters
std::cout << x << std::endl; //Prints "this is a string"
std::cin.ignore();
return 0;
}
I have a function which returns a string and I need to convert it to a c-style string. Doing return_string().c_str() gives me weird output like
▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌p. But if I store the output of a function in a string first and then convert that string to a c-style string it works. What is wrong with the first way?
When you do return_string().c_str() you get the pointer to a temporary object that will go out of scope once the expression is finished. If you save the pointer and use it later you will have undefined behavior.
With
std::string str = return_string();
you copy the returned temporary object. Getting a pointer to the copy will work since it still exists in the program at the point when you use the pointer.
const char *y = return_string().c_str();
is a problem if you wish to use y later to access the contents. In that line, the return value of return_string() is a temporary that gets destroyed after that line is executed. Hence, y is dangling pointer.
Accessing the contents of a dangling pointer causes undefined behavior.
When you use
std::string str = return_string();
const char *x = str.c_str();
x is not a dangling pointer as long as str is in scope. Hence,
std::cout << x << std::endl;
is well behaved.
You can't "convert from string to const char*". No such thing exists, because a const char* does not contain characters. It is not a string! It only pointers to some characters somewhere.
In this case, via std::string::c_str(), it points to characters that no longer exist.
I have a function f() which receives a char* p and gives a const char* to it.
void f(char *p) {
string s = "def";
strcpy(p, s.c_str());
}
In the main() below I expect to get q = "def".
int main(){
char *q = "abc";
f(q);
cout << q << endl;
}
By running this I get segmentation fault and as I am new in C++ I don't understand why.
I also get a segmentation fault when I do not initialize q thus:
int main(){
char *q;
f(q);
cout << q << endl;
}
Knowing that the function's parameter and the way it's called must not change. Is there any work around that I can do inside the function? Any suggestions?
Thanks for your help.
You are trying to change a string literal. Any attemp to change a string literal results in undefined behaviour of the program.
Take into account that string literals have types of constant character arrays. So it would be more correct to write
const char *q = "abc";
From the C++ Standard (2.14.5 String literals)
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
You could write your program the following way
//...
void f(char *p) {
string s = "def";
strcpy(p, s.c_str());
}
//..
main(){
char q[] = "abc";
f(q);
cout << q << endl;
}
If you need to use a pointer then you could write
//...
void f(char *p) {
string s = "def";
strcpy(p, s.c_str());
}
//..
main(){
char *q = new char[4] { 'a', 'b', 'c', '\0' };
f(q);
cout << q << endl;
delete []q;
}
This is an issue that, in reality, should fail at compilation time but for really old legacy reasons they allow it.
"abc" is not not a mutable string and therefore it should be illegal to point a mutable pointer to it.
Really any legacy code that does this should be forced to be fixed, or have some pragma around it that lets it compile or some permissive flag set in the build.
But a long time ago in the old days of C there was no such thing as a const modifier, and literals were stored in char * parameters and programmers had to be careful what they did with them.
The latter construct, where q is not initialised at all is an error because now q could be pointing anywhere, and is unlikely to be pointing to a valid memory place to write the string. It is actually undefined behaviour, for obvious reason - who knows where q is pointing?
The normal construct for such a function like f is to request not only a pointer to a writable buffer but also a maximum available size (capacity). Usually this size includes the null-terminator, sometimes it might not, but either way the function f can then write into it without an issue. It will also often return a status, possibly the number of bytes it wanted to write. This is very common for a "C" interface. (And C interfaces are often used even in C++ for better portability / compatibility with other languages).
To make it work in this instance, you need at least 4 bytes in your buffer.
int main()
{
char q[4];
f(q);
std::cout << q << std::endl;
}
would work.
Inside the function f you can use std::string::copy to copy into the buffer. Let's modify f.
(We assume this is a prototype and in reality you have a meaningful name and it returns something more meaningful that you retrieve off somewhere).
size_t f( char * buf, size_t capacity )
{
std::string s = "def";
size_t copied = s.copy( buf, capacity-1 ); // leave a space for the null
buf[copied] = '\0'; // std::string::copy doesn't write this
return s.size() + 1; // the number of bytes you need
}
int main()
{
char q[3];
size_t needed = f( q, 3 );
std::cout << q << " - needed " << needed << " bytes " << std::endl;
}
Output should be:
de needed 4 bytes
In your question you suggested you can change your function but not the way it is called. Well in that case, you actually have only one real solution:
void f( const char * & p )
{
p = "def";
}
Now you can happily do
int main()
{
const char * q;
f( q );
std::cout << q << std::endl;
}
Note that in this solution I am actually moving your pointer to point to something else. This works because it is a static string. You cannot have a local std::string then point it to its c_str(). You can have a std::string whose lifetime remains beyond the scope of your q pointer e.g. stored in a collection somewhere.
Look at the warnings you get while compiling your code (and if you don’t get any, turn up the warning levels or get a better compiler).
You will notice that despite the type declaration, the value of q is not really mutable. The compiler was humoring you because not doing so would break a lot of legacy code.
You can't do that because you assigned a string literal to your char*. And this is memory you can't modify.
With your f, You should do
int main(){
char q[4 /*or more*/];
f(q);
std::cout << q << std::endl;
}
The problem is that you are trying to write on a read-only place in the process address space. As all the string literals are placed in read-only-data. char *q = "abc"; creates a pointer and points towards the read-only section where the string literal is placed. and when you copy using strcpy or memcpy or even try q[1] = 'x' it attempts to write on a space which is write protected.
This was the problem among many other solutions one can be
main(){
char *q = "abc"; \\ the strings are placed at a read-only portion
\\ in the process address space so you can not write that
q = new char[4]; \\ will make q point at space at heap which is writable
f(q);
cout << q << endl;
delete[] q;
}
the initialization of q is unnecessary here. after the second line q gets a space of 4 characters on the heap (3 for chars and one for null char). This would work but there are many other and better solutions to this problem which varies from situation to situation.