#c
#c #указатели #память #ошибка сегментации
Вопрос:
Я пытаюсь создать собственный класс list, который имеет фиксированный размер и может хранить целые числа. Просто для учебных целей.
Вот как я это делаю: у меня есть структура с именем Item, которая содержит данные (целое число для хранения) и указатель на следующий элемент в списке. Когда список инициализируется, я сначала добавляю в список x количество пустых элементов.
Адрес элемента, объявленного на итерации (n-1), сохраняется в буфере, так что адрес элемента на итерации n может быть установлен как следующий для элемента на предыдущей итерации (n-1). Это позволяет связать все элементы друг с другом.
И адрес самого первого элемента также сохраняется как точка доступа для всего списка позже в коде.
Но на самом деле это не работает; это мой код:
#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 = amp;item;
//Setting current item as nextptr
//for previous item in buffer
if (i > 0)
this->buffer->next = amp;item;
//Storing current address in buffer
this->buffer = amp;item;
//Outputting address and value (just for testing purposes)
//std::cout << "Address: " << amp;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
Однако, когда я раскомментирую тестовую строку 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
Каждый элемент имеет один и тот же адрес, конечно, это не то, что я имел в виду. Каждый элемент, созданный на итерациях, должен быть другим элементом с другим адресом.
Имеет ли это какое-то отношение к тому, что элемент объявлен локальным, и по этой причине объект хранится в стеке, а не в свободном пространстве? Я попытался использовать ключевое слово ‘new’, но это не сработало!
TL; DR 1) Почему я получаю ошибку сегментации 2) Как исправить, чтобы каждый элемент, созданный в List(), имел отдельный адрес?
Комментарии:
1. Используется
new
для выделения памяти дляItem
. Почему вы ожидаете, что локальная переменная alllocated в локальном хранилище будет иметь другой адрес на каждой итерации цикла?2. Сохранение
amp;item
для последующего использования бессмысленно, посколькуitem
время жизни заканчивается итерацией, на которой оно было создано. Последующее разыменование его не определено.
Ответ №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;
}
}
Но тогда ваш код будет пропускать память. Если у вас есть такой конструктор, вам также понадобится конструктор копирования, оператор присваивания и деструктор (правило 3)
Комментарии:
1. Я уже думал о чем-то подобном и пытался использовать ‘new’, но получил ошибки. Благодаря вам я знаю, понимаю это на 100%! Спасибо!
Ответ №2:
Item item;
является переменной, выделенной стеком, что означает, что когда программа выходит из текущей области видимости (в данном случае для итерации цикла), она будет освобождена. Адрес памяти один и тот же, потому что компилятор каждый раз выделяет его в одном и том же месте. Ваша программа выходит из строя, потому что вы пытаетесь разыменовать указатель, указывающий на ничто.
Вы должны динамически распределять каждый Item
из них в куче через a new Item;
— Но будьте осторожны, вы ДОЛЖНЫ вызывать delete
каждую из выделяемых вами переменных, иначе у вас будет утечка памяти.
Комментарии:
1. Примечание: выделенное локальное хранилище является более правильным термином, чем stack .