【问题标题】:What does "Unhandled exception thrown: read access violation. _First was nullptr" error mean?“抛出未处理的异常:读取访问冲突。_First was nullptr”错误是什么意思?
【发布时间】:2016-11-11 17:29:40
【问题描述】:

我编译了我的代码,但它抛出了“抛出异常:读取访问冲突。 _First 是 nullptr。”

我真的不知道这意味着什么,因为我还是 C++ 的初学者。我真的需要你的帮助来解决这个问题,因为我已经为这段代码苦苦挣扎了好几天,这太令人沮丧了......

提前谢谢你。

#include <iostream>
#include <cstring>
#include <fstream>
using namespace std;

class MyString {
public:

    //default constructor
    MyString();

    MyString(char* chars);

    //copy constructor
    MyString(const MyString &);

    int length() const;

    //destructor
    ~MyString();

    //operator overloads
    char& operator[](int index);
    friend MyString operator+(const MyString& newWord, const MyString& newWord2);
    MyString& operator+=(const MyString& newWord);
    friend ostream& operator<<(ostream& newWord, const MyString& newWord2);
    friend istream& operator >> (istream& newWord, MyString& newWord2);
    friend bool operator==(const MyString& newWord, const MyString& newWord2);
    friend bool operator!=(const MyString& newWord, const MyString& newWord2);
    friend bool operator<(const MyString& newWord, const MyString& newWord2);
    friend bool operator<=(const MyString& newWord, const MyString& newWord2);
    friend bool operator>(const MyString& newWord, const MyString& newWord2);
    friend bool operator>=(const MyString& newWord, const MyString& newWord2);

private:
    char* value;
    int size;
};

//default constructor
MyString::MyString() {

    value = NULL;
    size = 0;
}

//copy constructor
MyString::MyString(const MyString& newWord) {

    //perform a deep copy to copy each of the value to a new memory
    size = newWord.size;
    char* newCopy = new char[size];

    for (int ii = 0; ii < size; ii++) {
        newCopy[ii] = newWord.value[ii];
    }
}

//constructor with an argument
MyString::MyString(char* chars) {

    //give the value and the size
    value = chars;
    size = strlen(chars);
}

//find length
int MyString::length() const {

    return size;
}

//find the value of each index
char& MyString::operator[](int index) {

    return value[index];
}

//operator + (concatenate)
MyString operator+(const MyString& newWord, const MyString& newWord2) {

    MyString concatenated;
    concatenated = strcat(newWord.value, newWord.value);
    return concatenated;

}

//operator += (append)
MyString& MyString::operator+=(const MyString& newWord) {

    char * newMemory = value;
    value = new char[strlen(value) + newWord.length() + 1];
    strcpy(value, newMemory);
    strcat(value, newWord.value);
    if (size != 0)
    {
        delete[] newMemory;
    }
    size = strlen(value);
    return *this;
}

//ostream operator
ostream& operator<<(ostream& newWord, const MyString& newWord2) {

    newWord << newWord2.value;
    return newWord;
}


//istream operator
istream& operator >> (istream& newWord, MyString& newWord2) {

    const int MAX = 100;
    char* ptr = new char[MAX];
    newWord >> ptr;
    newWord2 = MyString(ptr);
    delete ptr;
    return newWord;
}

//all boolean operators
bool operator==(const MyString& newWord, const MyString& newWord2) {
    if (newWord.value == newWord2.value) {
        return true;
    }
    else {
        return false;
    }
}

bool operator!=(const MyString& newWord, const MyString& newWord2) {
    if (newWord.value != newWord2.value) {
        return true;
    }
    else {
        return false;
    }
}

bool operator<(const MyString& newWord, const MyString& newWord2) {
    if (newWord.value < newWord2.value) {
        return true;
    }
    else {
        return false;
    }
}

bool operator<=(const MyString& newWord, const MyString& newWord2) {
    if (newWord.value <= newWord2.value) {
        return true;
    }
    else {
        return false;
    }
}

bool operator>(const MyString& newWord, const MyString& newWord2) {
    if (newWord.value > newWord2.value) {
        return true;
    }
    else {
        return false;
    }
}

bool operator>=(const MyString& newWord, const MyString& newWord2) {
    if (newWord.value >= newWord2.value) {
        return true;
    }
    else {
        return false;
    }
}

//destructor to release memory
MyString::~MyString() {
    delete[] value;
}

