I am trying to implement the functions below, but the output for foo() is a bunch of nonsense. I tried to run the debugger and didn't see any problems inside the append function. But the total variable in foo() isn't properly assigned the value "abcdef". Any ideas why?
int main()
{
cout<<"foo is"<<endl;
foo();
return 0;
}
const char* append(const char* s1, const char* s2) {
string s(s1);
s += s2;
return s.c_str();
}
void foo() {
const char* total = append("abc", "def");
cout<<total;
}
Because in append(), you returned s.c_str(); then, s is destructed, which means that the pointer returned is invalidated immediately.
Let append() return std::string to solve this issue.
std::string append(const char* s1, const char* s2) {
return std::string(s1).append(s2);
}
void foo() {
std::string total = append("abc", "def");
cout << total;
}
Undefined Behaviour. c_str is only valid for the lifetime of s (and only then if s is not modified in any way). Once append has returned, s is out of scope. Boom!
One fix is to have append return a std::string.
const char* append(const char* s1, const char* s2) {
string s(s1);
s += s2;
return s.c_str();
}
The variable s is local to the function. It is destroyed when the function returns. That means that the value that you return, s.c_str(), points to memory that has been deallocated. De-referencing that memory results in undefined behaviour.
The rule with c_str() is, loosely put, that the value it returns is guaranteed to be valid until either the string object is modified or destroyed.
Simply put, you should stop using C strings unless you need to use them for interop. Your function should be:
string append(const string& s1, const string& s2)
{
return s1 + s2;
}
You are returning a pointer from append() that is not valid when the function returns.
string s(s1);
defines a object in append(). It is destroyed when you return from the function. Hence, the returned value of s.c_str() is not valid in foo.
You can change your code to be:
string append(const char* s1, const char* s2) {
string s(s1);
s += s2;
return s;
}
void foo() {
string total = append("abc", "def");
cout<<total;
}
That should work.
return s.c_str(); : you return pointer obtained from temporary variable s.
You may fix your problem in two ways -
Return std::string by value from append.
Pass to append pointer to fill it with data -
void append(const char* s1, const char* s2, char* out) {
string s(s1);
s += s2;
strncpy(out, s.c_str(), s.size()+1);
}
void foo() {
char total[7] = {0}; //should be enough to carry appended string + '\0'
append("abc", "def", total);
cout<<total;
}
Related
I try to call erase() function to remove whitespaces in string for function that converts string to integer. The same code works in main() but inside function it doesn't.How can I fix it?
int convert(const string &line)
{
for (int i=0;i<line.length();i++)
{
char wh=' ';
if(line.find(wh))
line.erase(find(line.begin(),line.end(),' '));
//cout<<line[i];
}
if(line.length()==0)
return 0;
int a=line[line.length()-1]-'0';
int b=convert(line.substr(0,line.length()-1))*10;
return a+b;
}
try to pass line by value instead of const reference. The const ref can't be changed
int convert(std::string line)
Try removing const from convert().
Anything declared with const can't be modified.
I tried to derive from std::exception like so:
class bad_number : public std::exception
{
public:
bad_number(const char*);
virtual const char* what() const noexcept;
private:
const char* num;
};
bad_number::bad_number(const char * num) : num(num){ }
const char* bad_number::what() const noexcept
{
std::string str;
str.append("Invalid number format:");
str.append(num);
char* result = new char[str.size()];
strcpy(result, str.c_str());
return result;
}
//The function that uses the exception
long double convert(const char *str)
{
char *endptr;
double result = strtold(str, &endptr);
if (*endptr != '\0')
throw bad_number(str);
return result;
}
The main function, in turn is the following:
int main(int argc, char ** argv)
{
std::vector<long double> vect;
for(int i = 1; i < argc; i++)
{
try{
vect.push_back(convert(argv[i]));
} catch (bad_number& e){
std::cout << e.what() << std::endl; //free():
//invalid pointer: 0x000000000131bc40 ***
}
}
}
DEMO
Why is the invalid pointer error printed? On my platform (Windows 8 + cygwin) it prints the actual what() return value. Does it mean that I have UB? If so, where in my code is UB?
In general, how can I fix that? I want a human-readable what() return value to be printed instead.
std::string::size() returns the number of characters in the string, which does not include a null terminator. Thus result is one char too short, and strcpy writes past it.
Besides, it's really a bad idea to dynamically allocate a new char array everytime what() is called. This is made worse by the fact that what() is declared noexcept, even though new may throw.
Better construct an std::string in the exception's constructor, store it as a member variable, and just return its c_str().
Modify below what function code like this
const char* bad_number::what() const noexcept
{
std::string str;
str.append("Invalid number format:");
str.append(num);
char* result = new char[str.size()+1];
strcpy(result, str.c_str());
return result;
}
I have been searching around the web for returning a string in a function.
const char *func()
{
const char *s1 = "hello";
return s1;
}
This works if you predefine the string. How do I let the user input a string and return that specific string
const char *func()
{
char s1[313];
cin >> s1;
return s1;
}
I tried the above but it gave the warning
warning: address of stack memory associated with local variable
's1' returned [-Wreturn-stack-address]
return s1;
One easy way to do it would be to use an std::string:
#include <string>
#include <iostream>
std::string func()
{
std::string s1;
std::cin >> s1;
return s1;
}
You can either allocate a character array inside the function or pass an array as an argument to the function.
In the first case you can either use standard class std::string or you will need to allocate the array your self.
For example
std ::string func()
{
std::string s1;
cin >> s1;
return s1;
}
or
char * func()
{
const size_t N = 313;
char *s1 = new char[N];
cin.getline( s1, N );
return s1;
}
in the second case the function can look the following way
char * func( char s1[], size_t n )
{
cin.getline( s1, n );
return s1;
}
and in main it could be called as
int main()
{
const size_t N = 313;
char s1[N];
func( s1, N );
}
The "correct" way of doing this in C++ is to use an std::string, as juanchopanza said, but just FYI, one could achieve this without std::strings via something like this:
char* func() {
char* s1 = new char[313]; // allocate memory on the heap
cin >> s1;
return s1;
}
Though this has the stipulation of requiring the caller to delete[] the result of the function:
char* s = func();
// do stuff with s
delete[] s; // must be called eventually
Don't ever do this in actual C++ code though -- use std::string.
The method fails to find the char* array passed to it even though it is present in map.
When I replaced char* with std::string in map. Code works fine.
static void CreateTranslationMap();
static UString FindTranslatedString(char* propertyName);
static std::map<char*,UString> TranslationMap ;
static void CreateTranslationMap()
{
UString engString("TextAlignmentPosition");
char* transString= MSGTXT("TextAlignmentPosition");
TranslationMap.insert(std::pair<char*,UString>(transString,engString));
}
UString FindTranslatedString(char* propertyName)
{
UString NotFound("CannotFind");
std::map<char*, UString>::iterator itr;
itr = TranslationMap.begin();
itr = TranslationMap.find(propertyName);
if(itr!= TranslationMap.end())
{
return itr->second;
}
else if(itr== TranslationMap.end())
{
return NotFound;
}
}
You need to use your own custom comparator for comparing pointer to char
Use:
struct cmp_c_string
{
bool operator()(char const *lhs, char const *rhs) const
{
return std::strcmp(lhs, rhs) < 0;
}
};
std::map<char*,UString, cmp_c_string > TranslationMap ;
That's because when doing comparison for equality the map uses <.
When the Key of the map is char* you are doing comparisons of pointers (not the C-String). So you are testing to see if one pointer is less than the other pointer (ie comparing the address).
When the Key of the map is std::string you using the operator< that is defined for std::string, which actually compares the characters in the string to determine which is less than the other.
As the std::map is a template it actually takes more parameters to define how it works. The third parameters is the comparison function (which defaults to less std::less<K> which is usually operator<).
So you can use char* you just need a custom comparator operator.
bool comparitor(char const* lhs, char const* rhs)
{
return (test for less than or some other strict weak function);
}
std::map<char*, UString, comparitor> myMap;
when using char *, it just compare address.
char a[] = "hi";
char b[] = "hi";
char *c = a;
char *d = b;
c & d are different.(c != d) If you want to compare string, you should use strcmp.
But when using string, it overwrites "==" operation.
So you can just compare using "=="
string a = "hi";
string b = "hi";
a & b are same. (a == b)
You have this behavior because you use pointer to string literal which is different every time you create such a pointer. So, for example, you create 2 pointers:
char* p1 = "Hello world!";
char* p2 = "Hello world!";
While content to which p1 and p2 point is identical the pointers, themselves, are different. So p1 != p2, and you trying to store pointer in the map. You should use std::string instead or have global constants pointers which you'd use everywhere; something like:
const char* const c_transString = MSGTXT("TextAlignmentPosition");
...
TranslationMap.insert(std::pair<char*,UString>(c_transString, engString));
...
FindTranslatedString(c_transString)
Just replace char* to const char* because the map data type always take the string in const form . I took your example and it is running in my terminal. So the new code is :
#include<iostream>
using namespace std;
static void CreateTranslationMap();
static string FindTranslatedString(const char* propertyName);
static std::map<const char*,string> TranslationMap ;
static void CreateTranslationMap()
{
string engString("TextAlignmentPosition");
const char* transString= ("1");
TranslationMap.insert(std::pair<const char*,string>(transString,engString));
}
string FindTranslatedString(const char* propertyName)
{
string NotFound("CannotFind");
std::map<const char*, string>::iterator itr;
itr = TranslationMap.begin();
itr = TranslationMap.find(propertyName);
if(itr!= TranslationMap.end())
{
return itr->second;
}
else if(itr== TranslationMap.end())
{
return NotFound;
}
}
int main()
{
CreateTranslationMap();
string s =FindTranslatedString("1");
cout<<s<<endl;
return 0;
}
I'm confused about char * and const char *. In my example I'm not sure how to put them together. I have several const char * strings I would like to concatenate to a final const char * string.
struct MyException : public std::exception
{
const char *source;
int number;
const char *cause;
MyException(const char *s, int n)
: source(s), number(n) {}
MyException(const char *s, const char *c)
: source(s), number(0), cause(c) {}
const char *what() const throw()
{
if (number != 0) {
char buffer[1024];
// why does this not work?
cause = strerror_r(number, buffer, 1024);
}
// how to concatenate the strings?
return source + ": " + cause;
}
};
You can store a std::string and still return a const char * from your what function.
struct MyException : public std::exception
{
private:
std::string message;
public:
MyException(const char *s, int n) {
char buffer[1024];
strerror_r(n, buffer, 1024);
message.reserve(strlen(s) + 2 + strlen(buffer));
message = s;
message += ": ";
message += buffer;
}
MyException(const char *s, const char *c) {
message.reserve(strlen(s) + 2 + strlen(c));
message = s;
message += ": ";
message += c;
}
const char *what() const throw()
{
return message.c_str();
}
};
Just use strcat() and strcpy() function from string.h.
http://www.cplusplus.com/reference/clibrary/cstring/strcat/
http://www.cplusplus.com/reference/clibrary/cstring/strcpy/
Also, since you don't have to modify original strings, the difference between const char* and char* doesn't matter.
Also don't forget to malloc() (reserve the space for) the required size of destination string.
This is how I'd implement this:
struct MyException : public std::exception
{
public:
const char *source;
int number;
const char *cause;
private:
char buffer[1024]; // #1
std::string message; // #2
std::string build_message() {
if (number != 0) {
cause = strerror_r(number, buffer, 1024); // use the member buffer
}
std::string s; // #3
s.reserve(strlen(source) + 2 + strlen(cause));
return s + source + ": " + cause;
}
public:
MyException(const char *s, int n)
: source(s), number(n), cause(), message(build_message()) {}
MyException(const char *s, const char *c)
: source(s), number(0), cause(c), message(build_message()) {}
const char *what() const throw()
{
return message.c_str(); // #4
}
};
Things to note:
The original code was using a local variable for a buffer. That is a bad idea, as the pointer stored in cause would be invalid the moment the scope ends.
For the concatenated message, dynamic allocation would be required. And that also means that cleanup of that storage would be required. I grabbed an existing tool that does that and provides string-like operations: std::string.
With std::string concatenation can be done with the + operator. Note how I asked it to reserve memory for the expected size. This is memory an optimization, and is not required: the string would allocate enough memory either way.
what cannot throw an exception, otherwise a call std::unexpected would arise. So the string cannot be allocated here.
If you must work with char* pointers, you will want to use strcat. strcat takes two arguments a char* and a const char* and appends the string pointed to by the const char* onto the char*. This means you first need to copy your first string over.
You'll want to do something like this:
char* Concatenate(const char* first, const char* second)
{
char* mixed = new char[strlen(first) + strlen(second) + 2 /* for the ': ' */ + 1 /* for the NULL */];
strcpy(mixed, first);
strcat(mixed, ": ");
strcat(mixed, second);
return mixed;
}
Isn't that just ugly? And, remember, because you've dynamically allocated the char* returned by that function the caller must remember to delete[] it. This ugliness and the need to ensure the caller cleans up in the right way is why you're better off using a string implementation such as std::string.
Allocate a buffer of size strlen(source) + strlen(cause) + 3 and use sprintf to create your message. Actually you can move this code to constructor so that what becomes simple getter.
If you really must use c-strings, you should look at strcat() to concatenate them together. However, since you are creating a custom exception, it would be reasonable to consider using std::string instead because it is more friendly to use in C++.