为啥我的动态分配的数组没有保存正确的值?

Posted

技术标签:

【中文标题】为啥我的动态分配的数组没有保存正确的值?【英文标题】:Why isn't my dynamically allocated array holding the correct values?为什么我的动态分配的数组没有保存正确的值? 【发布时间】:2017-04-29 04:49:32 【问题描述】:

我们正在讨论我学校的课程主题。我应该创建一个名为 Student 的类,我们可以使用它在动态分配的数组中保存不同的测试分数。我正在努力弄清楚为什么当我打印出这个类的特定实例的数组时,它给了我完全不正确的值。我知道我的类的 setter 函数正在设置正确的值,但是每当我将它们打印出来时,它们都是完全错误的。下面是我的代码。 main() 中被注释掉的部分只是用来测试我的 makeArray() 函数的逻辑。

#include <iostream>

using namespace std;


class Student 

private:

    string name;
    int id;
    int* testptr;
    int num;

    void makeArray() 
        int array[num];
        testptr = &array[num];

        for (int i = 0; i < num; i++) 
            array[i] = 0;
        
    

public:

    Student() 
        setName("None");
        setID(10);
        num = 3;
        makeArray();
    

    Student(int n) 
        setName("None");
        setID(10);
        if (n > 0)
            num = n;
        else
            num = 3;
        makeArray();
    

    Student(string nm, int i, int n) 
        setName(nm);
        setID(i);
        if (n > 0)
            num = n;
        else
            num = 3;
        makeArray();
    

    void setName(string nm) 
        name = nm;
    

    void setID(int i) 
        if (i >= 10 && i <= 99)
            id = i;
        
        else 
            cout << "Error: Cannot set id to " << i << " for " << getName();
            id = 10;
        
    

    void setScore(int i, int s) 
        if ((i >= 0 && i <= num) && (s >= 0 && s <= 100)) 
            //Here is where I set the values for the array. They come out correct in the console.
            testptr[i] = s;
            cout << testptr[i] << endl;
         else 
            cout << "The test " << i << " cannot be set to " << s << " for " << getName() << endl;
        
    

    string getName() const 
        return name;
    

    int getID() const 
        return id;
    

    void showScore() 
        //This is where I print out the values of the array. They come out incorrect here.
        for (int i = 0; i < num; i++) 
            cout << "Test " << i << " had a score of " << testptr[i] << endl;
        
    

    void display() 
        cout << "Calling the display function" << endl;
        cout << "The Name: " << getName() << endl;
        cout << "The ID: " << getID() << endl;
        showScore();
    

    ~Student() 

    

;

int main() 
    Student c("Joe", 40, 5);

    c.setScore(0, 90);
    c.setScore(1, 91);
    c.setScore(2, 92);
    c.setScore(3, 93);
    c.setScore(4, 94);


    c.display();

    /**int* testpointer;
    int num = 3;
    int array[num];
    testpointer = &array[0];
    for (int i = 0; i < num; i++) 
        testpointer[i] = i;
        cout << testpointer[i] << endl;
    **/



【问题讨论】:

你没有动态分配你的数组。它超出了范围并导致了可怕的 UB。 使用一些标准的container,可能是std::vector 【参考方案1】:

问题(实际上是问题集)在这个函数中

void makeArray() 
    int array[num];
    testptr = &array[num];

    for (int i = 0; i < num; i++) 
        array[i] = 0;
    

第一条语句int array[num],因为num 不是编译时常量,所以不是有效的C++。如果您的编译器支持它,它是编译器特定的扩展。 (从技术上讲,这是一个可变长度数组,它是 1999 年 C 标准中的 C 中的一项功能,自 2011 年起在 C 中是可选的,但在任何标准或草案中从未成为 C++ 的一部分。

其次,如果您的编译器确实支持该构造,它会创建一个自动存储持续时间的数组,当函数返回时该数组将不复存在。这意味着testptr 包含一个悬空指针。函数返回后,任何对 testptr 的取消引用(例如访问数组元素)都会产生未定义的行为。

如果您确实必须使用动态内存分配,请使用“新表达式”,例如testptr = new int[num]。请记住,然后有必要在您的类析构函数中执行delete [] testptr。还要查找“三规则”,因为在创建类的其他实例时,您需要编写复制构造函数和赋值运算符来正确管理数组。

更好的是#include &lt;vector&gt;,并将testptr 更改为std::vector&lt;int&gt; 类型。阅读标准矢量类的文档,了解如何使用它,包括管理它的大小。

另外,#include &lt;string&gt; 也是如此。没有它,您的代码不需要使用 std::string 类型进行编译 - 您的实现很幸运,&lt;iostream&gt; 包含 &lt;string&gt;。这不是 C++ 标准所要求的,实际上,某些编译器会发生这种情况,而其他编译器则不会。

您的代码中还有其他问题,但以上内容应该足以让您入门。

【讨论】:

【参考方案2】:

为了让类中的函数共享数据,您需要在类范围内声明变量。

class Foo

public:
  MyFunc1()  _n = 1; 
  MyFunc2()  _n = 2; 
private:
  int32_t n; // can be used by all member functions
;

在您的情况下,您在函数范围内声明了一个数组,一旦您离开该函数,该数组就会被销毁。

另外你不能在栈上声明一个可变大小的数组,而是你需要在堆上分配数组

class Foo

public:
  Foo():_array(nullptr),_size(0) 
  void AllocateArray(int size)  _array = new int32_t[size]; _size = size; 
  void DestroyArray()  delete [] _array; _size = 0; 
private:
  int32_t* _array;
  int8_t _size;
;

通常您会为此使用 C++ 容器之一(例如 std::vector),而不是使用原始指针。

【讨论】:

【参考方案3】:

您的代码中有多个问题,但让我们先从问题开始。 问题在于,在当前的实现中,您没有使用动态数组,而只是使用指向内存中一些随机数据的指针。动态分配的对象是使用new 运算符创建的。在您的情况下,您在堆栈上分配对象:

void makeArray() 
    int array[num];         // creates local object on stack
    testptr = &array[num];  // stores pointer to local object

    for (int i = 0; i < num; i++) 
        array[i] = 0;
    
 // stack is being destroyed here along with array object
  // testptr points to memory that should no be addressed anymore

在 C++ 中返回/存储指针或对声明对象范围之外的本地对象的引用是一个很常见的错误。初始化指针的正确方法是:

void makeArray() 
    // note parenthesis at the end,
    // default zero-initializer will be called 
    testptr = new int[num]();

注意,在这种情况下,您必须记住在不需要使用时释放分配的内存。实现这一点的最佳方法是将删除调用放入析构函数中:

   ~Student() 
       delete[] array;
   

然后当Student对象超出范围时,内存将被释放。

但我强烈建议您熟悉“智能指针”的概念,它是专门为减少手动内存管理工作而设计的。在现代 C++ 中,“原始”指针被认为是非常糟糕的做法,因为它可能导致许多错误。 更好的方法是根本不使用指针,而是使用标准 C++ 容器,例如 std::vector&lt;int&gt;

【讨论】:

以上是关于为啥我的动态分配的数组没有保存正确的值?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C 中找到动态分配数组的大小?

如何正确分配动态一维数组作为类的成员?

动态分配的二维数组

为啥必须动态分配扩展数组才能使此函数正常工作 C++

为啥在 C 中为数组声明和指向数组声明的指针动态分配的内存不同? [复制]

为啥二维动态数组有初始值?