изменено после освобождения — возвращает массив C `c_str` (const char*) в структуре

#c #arrays #c #malloc #c-strings

Вопрос:

Как мне получить массив c_str из a std::vector (внутри a struct ) для использования пользователями C?

Попытка:

 #include <vector>
#include <algorithm>

typedef struct { size_t size; const char** arr; } CStrStrArray;

CStrStrArray f() {
    const std::vector<const char*> cStrVec {"foo", "bar"}; 
    /* pretend ^this is huge^ with size   contents not known ahead of time */

    const char **cStrArr = (const char**)malloc(cStrVec.size());
    std::copy(cStrVec.begin(), cStrVec.end(), cStrArr);
    /* also tried `cStrVec.data();` */
    return {cStrVec.size(), cStrArr};
}

/* pretend this is 'main.c' and the above is in an `extern C` elsewhere */
int main(void) {
    CStrStrArray c_str_arr = f();
    free(c_str_arr.arr);
    c_str_arr.size = 0;
    return EXIT_SUCCESS;
}
 

Ошибка:

 malloc: Incorrect checksum for freed object 0x7ff996d3d790: probably modified after being freed.
Corrupt value: 0x7ff996d08280
executable(17572,0x11c6d5e00) malloc: *** set a breakpoint in malloc_error_break to debug
 

Комментарии:

1. Привет @AT — вы не копируете содержимое, вы копируете указатели — и область содержимого завершена, когда метод завершен.. Попробуйте поместить «foo», как, скажем, статическую константу вне метода, и посмотрите, чем она отличается.

2. @MrR Это указатели на строковые литералы, так что можно копировать указатели yhe

Ответ №1:

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

 malloc(cStrVec.size()) --> malloc(cStrVec.size() * sizeof *cStrArr)
                                  ------------/   --------------/
                                   Number of        size of a single
                                   char pointers    char pointer
                                   in the vector

                                  -------------------------------/
                                          Memory needed
 

Комментарии:

1. Должно быть cStrVec.size() * sizeof(char *) , вы тоже выделяете совпадение

2. @Слава cStrVec.size() * sizeof(char *) -это то же самое, что cStrVec.size() * sizeof *cStrArr . Однако последнее — «лучший стиль кодирования на языке Си» — по нескольким причинам. Аналогично: int d; assert(sizeof d == sizeof(int)); все в порядке

3. Что меня зацепило в этом, так это то, что я могу ссылаться на переменную до того, как она будет «сделана», как будто это не имеет смысла char c = c 5 ; но я думаю sizeof , что это особенное?

4. Да, вы правы, я думал, что cStrArr это указатель на CStrStrArray

5. @В Да, sizeof это особенное.

Ответ №2:

Если вам нужно преобразовать std::vector<std::string>> в CStrStrArray вас, вам не нужен промежуточный шаг и создайте дополнительные std::vector<const char *> :

 CStrStrArray f( const std::vector<std::string> amp;v ) {
    CStrStrArray r{ v.size(), 
         reinterpret_cast<const char **>( malloc( sizeof( char * ) * v.size() ) };
    for( size_t i = 0; i < v.size();   i )
        r.arr[i] = strdup( v[i].c_str() );
    return r;
}
 

Комментарии:

1. Да, но я не могу сделать это static , потому что я не знаю его размер заранее…

2. Вам не нужно заранее знать его размер, даже если он статичен, вы имеете в виду, что он будет меняться от звонка к звонку?

3. @Слава — Да, он вызывает HTTPS API на удаленном сервере, а затем я фильтрую ответ в строковый вектор, а затем преобразую этот строковый вектор в массив c_str

4. @В строковом векторе как?

5. Ну, у меня есть большой вектор структур, затем я преобразую его в вектор std::string (или const char* ), а затем преобразую его в массив c_str ( const char** ).