void test_copy_and_destructor(MyString S) {
    cout << "test: copy constructor and destructor calls: " << endl;
    MyString temp = S;
    cout << "temp inside function test_copy_and_destructor: " << temp << endl;
}

int main() {

    MyString st1("abc abc");
    MyString st2("9fgth");

    cout << "Copy constructor , << operator" << endl;

    MyString  st3(st1);

    cout << "st3: " << st3 << endl;

    test_copy_and_destructor(st2);

    MyString  st4;

    cout << "operator + " << endl;

    st4 = st3 + st2;

    cout << "st4: " << st4 << endl;

    cout << "st1 + st2: " << (st1 + st2) << endl;

    cout << "operators  [ ] " << endl;

    for (int i = 0; i < st2.length(); i++)
        cout << st2[i] << " ";

    cout << endl;

    cout << "operators  += , ==, != " << endl;

    st2 += st1;

    if (st3 == st1)
        cout << "st3 and st1 are identical " << endl;
    else cout << "st3 and st1 are not identical " << endl;

    if (st2 != st1)
        cout << "st2 and st1 are not identical " << endl;
    else cout << "st2 and st1 are identical " << endl;

    cout << "operators  < , <=, >, >= " << endl;

    if (st2 < st1)
        cout << "st2 < st1 " << endl;
    else cout << "st2 is not less than st1 " << endl;

    if (st1 <= st2)
        cout << "st1 <= st2 " << endl;
    else cout << "st1 is not less than or equal to st2 " << endl;

    if (st1 > st2)
        cout << "st1 > st2 " << endl;
    else cout << "not (st1 >  st2) " << endl;

    if (st1 >= st2)
        cout << "st1 >= st2 " << endl;
    else cout << "not (st1 >=  st2) " << endl;

    cout << "operator >> " << endl;

    //Open the data file
    ifstream input("A9_input.txt");
    if (input.fail()) {
        cout << "unable to open input file A9_input.txt, Exiting..... ";
        system("pause");
        return 0;
    }
    MyString temp1;
    MyString temp2("aaa");
    input >> temp1;
    input >> temp2;
    cout << "first element of input file: " << temp1 << endl;
    cout << "second element of input file: " << temp2 << endl;
    input.close();

    cout << "MyString says farewell....." << endl;
    system("pause");
    return 0;
}

【问题讨论】:

  • 那么在错误出现之前你走了多远?
  • 你正试图用value指向的字符串做一些事情,但是valueNULL
  • 你在跑之前尝试过走路——这段代码一团糟。没有空检查,operator+ 有很多错误 - 它甚至不应该编译,因为你使用 const MyString 作为 strcat 的第一个参数,更不用说strcat 要求目标缓冲区足够大以接收第二个附加文本。在operator+= 中,您使用newMemory 来存储旧地址...您的operator&gt;&gt; 很奇怪,您的比较运算符比较指针,并且您的析构函数假定您拥有指针,如果使用MyString(char* s) 构造,情况并非如此。

标签: c++ c++11 pointers operator-overloading dynamic-memory-allocation


【解决方案1】:

您的复制构造函数从不设置目标的value,因此当您尝试使用新字符串时,value 未初始化。您应该分配给value,而不是使用局部变量newCopy

MyString::MyString(const MyString& newWord) {

    //perform a deep copy to copy each of the value to a new memory
    size = newWord.size;
    value = new char[size];

    for (int ii = 0; ii < size; ii++) {
        value[ii] = newWord.value[ii];
    }
}

此外,采用char* chars 的构造函数必须复制chars。否则,如果参数是超出范围的本地数组或已删除的动态数组,则指针可能会变得无效。此外,由于析构函数执行delete[] value;,它要求它是动态分配的,当您从字符串文字初始化它时这是不正确的。

//constructor with an argument
MyString::MyString(char* chars) {

    size = strlen(chars);
    value = new char[size];
    for (int i = 0; i < size; i++) {
        value[i] = chars[i];
    }
}

【讨论】:

  • 当我尝试实现您建议的代码时,我仍然收到此错误:ConsoleApplication5.exe 中 0x0F5E0E09 (ucrtbased.dll) 引发的异常:0xC0000005:访问冲突写入位置 0x005D2000。你对这个有什么想法吗?抱歉,我刚刚使用 C++ 两个月。感谢您的大力帮助!
  • 对不起,你有很多代码,我看不到你可能遇到的所有问题。你需要学习如何使用调试器,这样你就可以在程序的每一步查看变量,并查看它们何时变得无效。
