【问题标题】:How to implement gets() or fgets() while using structure pointer?使用结构指针时如何实现gets()或fgets()?
【发布时间】:2014-01-15 13:07:20
【问题描述】:

下面的代码与scanf() 完美运行,但我想输入字符和空格。我尝试过gets()fgets()(在下面评论),但它不起作用(它跳到循环中的下一次迭代并在fgets() 使用的名称输入的输出期间显示空白(NULL))。知道怎么做吗?

PS:我已经用示例程序尝试了fputs(),它工作正常。但是我在使用结构指针时有点困惑。

#include <stdio.h>
#include <stdlib.h>
struct details {
    uint Id;
    char Name[20];
};

int main() {
    int tot, i;
    struct details *info;
    printf("Enter no. of details to be stored = ");
    scanf("%d", &tot);
    info = (struct details *)malloc(tot*sizeof(struct details));

    for(i=0; i<tot; i++) {
        printf("%d)Id = ", i+1);
        scanf("%d", &info->Id);
        printf("%d)Name = ", i+1);
        fgets(info->Name, 20, stdin); //How to make fgets() work??
        //scanf("%s", &info->Name); //works fine with scanf() 
        info++;
    }

    for(i=0; i<tot; i++) {
    --info;
    }

    printf("\nDisplaying details:\n");
    for(i=0; i<tot; i++) {
        printf("%d)Id = %d\n%d)Name = %s\n", i+1, info->Id, i+1, info->Name);
    info++;
    }

return 0;
}

输出:

[xyz@xyz:Temp] $ ./fgets_Struct
Enter no. of details to be stored = 2
1)Id = 111
1)Name = 2)Id = 222
2)Name =
Displaying details:
1)Id = 111
1)Name =

2)Id = 222
2)Name =
[xyz@xyz:Temp] $

【问题讨论】:

  • 当你说它不起作用时,你是什么意思?你的循环有一个问题,你应该使用一个临时变量来保存存储在变量信息中的原始地址。
  • 不工作?请更具体一点。它是否返回一些垃圾数据,或者它是哪种“不工作”?
  • 另见这个关于使用 fgets() 和 sscanf() 的 stackoverflow stackoverflow.com/questions/19363951/…
  • @RichardChambers 我已经更新了问题,请看一下
  • @V-X 我已经更新了问题,请看一下。

标签: c struct fgets


【解决方案1】:

问题来自“%d”scanf。它不消耗行终止符,因此下一次读取会将其解释为空行。

您的info 分配也是错误的。您应该使用的分配大小的不是info 的大小,而是info 指向的元素的大小

printf("Enter no. of details to be stored = ");
scanf("%d\n", &tot); // <-- must consume end of line here
info = (struct details *)malloc(tot*
   sizeof(*info)); // <-- use size of the pointed object, not the pointer

此外,修改 info 指针是不必要且危险的。
这样的东西会更简单,更安全

for(i=0; i<tot; i++) {
    printf("%d)Id = ", i+1);
    scanf("%d\n", &info[i].Id)); // <-- store input in current info record
                                 // and eat up the line terminator
    printf("%d)Name = ", i+1);
    fgets(info[i].Name,          // <-- idem
          sizeof(info[i].Name),  // fgets will guarantee no buffer overflow
          stdin);
}

