【问题标题】:c function for retrieving string from user input has memory issues用于从用户输入中检索字符串的 c 函数存在内存问题
【发布时间】:2014-10-13 19:27:29
【问题描述】:

我有以下代码用于通过终端提示从用户那里获取字符串:

#include <stdio.h>
#include <stdlib.h>

void GetString(int*, int*);

int main(void)
{
    unsigned int strLength = 32;
    char *stringPtr = malloc(strLength);
    printf("Enter some input: ");
    if (stringPtr != NULL)
    {
        int c = EOF;
        unsigned int i = 0;
        while ((c = getchar()) != '\n' && c != EOF)
        {
            stringPtr[i++] = (char) c;
            if (i == strLength)
            {
                strLength = i+strLength;
                if(stringPtr = realloc(stringPtr, strLength))
            }
        }
        stringPtr[i] = '\0';
        printf("\n\nString value: %s\n\n", stringPtr);
        free(stringPtr);
        stringPtr = NULL;
    }
}

从用户的角度来看,它运行良好,但是,我是新手,现在才真正开始了解指针如何相互配合,但是,我还没有找到一个可以简单地传递的好的在线示例如何成功处理未知数量的输入,而不用担心缓冲区溢出、分段错误等。

我上面列出的代码是我使用几个与动态内存分配相关的示例以及一些关于字符串操作的论坛构建的。任何人都可以验证这是一种安全、有效的方式来处理未知长度的用户输入吗?如果不是,您能否提供有关我发布的内容不正确的原因以及如何改进的信息?我只是想确保我学习正确,因为我正在自学(大部分情况下),这可能会导致对 C 的破坏性误解,从我从朋友/在线文章中听到的。

*****我已根据 cmets 中提供的帮助和下面的答案将上面的代码修改为更好的状态。我愿意对此进行进一步改进,并希望此示例可以帮助其他试图更好地了解如何以安全有效的方式处理用户输入的人。******

#include <stdio.h>
#include <stdlib.h>

void GetString(int*, int*);

int main(void)
{
    unsigned int strLength = 32;
    char *stringPtr = malloc(strLength);
    if (stringPtr == NULL)
    {
        fprintf(stderr, "Unable to allocate memory to hold char array. Exiting!\n");
        return 1;
    }
    printf("Enter some input: ");
    int c = EOF;
    unsigned int i = 0;
    while ((c = getchar()) != '\n' && c != EOF)
    {
        stringPtr[i++] = (char) c;
        if (i == strLength)
        {
            strLength += strLength;
            if ((stringPtr = realloc(stringPtr, strLength)) == NULL)
            {
                fprintf(stderr, "Unable to expand memory to hold char array. Exiting!\n");
                return 2;
            }
        }
    }
    stringPtr[i] = '\0';
    if (sizeof(stringPtr) < strLength)
    {
        stringPtr = realloc(stringPtr, strLength);
    }
    printf("\n\nString value: %s\n\n\n", stringPtr);
    free(stringPtr);
    stringPtr = NULL;
}

【问题讨论】:

  • 代码看起来不错,但内存管理很糟糕(每个附加字符都会重新分配)。对初始缓冲区大小和增量(双缓冲区大小)做出一些合理的假设。最后,您可能会进行浪费的缓冲区(重新)分配并缩小缓冲区。
  • SO 不是代码审查的地方。这个问题及其答案是不可搜索的,也无助于丰富 SO 的宝藏。它对其他读者没有用。
  • 我很抱歉。我认为这是形成关于 SO 的问题的好方法。您知道有没有更好的地方可以发布此类问题?
  • @AnthonyHopkins:有一些问题对 SO 数据库的“丰富”程度要低得多(这个问题还不错)
  • @AnthonyHopkins 您的原始问题很好,请避免对答案做出反应并调整问题。 (不过,您可以更改标题)

标签: c string input


【解决方案1】:

将此视为部分答案,希望以后可以提供更多提示。 以下是一些初始的 cmets:

  1. 首先检查 malloc() 是否成功。其他无需提示。
  2. 如果 malloc() 失败,立即终止,您可以使用 exit(1) 或 return(1),但如果您还先向 stderr 写入错误消息,例如使用 fprintf(stderr, "malloc failed! ");这样您就不必缩进 main() 函数的主要部分!
  3. 一次分配一个字节效率不高。通常,您会以更大的步长递增,例如一次 80 个字节。
  4. 您的代码可能无法很好地处理退格键或 UNICODE。但我想现在讨论还为时过早。
  5. realloc() 可能会失败,但你永远不会检查它。

