我正在尝试以一种可移植的方式使此动态重新分配工作。
我的程序接受用户的一行文本并将其附加到缓冲区。如果缓冲区中的文本长度为20个或更多,它将删除前20个字符,并将其后的所有字符移到缓冲区的开头。
我有这段代码可以在Linux上正常工作,但是当我在Windows上运行它时会发出垃圾。有谁知道为什么/如何仅使用malloc使其具有可移植性。 IE浏览器不使用string.h(strcpy)的str ...除了伦。
仅限c17-没有损坏的饰钉(不可移植)。这是我的代码。编译无误gcc 7.3,mingw 7.3。我用更安全的功能替换了gets和puts函数,但在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;
}
答案 0 :(得分:5)
在C中,字符串是由一个空字节终止的字符序列。您这里有许多错误,主要是与不考虑该事实以及内存泄漏有关。
首次在message
中设置main
时:
char *message = "";
message = realloc(NULL, 0);
message
要么为NULL,要么指向0字节的内存。通话时,第一次拨打rean
:
int mln = strlen( message );
您正在尝试取消引用NULL指针以读取分配的内存末尾。您希望分配至少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
lo
的终止空字节分配空间,也没有复制它。 message
作为第一个参数的同时,传递给realloc
的赋值,从而泄漏了message
在第一个NULL
中先前保存的内存和以前一样,为每个分配分配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
这些在分配的内存末尾进行读写的问题都调用undefined behavior,这解释了为什么您在不同的操作系统上看到不同的结果。
就符合标准的代码而言,请使用fgets
的{{1}} 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
它仍然可以正常工作并且符合要求。