【问题标题】:Unable to understand scanf() behaviour in the following code无法理解以下代码中的 scanf() 行为
【发布时间】:2014-12-16 07:19:36
【问题描述】:

输入 -

COMETQ
HVNGAT

这是代码 -

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

#define MAXLEN 6

main(void)
{
    char comet[MAXLEN], group[MAXLEN];
    unsigned long int result[2] = { 1,1 };
    short int i, j;

    scanf("%s",comet);
    scanf("%s",group);
    printf("\nComet's Name: %s\nGroup's Name: %s",comet,group);
    printf("\nComet's No.: %ld\nGroup's No.: %ld",result[0],result[1]);

    i = j = 0;
    while(comet[i]!='\0' && i<MAXLEN){
        result[0] *= (comet[i] - 'A' + 1);
        i++;
    }
    while(group[j]!='\0' && j<MAXLEN){
        result[1] *= (comet[j] - 'A' + 1);
        j++;
    }

    printf("\nComet's No.: %ld\nGroup's No.: %ld",result[0],result[1]);
    printf("\nComet's No. Mod 47: %ld\nGroup's No. Mod 47: %ld",result[0]%47,result[1]%47);

    if(result[0]%47 == result[1]%47)
        printf("\nGO");
    else
        printf("\nSTAY");

    exit(0);
}

现在,据我所知,scanf() 会读取一个字符串,直到检测到空格。但是在这里,输出是-

Comet's Name: COMETQHVNGAT
Group's Name: HVNGAT
Comet's No.: 1
Group's No.: 1
Comet's No.: -534663680
Group's No.: 994500
Comet's No. Mod 47: 43
Group's No. Mod 47: 27
STAY

但是,不应该是这样吗?

comet = "COMETQ" & Group = "HVNGAT"

我不明白为什么没有发生这种情况?

另外,当comet的大小为6字节时,它怎么存储-COMETQHVNGAT

【问题讨论】:

  • 代码存在一些问题,尤其是 scanf 调用:代码应始终检查 scanf 返回的代码,以确保操作成功。格式字符串应该(几乎)总是以前导 ' ' (空格)开头,因此在任何输入之前都会跳过空格。输入是每行 6 个字符,每个缓冲区只有 6 个字符。 printf 正在尝试使用 '%s' 打印字符串,但是,'string' 缺少终止 nul 字符,因此第一个 printf 继续运行,直到遇到 nul char,这是未定义的行为,可能导致 seg故障事件
  • 建议:使缓冲区(至少)7 个字符长,并在调用 scanf 之前将缓冲区内容预设为全部 '\0'。注意:scanf 不会在输入字符数组的末尾插入 nul 字符。

标签: c arrays printf scanf


【解决方案1】:

如果你想使用 char 数组作为字符串,你需要空终止它。

在您的代码中,数组长度指定为6,而您的输入本身就是6 字节,没有空间来存储终止的空字符。这就是为什么当 cometgroup 被传递给 printf() 时,它们会产生奇怪的输出。

一个字符串是空终止的,根据这个原则,你的内存访问将超出分配的内存大小cometgroup,产生undefined behaviour

更安全的选择:

使用fgets() 读取输入,受缓冲区大小限制,去掉最后一个\n 字符,一切顺利。详情请查看man page

另外,您需要将unsigned long int 的格式说明符%ld 更改为%lu%ld 用于signed long int

【讨论】:

    【解决方案2】:

    这是因为缓冲区大小#define MAXLEN 6 - 它导致Undefined Behavior - 没有空间用于终止字符\0

    COMETQ\0
    0123457
    

    MAXLEN 定义为 7。

    【讨论】:

    • 那么它如何存储“COMETQHVNGAT”呢?
    • @paver - 它根本不存储它。输出是随机的 - 它称为Undefined_behavior
    • 会不会是groupcomet之后分配了内存?
    • @paver - 它被称为未定义行为 - 这意味着任何事情都可能在不同的平台上发生。不要试图用how or why 来推理它——阅读这个stackoverflow.com/questions/4176328/…
    • @paver - 您在特定系统上的特定编译器恰好连续分配了cometgroup。 C 不检查数组的边界,所以你可以很高兴地飞离数组的末端并继续前进,printf 正在这样做,寻找\0。您不应该依赖其他编译器和其他系统,甚至您的编译器在不同的代码段中连续分配变量。 al-Acme 是正确的,需要将 MAXLEN 定义得足够大以容纳 nul。
    【解决方案3】:

    数组被声明内存将被连续分配。所以在第一个字符串中你 无法给出空字符。所以这就是它完全打印的原因。

    如果你给的输入小于 maxlen 它给出正确的输出。确保 maxlen 的值很高,因为用户可以给出 n 个字符的名称。

    检查:

    如果你打印两个数组的起始字符的地址,你可以得到6个字节的差异。

    【讨论】:

    • 也感谢您的帮助。
    猜你喜欢
    • 2017-02-03
    • 2011-09-06
    • 1970-01-01
    • 2022-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-08
    • 2017-10-31
    相关资源
    最近更新 更多