代码可以更高效(更短),但你有一个好的开始!

【讨论】:

  • 关于 4.: 怎么样?据我所知,一切都很好,无论读取了什么字节。 (退格不是char。)
  • 一个退格(通常是 ASCII 8)可能会被读取并实际占用缓冲区中的空间。没有错,就是有点不方便。如果你要保存字符串(包含退格),你以后可能会得到棘手的结果。
  • 程序的正确行为将 ASCII 0x8 解释为 0x8 而不是退格。如果我在终端中输入^V+&lt;backspace&gt;^V+^H,我希望接收到的是char,而不是实际的退格键。
  • 我想我们可以花一整夜讨论哲学问题,最终以诅咒和stty告终。我的重点是“如何改进”这个问题。并且在接收退格时减少缓冲区将是一种改进。所以我建议了。
【解决方案2】:

这条线效率不高,

char *stringPtr = malloc(strLength);

您为一个char 分配了内存。然后你为它重新分配更多。我建议你分配一个缓冲区,例如 1024 字节,如果输入大于 1024,那么你再分配 1024 字节。这类似于 stl::vector 增加其内存的方式。 (您也可以选择分配 512 字节)。

其背后的原因是,您每次向系统请求一些内存,都是昂贵的系统调用。因此,最好要求比您保存对系统的调用更多的要求。

【讨论】:

  • 我看到人们最初使用了不同大小的缓冲区,但我读到使用过多的内存可能会导致可利用的代码,因此我决定一次增加一个字节。我从来没有想过更长的字符串可能会有多昂贵。我对拥有 mroe 内存的安全性是不正确的吗?如果我分配 1-00 个字节,并且用户输入:“Hello”,那么实际上只使用了 6 个字节(包括 '\0')。对不起,如果这是一个愚蠢的问题。我真的想更好地处理这个问题。
  • 这是一个不错的考虑,但在这里并不真正相关。请注意,即使您只请求一个字节,操作系统也可能会为您保留更多,因为每次保留一个块更有效。
【解决方案3】:

这个答案的重点是 OP 提到:

 I have yet to find a good working example online that can simplistically relay 
 how to successfully handle an unknown amount of input without fear of buffer
 overflow, segmentation faults, etc.

Buffer OverflowSegmentation fault 上的维基百科文章很好地详细解释了它们。

What is segmentation fault? 上的 Stack Overflow 答案也提供了很好的解释。

如果您想真正深入了解缓冲区,如何使用它们并通过 asm/c 代码操作它们,我强烈建议您阅读文章“Smashing The Stack For Fun And Profit”。这篇文章本质上是关于使用缓冲区的第一个安全漏洞的圣经。这确实是一部出色的作品。

另外,如果您还在上大学,我强烈建议您阅读有关您在学校学到的任何概念的维基百科。令人惊讶的是,维基百科的文章比教授的文章更深入(我去了世界顶尖大学之一)。你会发现阅读维基百科的文章真的可以帮助你为期中和期末教授扔给你的“曲线球”做好准备。

如果您有任何问题,请告诉我!

【讨论】:

  • 感谢您的深入文章!这些看起来包含很多很好的信息。我会确保通过这些!
  • 很高兴听到这个消息,安东尼!希望您喜欢它们,如果您有任何问题,请告诉我!
  • 如果用户只输入 6 个字节,使用 100 个字节的缓冲区是否安全?这似乎有很多未使用的内存可能导致利用,因此我只根据需要分配字节。我是不是太偏执了,还是我只是不理解 c 内存管理的复杂性?
  • 你在正确的轨道上!只分配所需的内存总是好的(就最佳实践/程序员的责任而言)。非常奇怪的是,现代操作系统(2006 年后)使用地址空间布局随机化(en.wikipedia.org/wiki/Address_space_layout_randomization),因此现在很难利用未使用的堆栈空间。有办法解决这个问题,但你不应该太担心!很好的问题,如果您有任何其他问题,请告诉我! :0)
猜你喜欢
  • 1970-01-01
  • 2022-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-21
  • 2015-04-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多