printf("\nDisplaying details:\n");
for(i=0; i<tot; i++) {
    printf("%d)Id = %d\n%d)Name = %s\n", i+1, info[i].Id, i+1, info[i].Name);

至于 scanf / fgets,在您的情况下(将“%s”格式赋予 scanf),它们都应该读取输入,直到输入新行,包括空格。

编辑:我在这里说错了,抱歉,感谢 chux 纠正我。

scanf ("%s") 确实会停在第一个空白处。如果你想要整行,最简单的方法是使用 fgets。如果你真的想使用 scanf,你需要像"%[^\n]%*c" 这样的语法。

为了确保在用户输入超过 20 个字符时缓冲区不会溢出,您可以

  • "%19[^\n]%*c" 用作scanf 格式('\0' 终止符需要第20 个字符),或
  • 使用 fgets 并传递 20 作为缓冲区大小(fgets 负责处理终止符,它将最多读取 19 个字符以便能够添加 '\0' 而不会溢出)。

当然你应该使用sizeof 来计算最大缓冲区大小,例如:

fgets(info[i].Name, sizeof(info[i].Name), stdin);

因此,如果您决定将缓冲区大小更改为 50 个字符,则无需修改该值。

【讨论】:

  • 感谢您的回答,但有个小疑问:我使用的是结构指针'struct details *info',根据我的理解,结构指针只能通过'->'访问(箭头)运算符。但是您在上面的答案中的代码使用 '.'(dot) 运算符。 info[i].Name 还是 info->Name?如果我错了,请纠正我。
  • info-&gt;id 完全等同于info[0].idInfo[i] 确实引用了指针。它与*(info+i) 完全相同(只是以更易读的形式!)。所以&amp;Info[i].id&amp;((*(info+i).id) 相同。方括号引用 info 以访问表的 ith 元素,而 & 获取此 ith 元素的 id 字段的地址。
  • @DragonX,看看这篇文章可能会帮助你理解指针和数组的关系denniskubes.com/2012/08/16/the-5-minute-guide-to-c-pointers
  • "...scanf ...with "%s" 格式...在输入新行之前都应该读取输入,包括空格。"是不正确的。 "%s" in scanf() "匹配一系列非空白字符" (C11dr §7.21.6.2 12)。要使用scanf() 读取直到遇到'\n',建议格式"%[^\n]""%[^\n]%*c". 第二种形式使用 '\n' 或在 OP 的情况下:"%19[^\n]%*c"
  • 抱歉再次 ping:sizeof(info[i].Name)-1 应该是 sizeof(info[i].Name),不需要 -1 - 我也这么认为了很长时间。 “fgets 函数读取的字符数最多n 指定的字符数少一个 ...”和fgets() 的19 s/b 20。 scanf() 19 很好。
【解决方案2】:

首先,您的代码中有一个严重的错误。

info = (struct details *)malloc(tot*sizeof(info));

sizeof(info) 返回 4 或 8(取决于平台,32 位或 64 位),但与您的结构大小无关。因此,您正在为指针分配内存,但正在为您的结构使用该空间。将数据写入其中将覆盖内存中的其他数据(尽管在那个简单的示例中可能不是),因为您分配的字节太少。您要使用的是sizeof(struct details),它将返回 24。

现在是您的输入问题。基本上, scanf 和 gets 不应该像那样一起使用。问题是 scanf 会读取您键入的内容,直到您按回车键,这不包括在内。当你在那之后调用gets时,它会看到这个返回并立即返回一个空字符串。如果您将所有输入调用更改为获取,它会完美运行。示例:

char test[256];
printf("Enter no. of details to be stored = ");
gets(test);
tot = atoi(test);

【讨论】:

    【解决方案3】:

    在 ID 的 scanf()fgets() 之间放置 getchar() 行 像这样

    scanf("%d",&(info->Id));
    getchar();
    

    【讨论】:

      【解决方案4】:

      我遇到了同样的问题!实际上,我来到这个页面是为了得到解决方案,然后我想起了缓冲区!!所以这就是我解决它的方式:

      [...]
      
      cout << "\nType the name: " ;
      fflush(stdin); (you always have to clean the buffer before saving chars to a variable)
      gets(pers.nom);
      
      [...]
      

      干杯!! 附言cin 和 cout 来自 C++,但像 scanf 和 printf 一样工作

      【讨论】:

      • fflush(stdin) 可能在某些编译器下工作,但行为不是标准定义的,所以你应该避免它并以其他方式清理输入缓冲区
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-13
      • 1970-01-01
      • 2016-04-12
      相关资源
      最近更新 更多