-9

我正在尝试使这种动态重新分配以可移植的方式工作。

我的程序接受来自用户的一行文本并将其附加到缓冲区。如果缓冲区中的文本长度为 20 或更多,它会删除前 20 个字符并将其后的任何字符移动到缓冲区的开头。

我有这段代码可以在 Linux 上运行干净,但是当我在 Windows 上运行它时会发出垃圾。有谁知道为什么/如何仅使用 malloc 使其可移植。IE 不使用 string.h(strcpy) str... 除了 len。

仅限 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;
}
4

1 回答 1

5

在 C 中,字符串是由空字节终止的字符序列。您在这里有许多不相干的错误,主要与不考虑这一事实以及内存泄漏有关。

当您第一次设置messagemain

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
  • 第 2-3 行:您不为终止的空字节分配空间,lo也不复制它。
  • message第 5 行:您通过在使用时realloc传递分配给第一个参数来泄漏先前持有的内存messageNULL
  • 第 6-7 行:您通过执行相同的操作泄漏了第 5 行中分配的内存。此外,您也不会为空字节分配空间,也不会将其复制到下一行。

和以前一样,为每个分配分配 1 个额外字节并移动 1 个额外字节以说明空终止符。此外,lo在块的末尾 free ,删除额外realloc的 for message,并将之前的值传递给messageto ,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

这些在分配内存结束后读写的问题都会引发未定义的行为,这解释了为什么您在不同的操作系统上看到不同的结果。

就一致性代码而言,使用fgetsintead of gets

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

如果有空间,此函数将包含一个换行符line,因此如果是这种情况,请务必将其删除。

还要更改main为 returnint和 remove ,#include <malloc.h>因为malloc函数族被定义为驻留在stdlib.h.

如果您使用strcpyandstrcat而不是memmove,您将不必考虑复制空终止字节,因为这些函数会为您执行此操作。但是,在分配内存时,您仍然需要考虑这一点。, 和,之间也没有冲突strcpy,因为它们都是标准的一部分并且可以正常工作。一起使用是没有问题的。如果它们不能为您正常工作,那么您就没有正确使用它们。mallocrealloc

应用我的更新后,您可以替换它:

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);

它仍然可以正常工作并符合要求。

于 2018-11-30T17:26:15.153 回答