Проблемы с использованием realloc в C

У меня есть код, получающий строковые данные из сокета, который дает сбой на первой итерации:

int size_data = 1024*sizeof(char);              
char *data = malloc(size_data);
char *data_aux;
int br_aux=0;
int *nptr;

memset(&data[0], 0, size_data);
int br = recv(sockdata, data, size_data, 0);
data_aux = malloc(br);
while (br>0) {
  br_aux = br_aux + br;
  strcat(data_aux, data);
  br = recv(sockdata,data, size_data, 0);
  if (br > 0) {
    nptr = (int *) realloc(data_aux, br+br_aux);
  }
}
free(data);
printf("%s", data_aux);
free(data_aux);

Ничего такого сложного, но я получаю сообщение об ошибке:

* обнаружен glibc ./clientFTP: free (): недопустимый следующий размер (нормальный): 0x00000000061d6420 * * ======= Backtrace: ====== === /lib64/libc.so.6[0x366be7247f] /lib64/libc.so.6(cfree+0x4b)[0x366be728db] ./clientFTP[0x401e39] /lib64/libc.so.6 (__ libc_start_main + 0xf4) [ 0x366be1d9b4] ./clientFTP[0x400b89] ======= Карта памяти: ======== 00400000-00403000 r-xp 00000000 fd: 00 5396214 / home / alumno / FTP / clientFTP 00602000-00603000 rw- p 00002000 fd: 00 5396214
/ home / alumno / FTP / clientFTP 061d6000-061f7000 rw-p 061d6000 00:00 0
[куча] 366ba00000-366ba1c000 r-xp 00000000 fd: 00 1994999
/ lib64 /ld-2.5.so 366bc1c000-366bc1d000 r - p 0001c000 fd: 00 1994999
/lib64/ld-2.5.so 366bc1d000-366bc1e000 rw-p 0001d000 fd: 00 1994999
/lib64/ld-2.5. так 366be00000-366bf4e000 r-xp 00000000 fd: 00 1995001
/lib64/libc-2.5.so 366bf4e000-366c14e000 --- p 0014e000 fd: 00 1995001
/lib64/libc-2.5.so 366c14e000-366c152000 r --p 0014e000 fd: 00 1 995001
/lib64/libc-2.5.so 366c152000-366c153000 rw-p 00152000 fd: 00 1995001
/lib64/libc-2.5.so 366c153000-366c158000 rw-p 366c153000 00:00 0 3672200000-367220d000 r- xp 00000000 fd: 00 1995011
/lib64/libgcc_s-4.1.2-20080825.so.1 367220d000-367240d000 --- p 0000d000 fd: 00 1995011
/lib64/libgcc_s-4.1.2-20080825.so .1 367240d000-367240e000 rw-p 0000d000 fd: 00 1995011
/lib64/libgcc_s-4.1.2-20080825.so.1 2b5cdf8d9000-2b5cdf8dd000 rw-p 2b5cdf8d9000 00:00 0 2b5cdf8f6000f2b5cdf: 00 0 7fffae47e000-7fffae493000 rw-p 7ffffffe9000 00:00 0
[стек] 7fffae5fc000-7fffae600000 r-xp 7fffae5fc000 00:00 0
[vdso] ffffffffff600000-ffffffffffe00000 --- p 00000000 00:00 0
[vsyscall] Прервано


person Joe Lewis    schedule 16.05.2012    source источник


Ответы (4)


Есть две разные проблемы.

Во-первых, линия

nptr = (int *)realloc(data_aux,(br+br_aux));

делает три вещи:

  1. Он пытается выделить br + br_aux байта.
  2. Он может освободить память, на которую указывает data_aux.
  3. Он указывает nptr на адрес вновь выделенной памяти.

Но код продолжает использовать data_aux, как будто он все еще указывает на новую память.

Во-вторых, поскольку recv() возвращает количество полученных байтов, вы должны использовать эту информацию для добавления данных в буфер:

