2015-01-18 2 views
0

У меня есть функция-члена внутри mmapped-файл всепоглощающего класса, который выглядит следующим образом:недопустимого указатель при возврате зОго :: строки (так говорит LibC)

std::string Data::GetASCIIZ(OFFSET* offsetp) const 
{ 
    char* str = (char*)_buffer + *offsetp; // _buffer points to mmap'd file 
    *offsetp += strlen(str) + 1; 
    return std::string(str); 
} 

(тип «OFFSET» является беззнаковый длинный длинный)

Его raison d'etre - (a) возвращает строку std :: строку C-строки с нулевым завершением, которая предположительно существует со смещением *offsetp, после (b) продвижения значения *offsetp мимо конца указанной C-строки.

Я вызываю эту функцию во многих ситуациях, без проблем. Тем не менее, я недавно добавил новый вызов к нему, что всегда SIGABRTs в своеобразно:

*** glibc detected *** /home/ryan/src/coolapp/out/coolapp: free(): invalid pointer: 0xb7eb165c *** 

Данное сообщение сопровождается трассировку (кульминацией некоторого кода в libc.so.6), и карта памяти ... оба из которых якобы мне полезны, в отладке этой проблемы.

От отладки с помощью GDB я узнал, что SIGABRT фактически не происходит в моем методе Data::GetASCIIZ, указанном выше, а скорее в коде, который вызывает его в правой части задания. (Таким образом, я полагаю, при вызове конструктора копирования StD :: струны):

[EDIT обновлен для ласточкин хвост с ожидаемым ответом от @WhozCraig]

struct stuff 
{ 
    char version; 
    std::string sigstring; 
    // ... 
}; 

stuff* mystuff = (stuff*)malloc(sizeof(stuff)); 
// ... 
mystuff->sigstring = _data->GetASCIIZ(offsetp); // SIGABRT HAPPENS AT THIS SCOPE 

В этой конкретной ситуации, C-строка со смещением *offsetp оказывается пустой строкой, но я проверял, что это не является косвенным, временно изменяя *offsetp, чтобы указать на что-то еще из GDB.

Мой метод отмечен const, потому что он не изменяет никакого внутреннего состояния объекта Data. Я возвращаю объект, который живет в стеке, но я не делаю этого по ссылке, и я ожидал, что конструктор копирования (в вызывающем коде) будет делать правильную вещь, прежде чем этот элемент стека будет разрушен.

Я попытался переписать метод GetASCIIZ, чтобы использовать явный локальный, но это не помогло.

Я что-то упустил?

В случае, если это полезно, вот разбор вызова во время назначения, где происходит этот SIGABRT. («==>» находится в точке ошибки.)

424   sigstring = _data->GetASCIIZ(offsetp); 
    0x0807def1 <+183>: mov 0x8(%ebp),%eax 
    0x0807def4 <+186>: mov 0x4(%eax),%eax 
    0x0807def7 <+189>: lea 0x4(%eax),%ecx 
    0x0807defa <+192>: lea -0x18(%ebp),%eax 
    0x0807defd <+195>: mov 0x1c(%ebp),%edx 
    0x0807df00 <+198>: mov %edx,0x8(%esp) 
    0x0807df04 <+202>: mov %ecx,0x4(%esp) 
    0x0807df08 <+206>: mov %eax,(%esp) 
    0x0807df0b <+209>: call 0x809e6ee <Data::GetASCIIZ(unsigned long long*) const> 
    0x0807df10 <+214>: sub $0x4,%esp 
    0x0807df13 <+217>: mov -0x14(%ebp),%eax 
    0x0807df16 <+220>: lea 0x4(%eax),%edx 
    0x0807df19 <+223>: lea -0x18(%ebp),%eax 
    0x0807df1c <+226>: mov %eax,0x4(%esp) 
    0x0807df20 <+230>: mov %edx,(%esp) 
    0x0807df23 <+233>: call 0x8049560 <[email protected]> 
    0x0807df28 <+238>: lea -0x18(%ebp),%eax 
    0x0807df2b <+241>: mov %eax,(%esp) 
=> 0x0807df2e <+244>: call 0x80497f0 <[email protected]> 
    0x0807e026 <+492>: lea -0x18(%ebp),%eax 
    0x0807e029 <+495>: mov %eax,(%esp) 
    0x0807e02c <+498>: call 0x80497f0 <[email protected]> 
    0x0807e031 <+503>: mov %ebx,%eax 
    0x0807e033 <+505>: jmp 0x807e046 <CoolClass::SpiffyMethod(unsigned long long, unsigned long long, unsigned long long*)+524> 
    0x0807e035 <+507>: mov %eax,%ebx 
+0

Как можно прийти * при заявленном «новом вызове»? Выполняет ли объект «Данные» непреднамеренную копию и нарушает ситуацию [RO3] (http://en.wikipedia.org/wiki/Rule_of_three_ (C% 2B% 2B_programming))? И мне действительно интересно, что вы делаете с 'sigstring' * before *, чтобы вызвать вашу функцию. То есть Что такое '// ...'? – WhozCraig

+0

Попробуйте что-то вроде const string sigstring = _data-> Get ... – Pradyumna

+0

@WhozCraig По 'new call' Я предполагаю, что вы имеете в виду этот самый недавно добавленный вызов Data :: GetASCIIZ(). Нет; адрес объекта Data передается в конструктор класса, который в конечном итоге выполняет этот вызов. Этот адрес безводится внутри члена '_data', и он всегда упоминается только через' _data'. Итак, никаких копий. Также: '// ...' просто означает <какой-то нерелевантный код идет здесь>. Это назначение - первое, что когда-либо случается с 'sigstring' –

ответ

-1

Ваш образец выглядит следующим образом.

std::string Data::GetASCIIZ(OFFSET* offsetp) const 
{ 
    char* str = (char*)_buffer + *offsetp; // _buffer points to mmap'd file 
    *offsetp += strlen(str) + 1; 
    return std::string(str); 
} 

Не следует ли оператор возврата вернуть новой строки STL?

std::string Data::GetASCIIZ(OFFSET* offsetp) const 
{ 
    char* str = (char*)_buffer + *offsetp; // _buffer points to mmap'd file 
    *offsetp += strlen(str) + 1; 
    return new std::string(str); 
} 
+1

Это просто неправильно. –

+0

Нет, это выделит новую std :: string в куче и приведет к утечке памяти. Моя строка 'return std :: string (str)' эффективно создает неназванную локальную переменную исключительно для того, чтобы вернуть ее (по значению) вызывающей подпрограмме, которая присваивает ее значение другой строке до того, как первая будет уничтожена. –

+0

Так как я до сих пор не использовал STL, я не был уверен, и я ценю разъяснение. Если я понимаю, что вы говорите, то возвращаемый 'std :: string (str)' подразумевает новый оператор. –

Смежные вопросы