【问题标题】:Object created in for-loop has same address在 for 循环中创建的对象具有相同的地址
【发布时间】:2018-06-30 05:08:58
【问题描述】:

我正在尝试创建一个自己的列表类,它具有固定大小并且可以存储整数。仅用于学习目的。

这就是我的做法: 我有一个名为 Item 的结构,它保存数据(要存储的整数)和指向列表中下一项的指针。 当列表初始化时,我首先将 x 数量的空项添加到列表中。

迭代(n-1)中声明的item的地址存储在缓冲区中,这样迭代n中的item的地址,可以设置为上一次迭代(n-1)中的item的下一个。 这将所有项目相互链接。

第一个项目的地址也被保存下来,作为后面代码中整个列表的访问点。

但这并没有真正起作用;这是我的代码:

#include <iostream>

class List {
    public:
    //Item-object needed to store
    //elements
    struct Item {
        int data = 0;
        Item* next = nullptr;
    };

    //Constructor
    //creates a list of fixed size
    List(int size) {
        //Filling up with empty items
        for (int i = 0; i < size; i++) {
            Item item;

            //Storing iterator in item (just for testing purposes)
            item.data = i;

            //If first item,
            //store start-address
            //set buffer to start address
            if (i == 0) 
                this->start = &item;

            //Setting current item as nextptr
            //for previous item in buffer 
            if (i > 0)
                this->buffer->next = &item;

            //Storing current address in buffer
            this->buffer = &item;

            //Outputting address and value (just for testing purposes)
            //std::cout << "Address: " << &item << " -> " << item.data << std::endl;
        }
    }

    Item* getFirstItemAddress() {
        return this->start;
    }

    private:
    //Holding address of first item
    Item* start = nullptr;

    //Buffer holding temporary address
    Item* buffer = nullptr;

};

int main() {
    List list(5);

    //Printing out
    List::Item* current = list.getFirstItemAddress();
    while (current->next) {
        std::cout << current->data << std::endl;
        current = current->next;
    }

    return 0;
}

这是输出:

Testing output:
1168769696
-1064971727
Segmentation fault

但是,当我取消注释 testing-line 37 时,输出如下:

Address: 0x7ffe54015cf0 -> 0
Address: 0x7ffe54015cf0 -> 1
Address: 0x7ffe54015cf0 -> 2
Address: 0x7ffe54015cf0 -> 3
Address: 0x7ffe54015cf0 -> 4

Testing output:
1648675776
1648572376
1646105840
1226279756
Segmentation fault

我一开始不明白输出如何如此剧烈地改变“测试输出”的输出......

无论如何,分段错误信息:

(gdb) run
Starting program: /home/niel/Desktop/listTest/main 
Address: 0x7fffffffe0a0 -> 0
Address: 0x7fffffffe0a0 -> 1
Address: 0x7fffffffe0a0 -> 2
Address: 0x7fffffffe0a0 -> 3
Address: 0x7fffffffe0a0 -> 4

Testing output:
-136467520
-136570920
-139037456
1226279756

Program received signal SIGSEGV, Segmentation fault.
0x000000000040092e in main () at main.cpp:62
62              std::cout << current->data << std::endl;

不知道为什么 current->data 会出现分段错误,因为我给每个元素都提供了数据!

但最困扰我的是:

 Address: 0x7fffffffe0a0 -> 0
    Address: 0x7fffffffe0a0 -> 1
    Address: 0x7fffffffe0a0 -> 2
    Address: 0x7fffffffe0a0 -> 3
    Address: 0x7fffffffe0a0 -> 4

每个项目都有相同的地址,当然,这不是我的意思。在迭代中创建的每个项目都应该是具有另一个地址的另一个项目。 这是否与 Item 被声明为本地有关,并且该对象因此存储在堆栈中而不是在可用空间中?我尝试使用 'new' 关键字,但没有奏效!

TL;DR 1)为什么我得到分段错误 2) 如何解决 List() 中创建的每个 Item 都有一个单独的地址?

【问题讨论】:

  • 使用new为Item分配内存。为什么你会期望一个本地存储分配变量在循环的每次迭代中都有不同的地址?
  • 存储&amp;item 以供以后使用是没有意义的,因为item 的生命周期以创建它的迭代结束。之后取消引用它是未定义的。

标签: c++ pointers memory segmentation-fault


【解决方案1】:

问题

在列表构造函数中,您将对象item 的地址放入列表中,该对象位于循环本地,并在每次迭代结束时被销毁。

解决方案

您必须在免费存储中创建一个new 对象,这样它才能在迭代和构造函数结束时继续存在。在您学习的过程中,我保持简单并使用原始指针:

List(int size) {
    for (int i = 0; i < size; i++) {
        Item *pitem = new Item;  
        pitem->data = i;  

        if (i == 0) 
            start = pitem;
        if (i > 0)
            buffer->next = pitem;

        //Storing current address in buffer
        buffer = pitem;

        //Outputting address and value (just for testing purposes)
        //std::cout << "Address: " << pitem << " -> " << item->data << std::endl;
    }
}

但是你的代码会泄漏内存。如果你有这样的构造函数,你还需要一个复制构造函数、一个赋值运算符和一个析构函数(rule of 3)

【讨论】:

  • 我已经想到了类似的东西并尝试使用“新”,但出现错误。谢谢你,我知道100%理解它!谢谢!
【解决方案2】:

Item item; 是一个堆栈分配变量,这意味着当程序退出当前范围(在本例中为 for 循环迭代)时,它将被释放。内存地址是相同的,因为编译器每次都在同一个位置分配它。您的程序崩溃是因为您尝试取消引用指向任何内容的指针。

您应该通过new Item; 在堆上动态分配每个Item - 但请注意,您必须在您分配的每个变量上调用delete,否则您有自己的内存泄漏。

【讨论】:

  • 注意:本地存储分配是一个比堆栈更正确的术语。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-23
相关资源
最近更新 更多