1

我到处寻找我的问题的答案,但我还没有找到我的问题的可靠答案。

我目前正在用 C 编写一个程序,专门针对 UNIX 命令行(我使用 Linux 作为我的开发环境,但我希望这个程序尽可能地可移植)。现在,我有一个提示用户输入的基本 shell。然后用户将输入一个命令,该命令将被相应地处理。这是我到目前为止的代码:

/* Main.c */
int main(int argc, char **argv)
{
    while (TRUE)
    {
        display_prompt();
        get_command();
    }

    return 0;
}

/* Main.h */
void get_command()
{
    /*
     * Reads in a command from the user, outputting the correct response
     */

    int buffer_size = 20;   
    char *command = (char*) malloc(sizeof(char) * buffer_size);

    if (command == NULL)
    {
       return_error("Error allocating memory");
    }

    fgets(command, buffer_size, stdin);
    if (command[strlen(command) - 1] == '\n')
    {
        puts("It's inside the buffer.");
    }
    else
    {
        puts("It's not inside the buffer.");
    }

    free(command);
}

我最初的想法是检查\n字符并查看它是否适合buffer_size,如果它不realloc()适合扩展分配的内存的数据。

但是,在我realloc()的字符串之后,我将如何将剩余的数据添加stdincommand

4

4 回答 4

5

如果您确实需要,请使用 getline(3)。这是 POSIX.1-2008。请注意,无限长度的行是 DOS 攻击 (OOM) 的简单攻击向量。所以考虑制定一个合理的行长限制,并使用 fgets(3)。

于 2012-08-11T21:02:39.660 回答
2

您不需要执行任何重新分配,只需为 \0 添加比最大命令长度多 1 个字节并忘记 \n ,因为您不会总是得到一个 \n 用户输入输入。如果用户输入超过长度,那么您的字符串将被截断而没有 \n。因此,您在 fgets 之后的情况不正确并且基于错误的假设。

就像是:

   int buffer_size = MAX_COMMAND_LENGTH + 1;

关于内存分配:在这种情况下,您应该使用堆栈而不是堆来避免 malloc/free。所以你的代码会更简单,更不容易出错:

    char command[buffer_size];
    ...
    // free(command) <-- you dont need this anymore

请注意,您的命令将在函数返回后被释放。因此,如果您将其处理为 get_command 就可以了,但如果您想将其返回给调用者,您将收到来自调用者的缓冲区。

于 2012-08-11T20:17:02.807 回答
2

我认为关于假设最大命令长度的答案是正确的:通常您希望将命令保持在合理的长度内。

但是如果你真的不能对命令的最大长度做出假设,那么你就需要缓冲。

保持:

  • 一个固定buffer的,你总是fgets输入相同数量的字符,并且
  • acommand您附加的,以及realloc在必要时。

以下代码可能缺少一些错误处理:

#define BUFFER_SIZE 20
#define COMMAND_BLOCK_SIZE 50

void get_command()
{
    char *buffer = malloc(sizeof(char) * (BUFFER_SIZE + 1));

    char *command = malloc(sizeof(char) * (COMMAND_BLOCK_SIZE + 1));
    int commandSize = 50;

    // tmp pointer for realloc:
    char *tmp = NULL;
    char *retval = NULL;

    if ((buffer == NULL) || (command == NULL))
        return_error("Error allocating memory");

    retval = fgets(buffer, BUFFER_SIZE, stdin);

    while (retval != NULL)
    {
        if (strlen(buffer) + strlen(command) > commandSize)
        {
            tmp = realloc(command, commandSize + (COMMAND_BLOCK_SIZE + 1));
            if (tmp == NULL)
                return_error("Error allocating memory");
            else
            {
                 command = tmp;
                 commandSize += COMMAND_BLOCK_SIZE;
            }
        }

        // not using strncat because the check above should guarantee that
        //    we always have more than BUFFER_SIZE more bytes in command 
        strcat(command, buffer);

        if (buffer[strlen(buffer) - 1] == '\n')
            break;

        retval = fgets(buffer, BUFFER_SIZE, stdin);
    }

    printf("COMMAND: %s\n", command);
    free(buffer);
}

另请注意:

  • 我们在那里没有做任何有用的事情command,你可能想要传入 achar **以便你可以command退出这个函数,并在调用代码中释放它,例如在你的主循环中。
  • '\n' 保留在command:您可能想要丢弃它。
于 2012-08-11T20:40:55.997 回答
2

如果您使用的是 gnu 系统,请使用 c 库的gnugetline扩展,它会为您完成所有动态调整。

例如(虽然我没有测试过)

void get_command()
{
    /*
     * Reads in a command from the user, outputting the correct response
     */

    size_t buffer_size = 0;   
    char *command = NULL;

    ssize_t len = getline(&command, &buffer_size, stdin);

    if(len < 0)
    {
        perror("Error reading input");
    }
    else if (command[len - 1] == '\n')
    {
        puts("It's inside the buffer.");
    }
    else
    {
        puts("It's not inside the buffer.");
    }

    free(command);
}
于 2012-08-11T20:55:06.593 回答