Проблема форматирования памяти Windows
Я пытаюсь заставить это динамическое перераспределение работать портативным способом.
У меня есть этот код, который работает чисто в Linux, но когда я запускаю его в Windows, он выбрасывает мусор. Кто-нибудь знает почему / как сделать этот портативный только с помощью 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;
}
2 ответа
В 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);
memmove(message + mln, buf, bln + 1);
Существуют похожие проблемы, когда вы переписываете что-либо после 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
Вы не выделяете место для завершающего нулевого байта для lo
и ты не копируешь это. Вы теряете память, ранее удерживаемую message
во-первых realloc
назначить ему, а затем утечь эту память при повторном назначении. В этом последнем realloc
Вы снова не выделяете место для нулевого байта и не копируете его на следующей строке.
Как и прежде, выделите 1 дополнительный байт для каждого распределения и переместите 1 дополнительный байт для учета нулевого терминатора. Также бесплатно lo
в конце блока удалите лишние realloc
за message
и передать предыдущее значение message
в realloc
чтобы не пропускать память:
int exl = nln -20; // leftover length
char *lo = realloc(NULL, exl + 1); // leftover placeholder
memmove(lo, message+20, exl + 1); // copy leftover
wbuff(message); // write clear buff
message = realloc(message, exl + 1); // resize buffer
memmove(message, lo, exl + 1); // restore leftover
free(lo); // free leftover
Эти проблемы чтения и записи после окончания выделенной памяти вызывают неопределенное поведение, что объясняет, почему вы видите разные результаты в разных операционных системах.
Насколько соответствует код, используйте fgets
инеад из gets
:
fgets(line, sizeof(line), stdin);
Также изменить 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);
И это все еще будет работать должным образом и будет соответствовать.
И это помогло бы убедиться, что версия Windows работает так же стара, как и компилятор. Это было также, где я дурачился. не портативный не элегантно но рабочее решение для окон, которое, кажется, самое лучшее, на что можно надеяться при прикосновении к нему.
char
*rean
(message)
char *message;
{
/* performs (write) on buffer, trims lefover, then restores */
char buf[80] = "";
puts("enter a line");
gets(buf);
int bln = scount( buf );
int mln = scount( message );
int nln = bln + mln +1;
printf("new length %d\n", nln);
if ( x == 0 ) {
message = realloc(message, bln+1);
memmove(message, buf, bln);
strcat(message, "\0");
x = 1;
} else {
message = realloc(message, nln);
memmove(message + mln, buf, bln);
memmove(message + nln-1, "\0", 1);
}
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;
}
C17 Compat и почти портативный * Scount Strlen без использования string.h
Я знаю, что у malloc есть проблемы. это стало ясно мне при использовании sting.h(s) malloc. Я могу жить без string.h. Я предпочитаю. Если вы не можете писать строковые функции вручную, значит long не должен был использовать c. Я даже не знаю, почему люди поддерживают string.h. malloc с другой стороны.