проблема форматирования памяти Windows - PullRequest
0 голосов
/ 30 ноября 2018

Я пытаюсь заставить это динамическое перераспределение работать переносимым способом.

Моя программа принимает строку текста от пользователя и добавляет ее в буфер.Если длина текста в буфере составляет 20 или более, он удаляет первые 20 символов и перемещает любые символы после этого в начало буфера.

У меня есть этот код, который работает чисто в Linux, но когда язапустить его на окнах, он выбрасывает мусор.Кто-нибудь знает, почему / как сделать этот портативный только с помощью malloc.IE не использует string.h (strcpy) str ... ничего кроме len.

только c17 - никаких сломанных элементов (не переносимых).вот мой кодКомпилируется без ошибок gcc 7.3, mingw 7.3.Я заменяю get и put более безопасными функциями, и все равно получаю мусор на windows.Я предполагаю, что это проблема форматирования ...

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

void wbuff (message)
    char *message;
{
    FILE *f = fopen("file.txt", "w");
    fprintf(f, "%s", message);
    fclose(f);
}

char *rean (message)
    char *message;
{
    /* performs (write) on buffer, trims lefover, then restores */

    char buf[80] = "";
    puts("enter a line");
    gets(buf);

    int bln  =  strlen( buf );
    int mln  =  strlen( message );
    int nln  =  bln + mln;
    printf("new length %d\n", nln);

    message = realloc(message, nln);
    memmove(message + mln, buf, bln);

    /* MISTAKE IS HERE?! */
    if( nln >= 20 ) {
        int exl  = nln -20;                        // leftover length
        char *lo = realloc(NULL, exl);             // leftover placeholder
        memmove(lo, message+20, exl);              // copy leftover
        wbuff(message);                            // write clear buff
        message = realloc(NULL, nln);
        message = realloc(NULL, exl);              // resize buffer
        memmove(message, lo, exl);                 // restore leftover
    }

    return message;
}

void main (void)
{
    char *message = "";
    message = realloc(NULL, 0);
    while ( 1 == 1 ) {
        message = rean( message );
        puts(message);
    }
    return;
}

1 Ответ

0 голосов
/ 30 ноября 2018

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

Когда вы впервые устанавливаете message в main:

char *message = "";
message = realloc(NULL, 0);

message либо равен NULL, либо указывает на 0 байт памяти.Когда вы звоните, то звоните rean в первый раз:

int mln  =  strlen( message );

Вы либо пытаетесь разыменовать нулевой указатель, чтобы прочитать после конца выделенной памяти.Вы хотите выделить как минимум 1 байт для запуска и установить этот байт в 0, чтобы у вас была пустая строка:

char *message = realloc(NULL, 1);
message[0] = '\0';

Затем, когда вы скопируете буфер в сообщение:

message = realloc(message, nln);
memmove(message + mln, buf, bln);

Вы не выделяете достаточно места для завершающего нулевого байта и не копируете его, поэтому у вас нет строки.Когда вы затем пытаетесь распечатать его, либо puts, либо printf читает после конца выделенной памяти.Вам нужно выделить 1 дополнительный байт и скопировать 1 дополнительный байт:

message = realloc(message, nln + 1);     // allocate 1 extra byte for the null terminator
memmove(message + mln, buf, bln + 1);    // copy 1 extra byte

При повторном копировании чего-либо после 20 символов возникают похожие проблемы:

 int exl  = nln -20;                        // leftover length
 char *lo = realloc(NULL, exl);             // leftover placeholder
 memmove(lo, message+20, exl);              // copy leftover
 wbuff(message);                            // write clear buff
 message = realloc(NULL, nln);
 message = realloc(NULL, exl);              // resize buffer
 memmove(message, lo, exl);                 // restore leftover
  • строки 2-3: Вы не выделяете место для завершающего нулевого байта для lo и не копируете его.
  • строка 5: утечка памяти, ранее сохраненная message в первом realloc, путем передачи присваивания message при использовании NULL в качестве первого аргумента
  • строка 6-7: Вы теряете память, выделенную в строке 5, делая то же самое.Кроме того, вы снова не выделяете место для нулевого байта и не копируете его на следующую строку.

Как и раньше, выделяйте 1 дополнительный байт для каждого выделения и перемещайте 1 дополнительный байт на счетдля нулевого терминатора.Кроме того, освободите lo в конце блока, удалите лишние realloc для message и передайте предыдущее значение от message до realloc, чтобы не допустить утечки памяти:

 int exl  = nln -20;                        
 char *lo = realloc(NULL, exl + 1);         // allocate 1 extra byte
 memmove(lo, message+20, exl + 1);          // copy 1 extra byte
 wbuff(message);                            
                                            // remove extra realloc
 message = realloc(message, exl + 1);       // pass in old message, allocate 1 extra
 memmove(message, lo, exl + 1);             // copy 1 extra byte
 free(lo);                                  // free leftover

Все эти проблемы чтения и записи после окончания выделенной памяти вызывают неопределенное поведение , которое объясняет, почему вы видите разные результаты в разных операционных системах.

Что касается кода, соответствующегоидет, используйте fgets intead из gets:

 fgets(line, sizeof(line), stdin);

Эта функция будет включать новую строку в line, если есть место для этого, поэтому обязательно удалите ее, если это так.

Также измените main, чтобы он возвращал int, и удалите #include <malloc.h>, поскольку семейство функций malloc определено как находящееся в stdlib.h.

Если вы использовали strcpy иstrcat вместо memmove вам не пришлось бы учитывать копирование нулевого завершающего байта, поскольку эти функции делают это за вас.Однако вам все равно придется учитывать это при распределении памяти.Также нет конфликта между strcpy, malloc и realloc, так как все они являются частью стандарта и работают вместе должным образом.Использование их вместе - не проблема.Если они не работают должным образом, вы не используете их правильно.

После применения моих обновлений вы можете заменить это:

memmove(message + mln, buf, bln + 1);

на это:

strcat(message, buf);

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

 memmove(lo, message+20, exl + 1);              // copy leftover
 ...
 memmove(message, lo, exl + 1);                 // restore leftover

На это:

 strcpy(lo, message+20);
 ...
 strcpy(message, lo);

И оно все равно будет работать должным образом и будет соответствовать.

...