Грешка 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 и C++:

  • в C екземпляр на struct няма присъщ инвариант, той просто се намира в паметта
  • в C++ екземпляр на struct или class има набор от инварианти, които се установяват от конструктора и се поддържат от публичния интерфейс през целия му живот

Това се показва тук:

  • malloc, тъй като е C конструкция, просто ще запази малко сурова памет
  • new, тъй като е C++ конструкция, не само ще запази част от необработената памет, но и ще извика подходящия конструктор и по този начин ще установи инвариантите на екземпляра (ако има такива)

Забележка: ако използвате new с вградени типове като int, new int всъщност не инициализира нищо...

Следователно, когато осъществявате достъп до temp->value, имате достъп до неинициализирана памет. Това е недефинирано поведение (всичко може да се случи) и във вашия случай програмата следва някакъв див указател и каца в зона на паметта, достъпът до която е забранен.

Така че, просто забравете за тези 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