Windows内存格式化问题

时间:2018-11-30 15:42:02

标签: 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;
}

1 个答案:

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

您没有为终止的空字节分配足够的空间,也没有复制它,因此您实际上没有字符串。然后,当您尝试打印它时,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

重新复制超过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

这些在分配的内存末尾进行读写的问题都调用undefined behavior,这解释了为什么您在不同的操作系统上看到不同的结果。

就符合标准的代码而言,请使用fgets的{​​{1}} intead:

gets

如果有空格,此函数将在 fgets(line, sizeof(line), stdin); 中包含换行符,因此请确保删除。

还更改line以返回main,并删除int,因为#include <malloc.h>函数族定义为驻留在malloc中。

如果您使用stdlib.hstrcpy而不是strcat,则无需考虑复制空终止字节,因为这些函数可以为您完成。但是,在分配内存时,您仍然需要考虑到这一点。 memmovestrcpymalloc之间也没有冲突,因为它们都是标准的一部分,可以正常工作。一起使用它们没有问题。如果它们不能为您正常工作,那么您就不能正确使用它们。

应用我的更新后,您可以替换以下内容:

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

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

相关问题