Windows内存格式化问题

-9 c malloc portability string.h c17

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

我的程序接受用户的一行文本并将其附加到缓冲区。如果缓冲区中的文本长度为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;
}
Run Code Online (Sandbox Code Playgroud)

dbu*_*ush 5

在C语言中,字符串是一个以空字节结尾的字符序列。您这里有许多偏离一处的错误,大多数与不考虑该事实以及内存泄漏有关。

首次设置messagemain

char *message = "";
message = realloc(NULL, 0);
Run Code Online (Sandbox Code Playgroud)

message任一个为NULL均指向0字节的内存。通话时rean,第一次通话:

int mln  =  strlen( message );
Run Code Online (Sandbox Code Playgroud)

您正在尝试取消引用NULL指针以读取分配的内存末尾。您想要分配至少1个字节来开始并将该字节设置为0,以便您有一个空字符串:

char *message = realloc(NULL, 1);
message[0] = '\0';
Run Code Online (Sandbox Code Playgroud)

然后,当您将缓冲区复制到消息中时:

message = realloc(message, nln);
memmove(message + mln, buf, bln);
Run Code Online (Sandbox Code Playgroud)

您没有为终止的空字节分配足够的空间,也没有复制它,因此您实际上没有字符串。然后,当您尝试打印它时,将读取putsprintf读取超出分配的内存末尾的内容。您需要分配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
Run Code Online (Sandbox Code Playgroud)

当您重新复制超过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
Run Code Online (Sandbox Code Playgroud)
  • 第2-3行:您不会为终止的空字节分配空间,lo也不会复制它。
  • 第5行:您将第一个参数传递给了while ,从而泄漏了先前由message第一个保留的内存reallocmessageNULL
  • 第6-7行:您通过做同样的事情来泄漏第5行分配的内存。同样,您不会再为空字节分配空间,也不会在下一行复制它。

和以前一样,为每个分配分配1个额外的字节,并移动1个额外的字节以说明空终止符。另外,lo在代码块的末尾释放,删除多余的reallocfor 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
Run Code Online (Sandbox Code Playgroud)

这些超出分配的内存末尾的读取和写入问题均调用未定义的行为,这解释了为什么您在不同的操作系统上看到不同的结果。

就符合标准的代码而言,请使用fgetsintead gets

 fgets(line, sizeof(line), stdin);
Run Code Online (Sandbox Code Playgroud)

line如果有足够的空间,此函数将在其中包含换行符,因此请务必删除它。

还更改main为return int,并删除,#include <malloc.h>因为malloc函数族定义为驻留在中stdlib.h

如果您使用了strcpystrcat不是memmove,那么您将不必考虑复制空终止字节,因为这些函数会为您完成复制。但是,在分配内存时,您仍然需要考虑到这一点。还有之间没有冲突strcpymallocrealloc,因为他们是标准和工作的各个部分一起正常。一起使用它们没有问题。如果它们不能为您正常工作,则说明您没有正确使用它们。

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

memmove(message + mln, buf, bln + 1);
Run Code Online (Sandbox Code Playgroud)

有了这个:

strcat(message, buf);
Run Code Online (Sandbox Code Playgroud)

并替换为:

 memmove(lo, message+20, exl + 1);              // copy leftover
 ...
 memmove(message, lo, exl + 1);                 // restore leftover
Run Code Online (Sandbox Code Playgroud)

有了这个:

 strcpy(lo, message+20);
 ...
 strcpy(message, lo);
Run Code Online (Sandbox Code Playgroud)

而且它将仍然可以正常工作并保持一致。