【解决方案2】:

有一系列问题需要解决:

首先,您允许 value 为 0/NULL/nullptr(最好使用新的 c++11 nullptr 关键字!)。如果这样做,则需要在每个函数和运算符中考虑这种可能性,否则您将尝试访问无效的内存位置(例如,在执行strlen(value) 时)。这正是异常的含义(在大多数情况下,0 不是有效的内存地址,嵌入式系统存在一些异常 - 但是,如果写入它,您可能会破坏整个系统!):您遇到了这种情况。

为避免在每个函数/运算符中出现这种麻烦,您可能更愿意始终在构造函数中将value 设置为现有的空字符串区域,就像 std::string 所做的那样(例外:并非 std::string 的每个实现确实接受std::string(nullptr))。您也避免了用户的另一个问题:您使他能够获得大小为 0 的字符串的两种不同的字符串表示形式:nullptr""

另一个问题是,如果构造函数接受char*,您复制字符串,而是在析构函数中删除它。问题:参数可能是分配在堆栈上的字符缓冲区:

char buffer[64];
/* ... */
return MyString(buffer);

在许多情况下,拥有现有字符串的所有权可能是有效的(std::string 不可能做到这一点——在任何情况下都是安全的,在某些中效率低下),但你可能会遇到深层次的问题在其他情况下(如上所述,或者如果分配字符串文字 return MyString("hello world"); (好吧,这无论如何都是有问题的,因为文字在 C++ 中是 char const* - 但许多编译器让你侥幸逃脱,只是发出警告)。如果你想要获得所有权,我默认不会这样做(复制字符串),并添加另一个明确允许它的构造函数(MyString(char*, bool takeOwnership = false),以及(MyString(char const*) 总是复制 - 注意const)。当然,如果您拥有所有权,则需要记住某个地方。仅删除内部表示,如果您拥有所有权(默认...)。

您正在通过 ==、!=、、... 运算符比较 char* 值。请注意,只有当两个 value 成员都指向完全相同的字符串时,您才会得到相等,但如果两个不同的字符串字面上相等,则不会:

char b1[] = {'a', 'b', 'c', 0 };
char b2[] = {'a', 'b', 'c', 0 };
bool isEqual = MyString(b1) == MyString(b2);

isEqual 将是false!我假设您不是故意的,而是与 std::string 提供的相同行为。更严重的问题是运算符 、>=。仅当两个指针都指向同一个数组或指向末尾的一个数组 (b3 = b1 + 2) 时,与它们进行比较是合法的,否则,这是未定义的行为。

要获得我认为您想要的行为,您需要使用strcmp

bool operator X(MyString const& l, MyString const& r)
{
    return strcmp(l, r) X 0;
}

将 X 替换为适当的运算符(请注意,您可以将 != 定义为 !(l == r)&lt;=!(l &gt; r) 等)。

请,请,请:不要做这样的事情:

if(a == b)
    return true;
else
    return false;

bool isWhatEver;
if(c != d)
    isWhatEver = true;
else
    isWhatEver = false;

简单写

return a == b;

isWhatEver = c != d;

附录:

您违反了rule of three,您没有提供赋值运算符:operator=(MyString const&amp; other)。三法则实际上变成了 C++11 中的rule of five;您可能还想添加一个移动构造函数和一个移动赋值运算符。在实施之前,请查看here,这可能对您非常有用...


编辑:

仅从地址几乎不可能知道出了什么问题。您没有提供任何堆栈跟踪,也没有提供您的固定代码...

所以我允许自己按照我认为你可能想要的方式来调整你的课程。这个版本运行了你所有的测试,至少,输入文件的内容只是“hello world”。省略了移动构造函数(提示:为此使用 std::swap)。我仍然不保证它是没有错误的......一些建议:不要只拿我的代码,仔细看看我做了什么不同的事情,看看可能出了什么问题。最后一件事:由于使用 std::vector 超出了缓冲区大小,我使 operator>> 不会失败。

class MyString
{
public:

    //default constructor
    MyString();

    MyString(char const* chars);

    //copy constructor
    MyString(const MyString &);

    int length() const;

    //destructor
    ~MyString();

    //operator overloads
    char& operator[](int index);
    friend MyString operator+(const MyString& newWord, const MyString& newWord2);
    MyString& operator+=(const MyString& newWord);
    MyString& operator=(const MyString& newWord)
    {
        char* newValue = new char[newWord.size];
        delete value;
        value = newValue;
        size = newWord.size;
        memcpy(value, newWord.value, size);
        return *this;
    }
    MyString& operator=(char const* chars)
    {
        size_t newSize = strlen(chars);
        char* newValue = new char[newSize];
        delete value;
        value = newValue;
        size = newSize;
        memcpy(value, chars, size);
        return *this;
    }
    friend ostream& operator<<(ostream& newWord, const MyString& newWord2);
    friend istream& operator >>(istream& newWord, MyString& newWord2);
    friend bool operator==(const MyString& newWord, const MyString& newWord2);
    friend bool operator!=(const MyString& newWord, const MyString& newWord2);
    friend bool operator<(const MyString& newWord, const MyString& newWord2);
    friend bool operator<=(const MyString& newWord, const MyString& newWord2);
    friend bool operator>(const MyString& newWord, const MyString& newWord2);
    friend bool operator>=(const MyString& newWord, const MyString& newWord2);

private:
    char* value;
    int size;
};

//default constructor
MyString::MyString()
{
    value = new char[1];
    *value = 0;
    size = 0;
}

//copy constructor
MyString::MyString(const MyString& newWord)
{
    //perform a deep copy to copy each of the value to a new memory
    size = newWord.size;
    value = new char[size];
    memcpy(value, newWord.value, size);
}

//constructor with an argument
MyString::MyString(char const* chars)
{
    if(chars)
    {
        size = strlen(chars);
        value = new char[size];
        memcpy(value, chars, size);
    }
    else
    {
        value = new char[1];
        *value = 0;
        size = 0;
    }
}

//find length
int MyString::length() const
{
    return size;
}

//find the value of each index
char& MyString::operator[](int index)
{
    return value[index];
}

//operator + (concatenate)
MyString operator+(const MyString& newWord, const MyString& newWord2)
{
    MyString concatenated;
    concatenated.value = new char[newWord.size + newWord2.size + 1];
    memcpy(concatenated.value, newWord.value, newWord.size);
    memcpy(concatenated.value + newWord.size, newWord2.value, newWord2.size + 1);
    return concatenated;
}

//operator += (append)
MyString& MyString::operator+=(const MyString& newWord)
{
    if(newWord.size > 0)
    {
        char * newMemory = value;
        value = new char[size + newWord.size + 1];
        memcpy(value, newMemory, size);
        memcpy(value + size, newWord.value, newWord.size);
        delete[] newMemory;
        size += newWord.size;
    }
    return *this;
}

//ostream operator
ostream& operator<<(ostream& newWord, const MyString& newWord2)
{

    newWord << newWord2.value;
    return newWord;
}

//istream operator
istream& operator >>(istream& newWord, MyString& newWord2)
{
    std::vector<char> v;
    for(;;)
    {
        char c = newWord.get();
        if(newWord.eof() || isspace(c))
        {
            break;
        }
        v.push_back(c);
    }
    if(!v.empty())
    {
        newWord2 = v.data();
    }
    return newWord;
}

//all boolean operators
bool operator==(const MyString& newWord, const MyString& newWord2)
{
    return strcmp(newWord.value, newWord2.value) == 0;
}

bool operator!=(const MyString& newWord, const MyString& newWord2)
{
    return !(newWord == newWord2);
}

bool operator<(const MyString& newWord, const MyString& newWord2)
{
    return strcmp(newWord.value, newWord2.value) < 0;
}
bool operator>(const MyString& newWord, const MyString& newWord2)
{
    return strcmp(newWord.value, newWord2.value) < 0;
}

bool operator<=(const MyString& newWord, const MyString& newWord2)
{
    return !(newWord > newWord2);
}

bool operator>=(const MyString& newWord, const MyString& newWord2)
{
    return !(newWord < newWord2);
}

//destructor to release memory
MyString::~MyString()
{
    delete[] value;
}

【讨论】:

  • 当我尝试实现您建议的代码时仍然出现此错误:ConsoleApplication5.exe 中的 0x0F5E0E09 (ucrtbased.dll) 抛出异常:0xC0000005:访问冲突写入位置 0x005D2000。你对这个有什么想法吗?抱歉,我刚刚使用 C++ 两个月。感谢您的大力帮助!
  • 信息太少,无法猜测实际错误可能是什么。添加了一些可能对您有用的代码。
猜你喜欢
  • 1970-01-01
  • 2020-09-12
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-16
  • 1970-01-01
相关资源
最近更新 更多