I have came across a situation as follows and I am getting following warning from the compiler.
Main.cpp:14:22: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
Login(USER,PASSWORD);
Here are the codes
Passwords.h
#define USER "user"
#define PASSWORD "pass"
Main.cpp
#include <iostream>
#include "Passwords.h"
using namespace std;
void Login(char* username,char* password)
{
cout << "UserName is " << username <<endl;
cout << "Password is " << password <<endl;
}
int main()
{
Login(USER,PASSWORD);
return 0;
}
One thing I decided to do was to introduce a global variables in the Passwords.h file. But I really like to know what is the best practice to solve this issue.
I hope no one will mark this as duplicate as same question is asked. I need to eliminate this warning in legitimate way and looking for a best practice as many answers for the same problem gave some hacks to turn off compiler warnings and some casting solutions.
Update
The Login function is actually a virtual function so the parameter datatypes cannot be changed from char * to const char*. I used the specific code segment for simplicity.
But I really like to know what is the best practice to solve this issue.
The best practice is to use pointers/references to const when no modification is made to the pointed object/array. You should make the following change to the parameters:
void Login(const char* username, const char* password)
Conversion from string literal to const char* is well-formed and not deprecated.
the parameter datatypes cannot be changed from char * to const char*
Your requirement excludes the best practice shown above. Given this restriction, you should not pass a string literal to the function. The options left are workarounds. The workaround that I would suggest is to create a local copy of the literal, and pass that instead:
char user[] = USER;
char pass[] = PASSWORD;
Login(user,pass);
PS. Since C++11 the conversion from string literal to char* is not only deprecated, but ill-formed instead. A compiler conforming to the current standard may refuse to compile the program.
PPS. As pointed out in the comments, storing a password within the executable as plain text (which is the way string literals are stored) is dubious from security perspective.
Given that you can't change the function signature to something more appropriate, you're stuck. Bad code forces more bad code.
In this case you need to copy the strings to something that isn't const.
int main()
{
char user[] = USER;
char password[] = PASSWORD;
Login(user,password);
return 0;
}
Just do
void Login(const char* username, const char* password)
While you are on it, replace define with constexpr.
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've written the following program to match regular expressions in C++
#include <regex.h>
#include <iostream>
using namespace std;
/*
* Match string against the extended regular expression in
* pattern, treating errors as no match.
*
* return true for match, false for no match
*/
bool match(const char *string, char *pattern)
{
int status; regex_t re;
if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0)
return false;
/* report error */
status = regexec(&re, string, (size_t) 0, NULL, 0);
regfree(&re);
if (status != 0) {
return false; /* report error */
}
return true;
}
int main()
{
string str = "def fadi 100";
bool matchExp = match(str.c_str(), "^[Dd][Ee][Ff][' '\t]+[A-z]+([,])?[''\t]+[0-9]+$");
cout << (matchExp == true ? "Match": "No match") << endl;
}
The program works fine just as expected, but when I compile the code using gcc with the -Wall -Werror arguments (Linux environment), I get a very annoying warning message saying the following:
main.cpp: In function ‘int main()’:
main.cpp:33:90: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
Is there a way to force the compiler to believe that str.c_str() is the same as char * str? if so, how?
No, there isn't. That conversion was deprecated in C++03 and is illegal in C++11; don't do it.
Deprecation of that conversion comes from the fact that string literals are read-only, hence const; accessing them using a pointer to non-const char could possibly lead to modifying const objects, hence invoking undefined behavior. The warning isn't annoying; it is meant to save you from possibly crashing your application - or worse.
Also, you are wrong in reading the warning message; it isn't about c_str(), it is about passing string literal as char *.
The only way to really fix your code is to change second parameter of your match to be const char *, not char *, and copy the passed string to a new, buffer, internal to that function (why not in main()? Because with internal buffer, you have less boilerplate on the caller's side).
I'd also like to suggest totally different solution, since the question is tagged "C++": Boost.Regex.
Is there a way to force the compiler to believe that str.c_str() is the same as char * str?
That's actually not the issue here - you are already passing str.c_str() as a const char*.
The issue is that the second parameter is (also) a string literal, but has type char*. Try changing the second parameter to const char*.
If that still raises errors (due to the regex.h functions not specifying the correct const-ness), you're going to have to do something like this in main() or match():
char pattern[] = "^[Dd][Ee]...etc";
bool matchExp = match(str.c_str(), pattern);
See here for the reason why.
The problem is that a string literal should be only assigned to a pointer of a const char, so you need to change match to take a char const* pattern (which should be possible when you pass a string literal)
Make 2 parameter of function match const char *, warning is because of it
I have the function below in a file called WiServer.h for Arduino.
GETrequest(uint8* ipAddr, int port, char* hostName, char* URL);
Now the problem is I need to concatenate an int value (setting1) to the char* URL parameter like the below for example.
"twittermood.php?status=sendTweet&setting1="+setting1
I get an error:
invalid conversion from const char* to char*
How do I fix it?
You've gotten decent generic C++ advice, but for the special case of Arduino on an 8-bit AVR microcontroller I think you need platform-specific advice:
The Arduino runtime provides a String object. Your question is probably covered by these examples.
Because of the very limited RAM space on Arduino it is common to use special attributes to put constant strings in flash (essentially ROM) which requires different instructions to access. AVR code built with GCC is typically built on top of AVR Libc which has support for operating on a mix of constant strings and RAM strings. You must be explicit in your code and choose the right operations. This requires at least a basic understanding of how C strings work and how pointers work. I'm not sure how much of this cleverness is automatically provided by the Arduino String, but without this cleverness all of your string constants will end up copied into RAM at boot and will take up those precious bytes all the time.
If RAM space becomes a problem or you are working on an AVR application that does extensive string manipulation you need to learn how to use the mix of PGM Space operations (string functions that can work on read-only strings in flash) and regular C-style RAM-based string operations.
Use std::string, rather than C strings. Use string streams, rather than trying to concatenate non-string values to strings:
std::ostringstream oss;
oss << "twittermood.php?status=sendTweet&setting1=" << setting1;
use(oss.str()); // or use(oss.str().c_str());
If that API really needs a non-const string (given that it doesn't even take the length of the string, I suppose it's just a buggy API disregarding const), copy the string to a buffer and pass that:
const std::string& str = oss.str();
std::vector<char> buffer(str.begin(), str.end());
buffer.push_back('\0');
GETrequest(addr, port, &buffer[0], c);
As for what really happens when you do what you do:
"twittermood.php?status=sendTweet&setting1=" is an rvalue of the type char[43], which implicitly converts to const char*, a pointer to the first character. To that you add an integer, by this forming a new pointer of the type const char* pointing to some more or less random memory location. I suppose you try to pass this as the char* to your API function, for which the const would have to be dropped.
A C++ compiler, however, will never implicitly drop a const — for your own good.
Use a std::string, not a char*, for this sort of work. A char* in C is extremely basic and if you're not familiar with how C works, very easy to use wrong.
If you need to use char*, look into strcpy, strcat and snprintf. But these functions are very dangerous in a novice's hands and can lead to memory corruption and crashing.
You can use an ostringstream for this:
#include <sstream>
// ...
std::ostringstream os;
os << "twittermood.php?status=sendTweet&setting1=" << setting1;
GETrequest(addr, port, hostname, os.str().c_str());
Use std::string instead of char* and maybe a std::stringstream for your concatination. But first about your errors:
Your problem is that "twittermood.php?status=sendTweet&setting1=" will get you a const char*, which can't be implicitely converted to a char*. If you are really sure that GETrequest doesn't try to change the value of its URL parameter, you can use const_cast<char*>(...) on your const char* variable to cast away the constness. However, do this only if you are absolutely sure it won't be changed (don't lie to the compiler about constness (or anything really)).
Even if you do that "twittermood.php?status=sendTweet&setting1="+setting1 won't do what you think it does. As I said your string constant will give you a const char*, which doesn't have any knowledge about string operations. So adding an intto it won't concat that int to the string, but instead do some pointerarithmetic, so if you are lucky and your int was small enough you get only a part of the URL, otherwise you will address something completely different.
Posting C solution for completeness:
const char ctext[] = "twittermood.php?status=sendTweet&setting1=";
char text[sizeof(ctext) + 20];
snprintf(text, sizeof(text), "%s%i", ctext, setting1);
std strings and streams are much nicer/safer to use.
which is the best way to concat?
const char * s1= "\nInit() failed: ";
const char * s2 = "\n";
char buf[100];
strcpy(buf, s1);
strcat(buf, initError);
strcat(buf, s2);
wprintf(buf);
It gives error. What should be the correct way?
Thanks.
I think the correct way is:
std::string msg = std::string("Init() Failed ") + initError + "\n";
std::cout<<msg;
or
std::cout<<"Init() Failed "<<initError<<"\n";
Your big problem is that you're mixing data types. Use either char and associated functions or wchar and associated functions. If you need to mix them, use a conversion function. This makes no more sense than trying to pass a float to a function needing a string. (The compiler should be able to catch both problems, since the declaration of wprintf is something like int wprintf(const wchar_t *, ...).)
Another, more minor, issue is that printf and such are not the right functions to print out general strings, since if there are any percent signs in the strings you'll get undefined behavior. Use printf("%s",...) or puts(...) or related functions.
And, since this is C++, you'd be much better off using the std::string class. It isn't perfect, but it's far better than C-style strings.
Also, telling us what the error is would help. You haven't even told us whether it's a compiler error or a run-time error.
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);