Ошибка EXC_BAD_ACCESS для элемента std::string структуры

При доступе к члену структуры типа std::string выскакивала ошибка Bus Error: 10. Код выглядит следующим образом.

#include <iostream>
#include <string>

struct KeyValuePair {
    std::string key;
    std::string value;
};

struct KeyValuePair *temp = (struct KeyValuePair *) malloc(sizeof(struct KeyValuePair));


int main(void) {

    temp->value = "|";

    temp->value += "someval|";

    std::cout << temp->value << std::endl;

    return 0;
}

Запуск gdb в коде показывает следующее в строке temp->value = "|".

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00007fff8d99e524
0x00007fff898dc7ca in std::string::_M_mutate ()

Из приведенного выше сообщения все, что я понимаю, это то, что мой код пытается получить доступ к недопустимой/неавторизованной области памяти.

Мой вопрос: хотя я использовал malloc для получения области памяти для глобальной переменной temp, почему я не могу получить к ней доступ. Что мне не хватает. Пожалуйста помоги.


person Deepak    schedule 01.07.2014    source источник


Ответы (3)


Между С и С++ есть разница:

  • в C экземпляр struct не имеет врожденного инварианта, он просто находится в памяти
  • в C++ экземпляр struct или class имеет набор инвариантов, которые устанавливаются конструктором и поддерживаются общедоступным интерфейсом на протяжении всего времени его существования.

Это проявляется здесь:

  • malloc, будучи конструкцией C, просто зарезервирует немного необработанной памяти
  • new, являясь конструкцией C++, не только резервирует часть необработанной памяти, но также вызывает соответствующий конструктор и, таким образом, устанавливает инварианты экземпляра (если они есть).

Примечание: при использовании new со встроенными типами, такими как int, new int на самом деле ничего не инициализирует...

Поэтому при доступе к temp->value вы получаете доступ к неинициализированной памяти. Это Undefined Behavior (всё может случиться), и в вашем случае программа идёт по какому-то дикому указателю и попадает в зону памяти, доступ к которой ей запрещен.

Итак, просто забудьте пока об этих C-измах: обычные конструкции C++ гарантируют, что конструктор будет вызван для вас надлежащим образом.

KeyValuePair temp;

int main() {
    // ...
}

Или, если вам действительно нужно динамически выделяемое значение (зачем?):

KeyValuePair* temp = new KeyValuePair();

int main() {
    // ...
}

Но тогда вам придется подумать о вызове delete на temp в какой-то момент, только один раз, что намного сложнее. Без умного указателя это проигрышная игра.

person Matthieu M.    schedule 01.07.2014
comment
И это был мой следующий квест. Чтобы понять разницу между malloc и new. Вы помогли мне. - person Deepak; 01.07.2014

Вам нужно использовать new, а не malloc, чтобы гарантировать создание объектов std::string.

KeyValuePair *temp = new KeyValuePair;

Когда вам нужно уничтожить структуру, используйте delete.

delete temp;

Как правило, вы не должны использовать malloc при написании кода на C++. Вместо этого используйте new. Именно по этой причине.

Тем не менее, для вашего простого примера кажется, что нет реальной необходимости в динамическом распределении. Вы можете избежать динамического распределения следующим образом:

KeyValuePair temp;
person David Heffernan    schedule 01.07.2014
comment
Почему new? Это правильно, но новичкам это редко нужно. - person MSalters; 01.07.2014
comment
@MSalters Верно. Я просто хотел объяснить, почему malloc неправильно. - person David Heffernan; 01.07.2014

Вы используете конструкции C в C++. Просто полностью исключите malloc:

KeyValuePair temp; // Yes, that simple.

int main( ) {

    temp.value = "|";
person MSalters    schedule 01.07.2014