while (br > 0) {
  memcpy(data_aux + br_aux, data, br);
  br_aux += br;
  br = recv(sockdata, data, size_data, 0);
  if (br > 0) {
    nptr = (int *) realloc(data_aux, br + br_aux);
    if (nptr == NULL) {
      // ERROR
    }
    data_aux = nptr;
  }
}

Проблема с recv() в сочетании с любой из строковых операций (например, strcat()) заключается в том, что recv() может возвращать двоичные данные. Если эти данные содержат нулевой байт, строковые функции будут считать, что это конец данных, и будут вести себя так, как вы не ожидаете и не хотите.

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

person Adam Liss    schedule 16.05.2012
comment
Спасибо большое, все заработало !!! Но я просто изменил ваш код в этой строке: nptr = (int *) realloc (data_aux, br + br_aux); для этого: data_aux = (char *) realloc (data_aux, br + br_aux); Не могли бы вы объяснить, почему вместо strcat работает memcpy ?? Спасибо - person Joe Lewis; 16.05.2012
comment
Рад, что это сработало, но вам действительно стоит проверить наличие ошибок. realloc() может вернуть NULL в случае ошибки, что приведет к сбою программы. Вам нужно memcpy() вместо strcat(), потому что строковые функции будут остановлены, когда они встретят байт 0 (нулевой символ). Функции memxxx() принимают явный аргумент длины. - person Adam Liss; 16.05.2012
comment
и что я могу использовать, если я хочу получать файлы другого типа, например zip-файл (я пробовал, но это не сработало) - person Joe Lewis; 16.05.2012
comment
Сказать, что это не сработало, слишком расплывчато. Что случилось? Приведенный выше фрагмент должен работать для любого типа файла, если у вас не закончится память ... поэтому вам нужно проверять возвращаемые значения на наличие ошибок. :-) - person Adam Liss; 16.05.2012
comment
По-видимому, я получил 1024 байта несколько раз в br (так как файл большой), но я не знаю, почему он не копирует информацию, полученную сокетом. Результатом являются 5-байтовые данные, когда размер исходного файла составляет 19xxx байтов. Что я мог проверить? - person Joe Lewis; 16.05.2012
comment
Вы все еще используете printf() для отображения данных? Как и другие строковые функции, он остановится, когда встретит первый нулевой байт. - person Adam Liss; 16.05.2012
comment
Я распечатал данные с помощью printf, и он возвращает много странных символов (поскольку это zip-файл), однако первые возвращенные данные - это окончательные данные, которые я получаю (5-байтовые данные), поэтому он игнорирует конкатенацию остальной части данные... - person Joe Lewis; 16.05.2012
comment
Вы можете попробовать напечатать data и data_aux внутри цикла, чтобы вы могли видеть данные по мере их получения. Также будьте осторожны, поскольку цикл завершится до последнего memcpy(), вы потеряете последний полученный буфер. (Вы можете изменить цикл или добавить еще memcpy() после цикла.) - person Adam Liss; 16.05.2012
comment
Проблема заключается в методе копирования, memcpy не копирует больше информации, только первые 5 байтов, возвращаемых перед циклом, а strcat не заполняет data_aux полностью (но 8094 байта), однако он завершается правильно и без ошибок .... : \ - person Joe Lewis; 16.05.2012

В коде много проблем, но я решу самые серьезные:

  1. Вы выделяете память и предполагаете, что она готова к использованию:

    data_aux = malloc(br);
    ...
    strcat(data_aux, data); /* <- who said data_aux isn't garbage? */
    
  2. # P3 #
    # P4 #
    data_aux = realloc(data_aux, br + br_aux); /* reassign data_aux */
    
  3. Вы не проверяете никаких возвращаемых значений. Большой не проверяет результат recv() перед выделением ему памяти:

    br = recv(...);
    ...
    data_aux = malloc(br); /* -1 as a size is large in unsigned speak */
    
  4. Вы используете строковые функции ASCIIZ для данных, которые могут даже не содержать chars. Используйте memcpy вместо strcat или strncat.

