【问题标题】:Unnecessary new line character while taking multiple strings as input [duplicate]将多个字符串作为输入时不必要的换行符[重复]
【发布时间】:2016-11-28 04:29:29
【问题描述】:

我编写了一个小代码来获取多个字符串作为用户的输入。在此之前,我要求用户输入要作为输入的字符串数。以字符串为输入时,换行符自动占据第一个字符串的位置。我不知道发生了什么:(这是我的代码:

#include<stdio.h>
#include<string.h>
void main()
{
    char *arr[20],str[40];
    int n;
    printf("enter the number of strings\n");
    scanf("%d",&n);
    int i;
    printf("Enter the strings\n");[It isn't taking 3 strings as input,as a newline character is already occupying first place][1]
    for(i=0;i<n;i++)
    {
        gets(str);
        arr[i]=(char *)malloc(sizeof str);
        strcpy(arr[i],str);
    }
    printf("The Strings are:\n");
    for(i=0;i<n;i++)
    {
        printf(arr[i]);
        printf("\n");
    }
}

【问题讨论】:

  • 从不,从不使用gets。由于不安全,它已从 C11 中删除。请改用fgets(例如fgets (str, sizeof str, stdin))。您的问题的答案是getsfgets(和getline)都读取直到并包括'\n'。您通常会在存储字符串之前将其删除(例如,size_t len = strlen (str); if (str[len-1] == '\n') str[len-1] = 0;,因此您的某些变量不会有换行符),但如果您只是打印回stdout,请不要添加'\n'在您的printf 声明中。
  • 另外,NOT 转换malloc 的返回值,这是不必要的。有关详细说明,请参阅:Do I cast the result of malloc?
  • 是的,你是对的,fgets 绝对比 gets 更安全,因为它缓解了缓冲区溢出问题。那么,在我将字符串数量作为输入之后,换行符已经在输入缓冲区中等待了吗?并且在读取第一个字符串时,它会在缓冲区中看到一个换行符并将其作为第一个字符串?
  • 由于arr[i] 的内容是由用户定义的,使用printf(arr[i]); 是非常不安全的。用户可以在其中键入各种带有% 的内容并破坏您的程序。 %245n 很可能会导致崩溃。使用printf("[%s]\n", arr[i]); 或类似的方式,让自己有机会查看数据。方括号分隔字符串以帮助您查看前导和尾随空格(以及 CR 字符等)。

标签: c string malloc newline


【解决方案1】:

从 cmets 开始,您的代码中存在许多问题。首先gets,不要使用它,这是一种“悬而未决”的攻击——说得够多了。

接下来,验证所有用户输入。就你所知,一只猫可能会踩在键盘上。确保你测试你收到的输入,并且它符合你的期望。

scanf面向行的 输入混合使用(例如fgetsgetline 可能会给新用户带来问题。为什么?scanf 系列函数不会删除@987654326 @ 而是将其留在输入缓冲区中(例如stdin)。然后,当您尝试使用fgets 读取时,它在stdin 中看到的第一个字符是什么?一个'\n',它读取并认为是一个整体行。如果要使用scanf 读取字符串的数量,则由您决定删除scanf 留在stdin 中的'\n'

这并不难,您实际上可以在格式字符串中使用scanf 提供的赋值抑制运算符。例如:

scanf ("%d%*c", &n);

* 是赋值抑制运算符,当与%*c 一起使用时,它只是告诉scanf 读取并丢弃下一个字符而不添加到 match count(例如 scanf 返回的内容)。

在填充数组时,您需要使用while 循环而不是for 循环(或使用独立索引)。为什么?如果用户使用 ctrl + d (或 ctrl + z onwindoze)取消输入怎么办。如果您要迭代到 n 而不管用户做什么,您都可以轻松地尝试索引您尚未分配的数组元素,或为未输入的字符串分配。

总而言之,您可以执行以下操作:

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

enum { MAXA = 20, MAXS = 40 }; /* max ptrs and str */

int main (void) {

    char *arr[MAXA] = {NULL}, str[MAXS] = "";
    int i = 0, n = 0, ndx = 0;   /* initialize all variables */

    printf ("enter the number of strings: ");
    /* use assignment suppression %*c to discard the \n */
    if (scanf ("%d%*c", &n) != 1) { /* always validate input */
        fprintf (stderr, "error: invalid integer input.\n");
        return 1;
    }

    if (n > MAXA) { /* validate the value of n */
        fprintf (stderr, "warning: n > %d, using %d as limit.\n",
                MAXA, MAXA);
        n = MAXA;
    }

    printf("Enter the strings\n");
    while (ndx < n && fgets (str, sizeof str, stdin)) { /* validate input */
        size_t len = strlen (str); /* get the length */
        if (str[len - 1] == '\n')  /* check for '\n' */
            str[--len] = 0;        /* overwrite with nul-terminator */
        if (!(arr[ndx] = malloc (len + 1))) { /* validate allocation */
            fprintf (stderr, "error: virtual memory exhausted.\n");
            break;
        }
        strcpy (arr[ndx], str);   /* copy to array */
        ndx++;                    /* increment index */
    }

    printf("\nThe Strings are:\n");

    for (i = 0; i < ndx; i++) {  /* you have ndx strings not n */
        printf (" arr[%2d] : %s\n", i, arr[i]);
        free (arr[i]);  /* free memory when no longer needed */
    }

    return 0;
}

使用/输出示例

$ ./bin/nogets
enter the number of strings: 3
Enter the strings
My dog
has a lot
of fleas.

The Strings are:
 arr[ 0] : My dog
 arr[ 1] : has a lot
 arr[ 2] : of fleas.

现在尝试同样的操作并按 ctrl + d 而不是输入"of fleas."(您已被覆盖)。

最后,在您编写的动态分配内存的任何代码中,对于分配的任何内存块,您都有 2 个职责:(1) 始终保留指向起始地址的指针 对于内存块,(2)它可以在不再需要时释放。养成跟踪您分配的内存的习惯,并free 它而不是依赖它在exit 上完成。随着您的程序变得越来越复杂,这将为您提供良好的服务。

查看代码,确保您了解发生了什么。如果您有任何问题,请告诉我。

【讨论】:

  • 先生,我从您的代码和建议中学到了很多,我必须承认!我以前对很多事情一无所知,但是我从您的回答中了解了它们。我已经相当了解您的代码,从现在开始我将尝试遵循这种风格。
  • 很高兴我能帮上忙。编程的很大一部分只是学习如何将任何给定的问题分解为语言提供的工具来解决的任务。 C 是一种特别低级的语言。很多时候,您只是在决定如何处理内存字节。这个问题从stdin(输入缓冲区)读取字节并存储这些字节以供以后使用。这包括整数输入和字符串。这个问题有很多学习内容。花点时间了解如何处理整数、字符和浮点值的区别。你会做得很好。
【解决方案2】:

当你使用scanf读取数字时,它只处理输入的数字,而不是你输入数字后按下的回车。

可能值得刷新 for 输入的其余部分,例如 How to clear input buffer in C?

【讨论】:

    【解决方案3】:

    gets() 函数应将标准输入流 stdin 中的字节读取到 s 指向的数组中,直到读取到 &lt;newline&gt; 或遇到文件结束条件。

    所以每当你进入之后

    输入字符串的数量

    5

    所以如果你按下回车键,那么gets 就会被调用,它会将换行作为第一个输入。

    如果你没有输入回车,然后按 5abc 然后回车,那么它会将 abc 视为第一个字符串。

    你需要在gets之前清除缓冲区。

    你的正确程序是

    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    void main()
    {
            char *arr[20],str[40];
            int n;
            int ch;
            printf("enter the number of strings\n");
            scanf("%d",&n);
            int i;
            printf("Enter the strings\n");
            //flush the input stream
            while ((ch = getchar()) != '\n' && ch != EOF);
            for(i=0;i<n;i++)
            {
                    gets(str);
                    arr[i]=(char *)malloc(sizeof str);
                    strcpy(arr[i],str);
            }
            printf("The Strings are:\n");
            for(i=0;i<n;i++)
            {
                    printf("%s",arr[i]);
                    printf("\n");
            }
    }
    

    【讨论】:

      猜你喜欢
      • 2016-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-02
      • 1970-01-01
      • 1970-01-01
      • 2013-06-05
      相关资源
      最近更新 更多