i had met same situation in my project.
The following code were similarly reproduced.
#include <iostream>
#include <vector>
using namespace std;
class METHOD
{
public:
vector<METHOD*> m_arg;
string name;
string getName()
{
return name;
}
METHOD()
{
}
METHOD(string n)
{
name = n;
}
};
class PF : public METHOD
{
public:
PF(){};
};
void main()
{
PF p;
p.m_arg.push_back(new METHOD(string("a")));
p.m_arg.push_back(new METHOD(string("b")));
p.m_arg.push_back(new METHOD(string("c")));
for(int i = 0 ; i < (int)p.m_arg.size() ; ++i)
{
const char* ccs = p.m_arg[i]->getName().c_str(); //PROBLEM POINT
size_t ss = strlen(ccs);
}
}
the problem is //PROBLEM POINT
the return address of c_str() can't be assigned to const char* ccs.
in debugger, the css assigned memory address of return of c_str(), but css's value is "" and it work same as ""
but the next code is working as expected.
string str("ok");
const char* ckk = str.c_str();
after this code, ckk points after str.c_str() returnning addres, and ckk's value in debugger is "ok"
the same problem is occuring in std::wstring, wchar_t, unicode.
why did this problem occur?
who knows? help me plz~
ADD:
thanks to mystical i solve the problem same way. but this solution also work OK.
for(int i = 0 ; i < (int)p.m_arg.size() ; ++i)
{
string& strr = p.m_arg[i]->getName();
const char* ccs = strr.c_str();
size_t ss = strlen(ccs);
}
so i think the return value of getName() is live until the loop } closed.
but interestingly, the next is don't work
for(int i = 0 ; i < (int)p.m_arg.size() ; ++i)
{
string* strr = &p.m_arg[i]->getName();
const char* ccs = strr->c_str();
size_t ss = strlen(ccs);
}
the css is 'dangling' and also strr is ""
i've dazed and confused!
In your getName() function:
string getName()
{
return name;
}
The string that is returned is a copy of the original. It becomes an intermediate in the following expression:
const char* ccs = p.m_arg[i]->getName().c_str();
When the expression ends, that intermediate is destroyed. Therefore invalidating the pointer returned by c_str(). The result is that ccs becomes a dangling pointer.
The reason why this works:
string str("ok");
const char* ckk = str.c_str();
is because str is not an intermediate.
To make it work, change your loop to this:
for(int i = 0 ; i < (int)p.m_arg.size() ; ++i)
{
string str = p.m_arg[i]->getName()
const char* ccs = str.c_str();
size_t ss = strlen(ccs);
}
EDIT : To answer your new question:
The reason why this doesn't work:
string* strr = &p.m_arg[i]->getName();
is the same as before. getName() returns an intermediate. You then take the address of this and assign it to strr. The intermediate is destroyed after the statement, so strr becomes a dangling pointer as in the first case.
Related
This question already has answers here:
What is a dangling pointer?
(7 answers)
Is returning a reference to a local int variable always undefined behavior
(1 answer)
What are the basic rules and idioms for operator overloading?
(8 answers)
Why is the phrase: "undefined behavior means the compiler can do anything it wants" true?
(2 answers)
Closed last month.
I wrote my own string class for learning purposes. I'm trying to overload the += operator so that I can append strings together. But whenever I use the operator the inital object remains the same. I am confused.
StringF& StringF::operator+=(StringF& obj) {
const char* string = this->getString();
const char* stringToAppend = obj.getString();
const int stringLength = this->length();
const int stringToAppendLength = obj.length();
char* appendedString = new char[stringLength + stringToAppendLength];
appendedString[stringLength + stringToAppendLength] = '\0';
for (int i = 0; i < stringLength; i++) {
appendedString[i] = string[i];
}
for (int i = stringLength; i < stringLength + stringToAppendLength; i++) {
appendedString[i] = stringToAppend[i - stringLength];
}
StringF appendedObj = StringF::StringF(appendedString);
std::cout << "Appended obj: " << appendedObj.getString() << std::endl; //For debugging
return appendedObj;
}
Here is the class header file:
class StringF {
private:
const char* string;
public:
StringF(const char*);
int length();
const char* copy();
const char* getString();
const char* reverse();
int find(const char*);
StringF& operator+=(StringF&);
friend std::ostream& operator<<(std::ostream&, StringF&);
};
I want to be able to create two strings and append with one another. But the it doesn't work. Any ideas? If possible, I'd want an explanation as to why this doesn't work so I can try to figure it out on my own, as I am trying to learn I don't want the solution handed to me. But I'll take whatever.
So here is what I'm trying to do.
StringF s("Hello");
StringF s2(", World!");
s += s2;
std::cout << s << std::endl; //Should print "Hello, World!" but only prints "Hello".
Thanks in advance!
Edit: Here is the example for you to try. https://onlinegdb.com/ApfkIONXG
There are a few issues with the code as posted:
There is a buffer overflow
You are not modifying the this instance like operator+= is supposed to
You are returning a reference to a local
Here is some minimally edited code that fixes these issues
StringF& StringF::operator+=(StringF& obj) {
const char* string = this->getString();
const char* stringToAppend = obj.getString();
const int stringLength = this->length();
const int stringToAppendLength = obj.length();
// +1 for the null character we are appending
char* appendedString = new char[stringLength + stringToAppendLength + 1];
appendedString[stringLength + stringToAppendLength] = '\0';
for (int i = 0; i < stringLength; i++) {
appendedString[i] = string[i];
}
for (int i = stringLength; i < stringLength + stringToAppendLength; i++) {
appendedString[i] = stringToAppend[i - stringLength];
}
// TODO: de-allocate the previous 'string' member variable
// Now we need to update `this`. While we can't modify the data pointed
// at by the existing member pointer, we can change the pointer to point
// to the newly allocated data:
string = appendedString;
// operator+= should be returning a reference to `this`
return *this;
}
We can modify the member variable because const char* is a mutable pointer to constant characters. This means the characters at the address of the pointer are constant, but the memory storing the pointer address is still mutable.
|string: 0xDEADBEEF| // The pointer value is not const
|
|----> |abc\0| // This data is const
see Const before or const after? for more info on the const issue.
The return *this is because the general expectation of operator+= is that the following code should work:
StringF s1("a");
StringF s2("b");
StringF s3 = (s += s2);
assert(s3 == s);
This might seem like non-sense code in this simple example, but this pattern is useful in some cases and is the expected behavior of a operator+= overload.
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
using namespace std;
class MyString
{
char* str;
int size;
public:
MyString()
{
str = '\0';
size = 1;
}
MyString(const char* const s)
: str(new char[strlen(s)])
{
size = strlen(s) + 1;
strcpy(str, s);
}
MyString(const MyString& another)
: str(new char[another.size])
{
size = another.size;
for (int i = 0; i < size; i++)
str[i] = another.str[i];
}
~MyString()
{
delete[] str;
}
void set(const char* st)
{
size = (int)strlen(st) + 1;
str = new char[size];
for (int i = 0; i < size; i++)
str[i] = st[i];
}
bool isEqual(const MyString& other) const
{
if (size != other.size)
return false;
if (strcmp(str, other.str) == 0)
return true;
else
return false;
}
void print() const
{
for (unsigned int i = 0; i < size; i++)
cout << str[i];
cout << endl;
}
};
int main() {
MyString strs[] = {
MyString("C"),
MyString(),
MyString("Java")
};
strs[1].set("C++");
const int arraySize = sizeof(strs) / sizeof(MyString);
const MyString target("Java");
for (int i = 0; i < arraySize; i++) {
const MyString str(strs[i]); // copy constructor
if (str.isEqual(target)) {
cout << "[" << i << "]: ";
str.print();
break;
}
}
for (const MyString& str : strs) {
str.print();
}
}
In dev c++ it's working and there was no caution, but in visual studio 2019, catution like "heap corruption detected after normal block crt detected that the application" is occured. I don't what is problem.
When I debuged my code, it successfully worked befor the main fuction finished. When destructor worked, the caution was appeared.
Please help me :)
Your default constructor should not compile with recent GCC compilers, you can only assign NULL or nullptr to pointer
MyString() {
str = nullptr;
size = 0;
}
This constructor needs one extra 1 byte to allocate memory, then the buffer overflow can be avoided.
MyString(const char* const s) : str(new char[strlen(s) + 1]) {
size = strlen(s) + 1;
strncpy(str, s, size);
}
strncpy is a safer replacement for strcpy, I suggest you use this one.
You also need to add implementation for operator=, or the compiler-generated one will cause a memory leak once you use operator= to copy an object.
MyString& operator=(const MyString& another) {
if (&another == this) return *this;
delete[] str;
str = new char[another.size];
size = another.size;
// better to use strncpy here
for (int i = 0; i < size; i++) str[i] = another.str[i];
return *this;
}
A similar string implementation is discussed in this question, you may also reference the code there.
Demo
One problem is the initialization of the members in the default constructor:
str = '\0';
size = 1;
The character literal '\0 is equal to zero, which is equal to a null pointer. That assignment is equivalent to:
str = nullptr;
And yet you still say that the size of the string is 1, which is wrong since there is no string and the size should be zero.
Another (and the likely culprint of your problem) problem is that in your constructor MyString(const char* const s) you use strlen to get the length of a C-style null-terminated string for the allocation, but you forget that strlen doesn't count the null-terminator.
This means the allocation will be too small, and when you use strcpy to copy the string it will write the null-terminator out of bounds of your allocated memory.
Furthermore, you in other places count with the null-terminator, and actually set the size to include the null-terminator (even in the MyString(const char* const s) constructor). This goes against the normal C++ semantics of strings where the size doesn't include the terminator.
This could then lead to further problems, like in your print function where you actually print the null-terminator.
Here is a problem. When I try to convert it by using strncpy_s, array has some type of "trash data" from memory in the end of it. Even when I fill buffer with "\0". How to convert it clear?
typedef class Ryadok {
private:
int LengthOf = 0;
char text[20];
string* address;
public:
Ryadok(string strin) {
this->text[0] = '\0';
memset(text, '\0', sizeof(text));
strncpy_s(text, strin.c_str(), sizeof(text) - 1);
this->address = &strin;
for (int i = 0; i < sizeof(strin); i++) {
cout << this->text[i];
}
}
~Ryadok() {
}
}*cPtr;
int main()
{
Ryadok example("sdsdfsdf");
}
The idea to use c_str() function to convert the std::string to a a-string. Then we can simply call strcpy() function to copu the c-string into char array
std::string s = "Hello World!";
char cstr[s.size() + 1];
strcpy(cstr, s.c_str()); // or pass &s[0]
std::cout << cstr << '\n';
return 0;
When using the strncpy_s function you tell it to copy as many chars as will fit into your buffer "text". Since the string you create the "example" instance with is shorter, the copy function will keep going after the end of the actual string.
That is where your garbage comes from. Even worse you risk a Segmentation Fault this way. Your code might access parts of the RAM it is not allowed to read from. That will cause it to crash.
You are right though to copy the data pointed to by the return of c_str(). The pointer returned by c_str() points to data that belongs to the std::string object and might be changed or even invalidated by that object. (Read more here)
Here's a modified version of your code that should avoid the garbage:
typedef class Ryadok {
private:
int LengthOf = 0;
char text[20];
string* address;
public:
Ryadok(string strin) {
this->text[0] = '\0';
memset(text, '\0', sizeof(text));
if(strin.length()+1 <= sizeof(text)) {
strncpy_s(text, strin.c_str(), strin.length()+1);
} else {
//some error handling needed since our buffer is too small
}
this->address = &strin;
for (int i = 0; i < sizeof(strin); i++) {
cout << this->text[i];
}
}
~Ryadok() {
}
}*cPtr;
int main()
{
Ryadok example("sdsdfsdf");
}
I have been pulling my hair out on this particular issue and would like some advice. I have the following struct:
struct MqlStr // MQL String Array
{
int len;
char *string;
};
this is being passed to a function as a pointer from an external application as such:
MT4_EXPFUNC double __stdcall CheckExecutionRequests(MqlStr* RequestInfo)
within the function i am generating a number of string values that i need to assign to varies elements of the MqlStr array. the following works fine:
RequestInfo[1].string = "1";
RequestInfo[2].string = "2";
but when i use strcpy to get my generated string value into the array, it overwrites the entire array with the value i copied. for example:
string field1 = value.substr(Demark + 1, Demark2 - Demark - 1);
strncpy(RequestInfo[1].string, field1.c_str(), field1.size());
string field2 = value.substr(Demark + 1, Demark2 - Demark - 1);
strncpy(RequestInfo[2].string, field2.c_str(), field2.size());
if field1 = 1 and field2 = 2 then the entire RequestInfo[] array would be equal to 2 (the last value copied)
can someone point me in the right direction?
RequestInfo[1] = "1";
is not doing what you think. It's either
RequestInfo[1].string = "1";
if RequestInfo is a vector of MqlStr objects containing at least 2 elements, or
RequestInfo->string = "1";
if RequestInfo is a pointer to a single MqlStr object.
Have you allocated enough space for the .string pointers in your RequestInfo elements? strncpy is not allocating the space for you, use strdup for that.
You need to manage MqlStr memory in a safe manner, this can happen by using a standard container like std::string or by writing methods to allocate and deallocate the internal memory.
Here is an example of a simple class that manages its internal memory:
#include <cstdlib>
#include <iostream>
#include <string.h>
#include <sstream>
struct MqlStr
{
public:
int len;
char *string;
MqlStr() { init (); }
~MqlStr() { dealloc(); }
void assign(std::string& str) {
dealloc();
string = new char[str.length() + 1];
strncpy(string, str.c_str(), str.length());
string[str.length()] = 0;
len = str.length();
}
void dealloc() {
if(string != 0) delete [] string; init();
}
private:
void init() { string = 0; len = 0; }
MqlStr(const MqlStr &);
void operator= (const MqlStr &);
};
double CheckExecutionRequests(MqlStr* RequestInfo)
{
static int callCount = 0;
std::ostringstream stringstream; stringstream<<"callCount: "<<callCount++;
std::string field1 = stringstream.str();
RequestInfo->assign(field1);
return 1.0;
}
int main(int argc, char** argv)
{
MqlStr s[5];
std::cout<<"First call"<<std::endl;
for(unsigned i = 0; i < sizeof(s)/sizeof(s[0]); ++i)
CheckExecutionRequests(s + i);
for(unsigned i = 0; i < sizeof(s)/sizeof(s[0]); ++i)
std::cout<<"MqlStr["<<i<<"].string = "<<s[i].string<<std::endl;
std::cout<<"Second call"<<std::endl;
for(unsigned i = 0; i < sizeof(s)/sizeof(s[0]); ++i)
CheckExecutionRequests(s + i);
for(unsigned i = 0; i < sizeof(s)/sizeof(s[0]); ++i)
std::cout<<"MqlStr["<<i<<"].string = "<<s[i].string<<std::endl;
return EXIT_SUCCESS;
}
The second execution of CheckExecutionRequests with the same MqlStr instances will not corrupt the memory.
An extension to the code can be preallocation of the string size, and only reallocating the memory in the assign method if the new str.length > this.maxLength (preallocated length different from the string size).
The copy constructor and assignment operator are currently disabled, because they can cause problems if not implemented properly while managing internal memory on the heap.
A simpler solution would be to write your struct using a standard container as follows:
struct MqlStr
{
public:
std::string string;
}
And then just assign the string you get for the fields to MqlStr string.
I have this function in my program
const char* Graph::toChar() {
std::string str;
const char* toret;
str = "";
for (vector<Edge*>::iterator it = pile.begin(); it != pile.end(); it++) {
str += (*it)->toString();
}
toret = str.c_str();
return toret;
}
Then I'm debugging the function everything works properly until I'm in return toret; line. I press step over and the debugger is going to std::string str; line and all string and charater variables become "", so the final return of the function is "" (nothing).
What am I doing wrong?
*(it)->toString(); is working properly and when the debugger executes *toret = str.c_str();* the value in toret is correct.
Thx
What you're doing here is bad: You are returning the c_str of an std::string that's about to be deleted when it goes out of scope. regardless of debug mode or not, this means unpredictable behavior. actually rather predictable - your program will crash :)
You should either return const std::string, accept std:string & as an argument and build it, or use strdup() to copy the string's c_str to something that will stay in memory. Keep in mind that using strdup() means you'll have to delete it somewhere later.
Here are two forms of the function that will work:
const std::string Graph::toChar() {
std::string str;
for (vector<Edge*>::iterator it = pile.begin(); it != pile.end(); it++) {
str += (*it)->toString();
}
return str;
}
or
void Graph::toChar(std::string &out) {
out = ""
for (vector<Edge*>::iterator it = pile.begin(); it != pile.end(); it++) {
out += (*it)->toString();
}
}