I need a char* for my filename. It should be something like this: cards/h11.bmp
i have a function I cobbled together from various SO articles:
char* getFileName(int* pc1_no, char* suite)
{
int number;
char pCard1[80];
strcpy_s(pCard1, "cards/");
strcat_s(pCard1, suite);
number = *pc1_no;
cout << number << endl;
string str = to_string(number);
char const *pchar = str.c_str();
strcat_s(pCard1, pchar);
strcat_s(pCard1, ".bmp");
return pCard1;
}
Which of course, returns garbage. I don't quite get getting the pointer value. I pretty sure I have made a dumb mistake with the pointer. Thanks in advance.
The best way to do all this is get rid of all the pointers and use a string all of the way through:
std::string getFileName(int pc1_no,
const std::string & suite)
{
std::string pCard1 = "cards/" + suite + std::to_string(pc1_no) + ".bmp";
return pCard1;
}
or if building to older C++ standards where std::to_string is not available:
std::string getFileName(int pc1_no,
const std::string & suite)
{
std::stringstream pCard1;
pCard1<< "cards/" << suite << pc1_no << ".bmp";
return pCard1.str();
}
Rational
char pCard1[80];
is a local variable. It will die at the end of the function, so the function returns a pointer to invalid memory. Many bad things can happen as a result and few good. Watch out for the few good. They are liars waiting for the most opportune time to strike.
The simplest solution maintaining OP's structure is use a std::string to perform string manipulation inside the function.
std::string getFileName(int* pc1_no, char* suite)
{
std::string pCard1 = "cards/";
pCard1 += std::string(suite);
pCard1 += std::to_string(*pc1_no);
pCard1 += std::string(".bmp");
return pCard1;
}
The above is Horrible Code, both excessively verbose and inefficient, but we've already covered the right way in the intro. This is just a touch-point on the logical progression to that right way.
Faster and less complex is to take advantage of std::stringstream's ability to format c-style strings and numbers without any outside help. This approach is probably the best up until std::to_string became available in the C++11 standard.
std::string getFileName(int* pc1_no, char* suite)
{
std::stringstream pCard1;
pCard1<< "cards/" << suite << *pc1_no << ".bmp";
return pCard1.str();
}
There is the possibility of a performance penalty returning the string by value, but the compilers of the past few decades are good at detecting and taking advantage of opportunities to omit unnecessary copying and employing move semantics behind the scenes.
Returning string by value is also far less error-prone than dynamically allocating storage and returning the storage to a caller with the expectation that the caller release it. Any performance penalty that may remain is highly likely to be worth the price. Profile the code to be sure.
Improving the function call:
Passing pc1_no in as a pointer is not helping in any way. Unless you need to modify the value inside the function, just pass by value. If you do need to change the value, prefer a reference.
std::string getFileName(int pc1_no, char* suite)
If you try to pass a string literal: eg:
getFileName(&somenumber, "string literal");
string literals may be in non-writable memory and are always const char * in C++. Passing a const value into a space that could attempt to change the value is bad form. This was allowed for backwards compatibility with C under older C++ Standards, though it may generate an warning, but is illegal in after the C++ 11 Standard.
If your function does not need to modify the contents of the char array, and this doesn't, it's a good practice to tag the string as const and allow the compiler to prevent accidents regardless of whether your compiler allows const to non-const assignments:
std::string getFileName(int pc1_no, const char* suite)
You may have more versatility if you use a reference to a const std::string as it is implicitly convertible from both const and non-const char arrays and allows the rest of your program to take advantage of the many benefits of std::string without needless calls to c_str and data.
std::string getFileName(int pc1_no,
const std::string & suite)
That brings us back to where this answer came in.
Here is how this code would look like in C++:
#include <string>
std::string getFileName(int* n, const std::string& suite) {
return "cards/" + suite + std::to_string(*n) + ".bmp";
}
Even better would be to take the first parameter by value (int n) and dereference at the call site if necessary.
char* getFileName(int pc1_no, char* suite)
{
static char pCard1[80]; // Guarantee safe pointer return
strcpy_s(pCard1, "cards/");
strcat_s(pCard1, suite);
string str = to_string(pc1_no);
char const *pchar = str.c_str();
strcat_s(pCard1, pchar);
strcat_s(pCard1, ".bmp");
return pCard1; // Now you don't lose the result.
}
Or you can use recommended C++ style which is already answered.
Related
I understand that it is always advisable to use std::string compared to const char * because of the inadvertent changes that may happen because of using a pointer. But for simple usages where there can't be any inadvertent changes by any other engineer, is it still a big deal. For e.g. see the code below -
So in this example below, I could have used
std::string BothUserNameAndPassword = "abracadabra";
I see time to time having to deal with code reviews that has code such as the above.
#include<iostream>
using namespace std;
void authenticatePassword(std::string const & username, std::string
const &password);
int main()
{
const char* BothUserNameAndPassword = "abracadabra";
authenticatePassword(BothUserNameAndPassword, BothUserNameAndPassword);
}
void authenticatePassword(std::string const & username, std::string const &password)
{
cout << "username = "<<username<<" and password = "<<password <<endl;
}
Is there any reason to change from using const char* to string [...]?
Yes, there is. The function signature reveals that two const std::string& arguments are expected. Now what happens if you pass in a const char* object? A temporary std::string instance is implicitly constructed. By going with
const std::string BothUserNameAndPassword = "abracadabra";
authenticatePassword(BothUserNameAndPassword, BothUserNameAndPassword);
you don't make this extra effort. I wouldn't argue that this is a performance-critical tweak, but it might be considered more readable, as you don't have to think about conversions when having a look at the function signature and the variable you are passing to it.
Note that from C++17 on, you might also want to consider std::string_view for such cases (both function arguments and binding the literal to a variable).
Your user will probably specify the username and password externally, which will be read in a string. So no reason not to use a string from the start. The double string creation artifact of this example may not be a real world or production case (at least I hope it's not...).
In modern C++ (post C++17), there is no good reason to use const char* in new code anymore, you should use string_view instead. This efficiently replaces the pointer as it has the size of the string and can also efficiently slice bits of the string. It is NOT suited for something that will end up being stored in a string. In that case, you should use a string from the start.
So there are two use cases, one fitted for string and one for string_view:
storing the data
using the data
In this specific case, I would do:
int main()
{
constexpr std::string_view BothUserNameAndPassword = "abracadabra";
authenticatePassword(BothUserNameAndPassword, BothUserNameAndPassword);
}
void authenticatePassword(std::string_view username, std::string_view password)
{
cout << "username = "<<username<<" and password = "<<password <<endl;
}
I have found a lot of examples from stack overflow, but those examples are based on non-const std::string. What if the string is const(top level and non top level)?
std::string encrypt(std::string const &input)
{
//do something, ArrayGuard is a smart pointer to guard the raw char* delete by free
ArrayGuard<char> guard(encrypt_str(const_cast<char*>(input.c_str())));
return std::string(guard.get(), std::strlen(guard.get()));
}
case 1(non top level?) :
std::string input = "abcde";
std::string encrypt_password = encrypt(input);
case 2(top level?) :
std::string const input = "abcde";
std::string encrypt_password = encrypt(input);
I am sure that the encrypt_str would not alter the char* I pass into it(if it is possible, I would like to ask them to fix this non const correction api).
ps : I am not sure that I am fully understanding what is top level const/&/&&/volatile, that is why I put "?" behind the case
The rule you have to watch out for regarding const_cast is that if you use it to modify an object that was originally declared as const, then you get undefined behaviour.
As long as you're sure this doesn't happen, you're good.
The constness of the std::string doesn't make any difference. Even if the std::string is not const, the c_str function still returns a const char*. Nothing changes when you make the std::string const.
I have a problem which i cannot fix on my own.
string filenameRaw;
filenameRaw= argv[1];
function(filenameRaw.c_str(),...);
function(const char* rawDataFile,const char* targetfieldFile,const char* resultFile,const char* filename)
...
this->IOPaths.rawData=rawDataFile;
...
works very fine so far. Now I try to put another string in the variable IOPaths.rawData...
function(const char* rawDataFile,const char* targetfieldFile,const char* resultFile,const char* filename)
...
string filenameRaw;
filenameRaw=reader.Get("paths", "rawData", "UNKNOWN")
...
const char* rawDataFile1=filenameRaw.c_str();
cout << "Compare: " << strcmp(rawDataFile,rawDataFile1) <<endl;
...
this->IOPaths.rawData=rawDataFile1;
this does not work any more. Later in my programm I get errors with the filename. The strcmp definitly gives a 0, so the strings must be equal. Does anyone has an idea what i am doing wrong?
The validity of the output of c_str() is limited to, at most, the lifetime of the object on which c_str() was called.1
I suspect that this->IOPaths.rawData is pointing to deallocated memory once filenameRaw is out of scope.
An adequate remedy would be to pass the std::string around rather than [const] char*. A good stl implementation would use copy on write semantics for the string class so perhaps you wouldn't be repeatedly copying string data.
1In certain instances (such as if the object is modified), it could be less.
I am trying to convert an int to a cstring. I've decided to read the int into a regular string via stringstream, and then read the string into a char array. The following seems to be working, but I'm wondering if I'm just getting lucky with my compiler. Does the code seem sound? Thanks!
int zip = 1234;
char zipString[30];
stringstream str;
str << zip;
str >> zipString;
cout << zipString;
You can get a C++ std::string from the stream's str() function, and an immutable C-style zero-terminated string from the string's c_str() function:
std::string cpp_string = str.str();
char const * c_string = cpp_string.c_str();
You might be tempted to combine these into a single expression, str.str().c_str(), but that would be wrong; the C++ string will be destroyed before you can do anything with the pointer.
What you are doing will work, as long as you're sure that the buffer is large enough; but using the C++ string removes the danger of overflowing the buffer. In general, it's best to avoid C-style strings unless you need to use an API that requires them (or, in extreme circumstances, as an optimisation to avoid memory allocation). std::string is usually safer and easier to work with.
Unless you have a specific reason that you need an array of char instead of a standard string, I'd use the latter. Although it's not strictly necessary in this case, I'd also normally use a Boost lexical_cast instead of explicitly moving things through a stringstream to do the conversion:
std::string zipString = lexical_cast<std::string>(zip);
Then, if you really need the result as a c-style string, you can use zipString.c_str() to get that (though it's still different in one way -- you can't modify what that returns).
In this specific case it doesn't gain you a lot, but consistent use for conversions on this general order adds up, and if you're going to do that, you might as well use it here too.
The std::string's c_str() member function returns a const char* (aka a C-style string).
std::string str = "world";
printf("hello, %s", str.c_str());
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Can I get a non-const C string back from a C++ string?
Do I need to convert it first? I saw in another post that .c_str() can be used if the function expected const char*. What about for just char*?
std::vector<char> buffer(s.begin(), s.end());
foo(&buffer[0], buffer.size());
s.assign(buffer.begin(), buffer.end());
There is no way to get a char* from a string that is guaranteed to work on all platforms, for the simple fact that string is not required to use contiguous storage.
Your safest, most portable course of action is to copy the string somewhere that does use contigious storage (a vector perhaps), and use that instead.
vector<char> chars(my_string.begin(), my_string.end());
char* ptr = &chars[0];
If you want to be hacky and non-portable and decidedly unsafe, you can confirm that your string implementation does in fact use contigious storage, and then maybe use this:
&my_str[0]
But I would punch any developer that worked for me that did this.
EDIT:
I've been made aware that there are currently no known STL implementations that do not store the string data in a contiguous array, which would make &my_str[0] safe. It is also true (and I was asked to state this) that in the upcoming C++0x standard, it will be required for the storage to be contiguous.
It's been suggested that because if these facts that my post is factually incorrect.
Decide for yourself, but I say no. This is not in the current C++ standard, and so it is not required. I will still in practice do things the way I have suggested, and in any code review I will flag any code that assumes the underlying storage is contigious.
Consider this. Suppose there were a question about vtable pointers. Someone wants to examing a class and get the pointer to a virtual function by looking at the vtable. I would immediately tell them not to do this because there is no mention of how virtual methods are implemented in C++. Every implementation I know uses vtables, and I can't think of a better way to do it. It is likely that polymorphism will forever be implemented using vtables. Does that make it ok to examing the vtable directly?
IMO no, because this depends on undocumented implementation details. You have no control over this, and it could change at any time. Even if you expect it will never change, it is still bad engineering to rely on these implementation details.
Decide for yourself.
There are three scenarios:
If the function is outside of your control, and it either modifies the string, or you don't and can't know if it modifies the string:
Then, copy the string into a temporary buffer, and pass that to the function, like so:
void callFoo(std::string& str);
{
char* tmp = new char str(str.length() +1);
strncpy(tmp, str.c_str(), str.length());
foo(tmp);
// Include the following line if you want the modified value:
str = tmp;
delete [] tmp;
}
If the function is outside of your control, but you are certain it does not modify the string, and that not taking the argument as const is simply a mistake on the API's part.
Then, you can cast the const away and pass that to the function
void callFoo(const std::string& str)
{
foo(const_cast<char*> str.c_str());
}
You are in control of the function (and it would not be overly disruptive to change the signature).
In that case, change the function to accept either a string& (if it modifies the input buffer) or either const char* or const string& if it does not.
When a parameter is declared as char* there it is implicitly assumed that the function will have as a side effect the modification of the string that is pointed. Based in this and the fact that c_str() does not allow modifications to the enclosed string you cannot explicitly pass an std::string to such a method.
Something like this can be achived by following the following approach:
#include <cstdlib>
#include <string>
#include <iostream>
void modify_string(char* pz)
{
pz[0] = 'm';
}
class string_wrapper
{
std::string& _s;
char* _psz;
string_wrapper(const string_wrapper&);
string_wrapper& operator=(const string_wrapper&);
public:
string_wrapper(std::string& s) : _s(s), _psz(0) {}
virtual ~string_wrapper()
{
if(0 != _psz)
{
_s = _psz;
delete[] _psz;
}
}
operator char*()
{
_psz = new char[_s.length()+1];
strcpy(_psz,_s.c_str());
return _psz;
}
};
int main(int argc, char** argv)
{
using namespace std;
std::string s("This is a test");
cout << s << endl;
modify_string(string_wrapper(s));
cout << s << endl;
return 0;
}
If you are certain that the char* will not be modified, you can use const_cast to remove the const.
It's a dirty solution but I guess it works
std::string foo("example");
char* cpy = (char*)malloc(foo.size()+1);
memcpy(cpy, foo.c_str(), foo.size()+1);