person user7116    schedule 16.05.2012
comment
Чтобы прояснить второй момент, вы должны присваивать возвращаемое значение realloc параметру data_aux, а не nptr. (И вы должны преобразовать возвращаемое значение в char*.) - person happydave; 16.05.2012
comment
Этот ответ упускает из виду обе большие проблемы. Нет проверки на наличие нулевых символов, и указатель никогда не корректируется так, чтобы указывать на перераспределенную память. никогда нельзя вызывать realloc() и продолжать использовать исходный указатель. - person Adam Liss; 16.05.2012
comment
@AdamLiss: Уточню готовые к использованию и добавлю еще большие. - person user7116; 16.05.2012
comment
Большое вам спасибо, вы правы !! Как предложил пользователь Адам Лисс, я использовал memcpy вместо strcat, что, как я полагаю, инициализирует информацию о data_aux в случае содержания мусора ... не так ли? - person Joe Lewis; 16.05.2012
comment
@JoeLewis: вас заботит только то, что размер этого буфера не совпадает с размером данных, полученных recv. Если это строковые данные ASCIIZ, это означает, что вам необходимо убедиться, что они завершаются NUL, или скопировать их в другой буфер символов правильного размера. - person user7116; 16.05.2012

Эээ - строка, которую вы получаете из сокета, завершается нулем?

Предложения:

  1. Убедитесь, что вы включили нулевой байт в свое сообщение, прежде чем отправлять его.

  2. Используйте strncat ()

  3. Обнулить буфер самостоятельно, если необходимо

PS: Я предполагаю, что вы переполняете буфер в вашем «strcat (data_aux)», что непреднамеренно повреждает как «data», так и «data_aux».

person paulsm4    schedule 16.05.2012
comment
Я не вижу, как это может решить мою проблему, конкатенация должна нормально работать с strcat, а строка, полученная из сокета, не является нулевой, поскольку я использую переменную br. - person Joe Lewis; 16.05.2012

Если realloc() завершается успешно, входной блок памяти больше не действителен, и возвращаемый указатель указывает на новый блок памяти. Если realloc() не удается, блок входной памяти все еще действителен. Вы назначаете возвращаемый указатель переменной nptr, которая вообще ни для чего не используется, и никогда не обновляете переменную data_aux, чтобы она указывала на перераспределенную память. Когда вы вызываете free(), чтобы освободить data_aux, если realloc() был вызван заранее и был успешным, вы освободите неправильный указатель, что может привести к сбоям, как вы видите.

Измените это:

nptr = (int *) realloc(data_aux,(br+br_aux)); 

Вместо этого:

nptr = (char*)realloc(data_aux, br_aux + br); 
if (!nptr)
{
    ... error handling ...
    break;
}
data_aux = nptr;

С учетом сказанного, вам следует переписать всю логику, чтобы вместо этого было что-то вроде следующего. Нет необходимости вызывать recv() несколько раз в цикле:

int size_data = 1024 * sizeof(char);                
char *data_aux = NULL;
int br_aux = 0;  
char *nptr;  

char *data = malloc(size_data);  
if (data)
{
    int br = recv(sockdata, data, size_data, 0);
    while (br > 0)
    {  
        nptr = (char*) realloc(data_aux, br_aux + br);
        if (!nptr)
            break;

        data_aux = nptr;
        memcpy(&data_aux[br_aux], data, br);  
        br_aux = br_aux + br;  
    }  

    free(data);  
}

printf("%.*s", br_aux, data_aux);  
free(data_aux);  

Для дальнейшего упрощения поместите вместо этого буфер data в стек:

char* data_aux = NULL;
int br_aux = 0;  

char data[1024];
char *nptr;  

int br = recv(sockdata, data, sizeof(data), 0);
while (br > 0)
{  
    nptr = (char*) realloc(data_aux, br_aux + br);
    if (!nptr)
        break;

    data_aux = nptr;
    memcpy(&data_aux[br_aux], data, br);  
    br_aux = br_aux + br;  
}  

printf("%.*s", br_aux, data_aux);  
free(data_aux);  
person Remy Lebeau    schedule 16.05.2012