为什么字符串在realloc之后在内存中变得无效?

时间:2016-06-05 16:08:13

标签: c++

我编写了类myString,它覆盖了一些运算符。如果它在vs2013(Microsoft Virtual Studio 2013)中运行,结果是错误的。但如果它在devcpp中运行,结果是正确的。 为什么呢?

顺便说一句,我有调试,发现问题是realloc。 在vs2013中重新分配后,内存中存在的数据变为无效。

我的所有代码:

#include <iostream>
#include<string>
#include <stdlib.h>
#include <string.h>
using namespace std;


class myString{
public:
    myString() :_str(NULL), length(0){};
    myString(char* str);

    friend myString& operator+(myString& str,char* s);
    friend myString& operator+(myString& str, myString& s);
    friend myString& operator+(myString& str, int num);
    friend ostream& operator<<(ostream& out,myString& str);
    void operator=(char* s);
    void operator=(myString& s);
private:
    char* _str;
    int length;
};

myString::myString(char* str){
    if (str == NULL){
        _str = NULL;
        length = 0;
        return;
    }

    length = strlen(str);
    _str = (char*)malloc(sizeof(char)*length);
    strcpy(_str, str);
}

myString& operator+(myString& str, char* s){
    if (s == NULL)
        return str;

    char* temp = (char*)realloc((void*)str._str, sizeof(char)*strlen(s));
    if (temp == NULL){
        cout << "error" << endl;
        return str;
    }

    str.length += strlen(s);
    strcat(temp, s);
    str._str = temp;
    return str;
}

myString& operator+(myString& str, myString& s){
    if (s.length == 0 || s._str == NULL)
        return str;

    char* temp = (char*)realloc((void*)str._str, sizeof(char)*strlen(s._str));
    if (temp == NULL){
        cout << "error" << endl;
        return str;
    }

    str.length += strlen(s._str);
    strcat(temp, s._str);
    str._str = temp;
    return str;
}

myString& operator+(myString& str, int num){
    char* s = (char*)malloc(sizeof(char) * 100);
    itoa(num, s, 10);


    /******problem! in vs2013, after realloc str._str become invalid.
          But in devcpp everything is right. Why???******/
    char* temp = (char*)realloc((void*)str._str, sizeof(char)*strlen(s));
    if (temp == NULL){
        cout << "error" << endl;
        return str;
    }

    str.length += strlen(s);
    strcat(temp, s);
    str._str = temp;
    return str;
}

void myString::operator=(char* s){
    if (s == NULL)
        return;

    this->length += strlen(s);
    this->_str = (char*)malloc(sizeof(char)*strlen(s));
    strcpy(this->_str, s);
}

void myString::operator=(myString& s){
    this->length = s.length;
    this->_str = s._str;

}

ostream& operator<<(ostream& out, myString& str){
    out << "string is:" <<str._str<<", length is:"<<str.length;
    return out;
}

int main(){
    myString str;
    str = "hello world";
    cout << str << endl;
    str = str+ 123;
    cout << str << endl;
    system("pause");
    return 0;
}

2 个答案:

答案 0 :(得分:1)

您缺少myString输入和析构函数的复制构造函数。而char*输入的复制构造函数不会复制char数据。

您的operator+运营商实施错误。 operator+不应该修改输入参数,但是你是。您需要返回参数数据的新合并副本,而您没有这样做。

您的operator=运营商也被错误地执行了。 char*运算符忽略NULL输入,使目标字符串不被修改而不是清除它。同一个运算符正在递增length而不是重新分配它。 myString运算符取得输入数据的所有权而不是复制它,留下两个myString个对象指向同一个物理内存块。两个操作员都有内存泄漏,他们在用新数据替换之前不会释放现有数据。他们都需要返回对已修改的myString的引用。

您的运营商应该使用copy-and-swap idiom来确保安全。

尝试更像这样的东西:

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <algorithm>

class myString
{
public:
  myString();
  myString(const myString &src);
  myString(const char* src);
  myString(const int src);
  ~myString();

  myString& operator=(const myString& rhs);
  myString& operator+=(const myString& rhs);

  friend myString operator+(const myString& lhs, const myString& rhs);
  friend std::ostream& operator<<(std::ostream& out, const myString& str);

private:
  char* _str;
  int length;
};

myString::myString()
  : _str(NULL), length(0)
{
}

myString::myString(const myString& src)
  : _str(NULL), length(0)
{
  if ((src._str != NULL) && (src.length > 0))
  {
    length = src.length;
    _str = new char[length + 1];
    std::copy(src._str, src._str + length, _str);
    _str[length] = 0;
  }
}

myString::myString(const char* src)
  : _str(NULL), length(0)
{
  if ((src != NULL) && (*src != 0))
  {
    length = std::strlen(src);
    _str = new char[length + 1];
    std::copy(src, src + length, _str);
    _str[length] = 0;
  }
}

myString::myString(const int src)
  : _str(NULL), length(0)
{
  char temp[16] = {0};
  std::itoa(src, temp, 10);
  length = std::strlen(temp);
  _str = new char[length + 1];
  std::copy(temp, &temp[length], _str);
  _str[length] = 0;
}

myString::~myString()
{
  delete[] _str;
}

myString& myString::operator=(const myString& rhs)
{
  myString temp(rhs);
  std::swap(_str, temp._str);
  std::swap(length, temp.length);
  return *this;
}

myString& myString::operator+=(const myString& rhs)
{
  myString temp;
  temp.length = length + rhs.length;
  temp._str = new char[temp.length + 1];
  std::copy(_str, _str + length, temp._str);
  std::copy(rhs._str, rhs._str + rhs.length, temp._str + length);
  temp._str[temp.length] = 0;
  std::swap(_str, temp._str);
  std::swap(length, temp.length);
  return *this;
}

myString operator+(const myString& lhs, const myString& rhs)
{
  myString temp;
  temp.length = lhs.length + rhs.length;
  temp._str = new char[temp.length + 1];
  std::copy(lhs._str, lhs._str + lhs.length, temp._str);
  std::copy(rhs._str, rhs._str + rhs.length, temp._str + lhs.length);
  temp._str[temp.length] = 0;
  return temp;
}

std::ostream& operator<<(std::ostream& out, const myString& str)
{
  out << "string is: " << str._str << ", length is: " << str.length;
  return out;
}

int main()
{
  myString str;
  str = "hello world";
  std::cout << str << std::endl;
  str = str + 123;
  std::cout << str << std::endl;
  std::system("pause");
  return 0;
}

答案 1 :(得分:0)

据我了解realloc的操作,它可能会将内存移动到另一个地方 - 特别是如果它需要更多空间,它可能会使旧内存位置无效(解除分配)。

在此之后,记忆不再是“你的”。并且可能无法预测地改变。所以在我看来,VS2013的行为是在realloc的定义范围内 - 它只保证返回的内存是有效的(即你的temp),而不是旧的内存地址将保持有效。

tldr:realloc可以破坏旧的内存位置;它在某些平台上工作的事实;是实施的结果,在许多记忆条件下都不可靠。