【问题标题】:Segmentation fault when using scanf for int?使用scanf for int时出现分段错误?
【发布时间】:2020-10-26 22:04:08
【问题描述】:

这是我的代码:

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

int main (int argc, char ** argv)
{
    int ops = 0;
    printf("Enter number of operations:");
    scanf("%d",&ops);

    FILE * fp = fopen (argv[0],"wb");
    //int i = 0;
    int key = 0;
    char op;
    for (int i = 0; i < ops; i++)
    {
        printf("\nEnter a key:");
        //printf("\nhi");
        scanf("%d",&key);  // Line 20 -- Segfault
        printf("\nh");
        fwrite(&key,sizeof(int),1,fp);
        printf("\nNow enter an operation for that key:");
        scanf("%c",&op);
        fwrite(&op,sizeof(char),1,fp);
    }
    fclose(fp);
    printf("Operations file written");

    return EXIT_SUCCESS;
}

程序运行良好,直到第 20 行的第一个循环本身上的 scanf。它返回“分段错误(核心转储)”。我有点不明白为什么只需调用 scanf 就会发生这种情况。

【问题讨论】:

  • 虽然行号确实可以更容易地找到正确的行,但实际上无法复制并运行它。
  • argv[0] 是 exe 名称,不是吗?
  • 您用于获取段错误的输入是什么?它在我的电脑上运行良好。
  • @JohnnyMopp 提出了一个很好的观点。你确定你不是指argv[1]?你拥有它的方式,你正在覆盖你的可执行文件。
  • 在许多系统上,尝试打开可执行文件进行写入只会失败 - 并且代码不会检查 fopen 的返回值以查看它是否成功。如果失败,则fp 为空,fwrite 调用可能会出现段错误。所以我想知道 OP 是否只是弄错了崩溃的位置。

标签: c segmentation-fault scanf user-input


【解决方案1】:

您在fwrite 上遇到了段错误,因为fpNULL

您正在使用argv[0],这是可执行文件的名称。而且,您没有检查fopen 的结果是否成功/失败。

这是更正后的代码。 #if 0 表示旧的/您的代码。 #if 1 表示添加代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#if 1
#include <errno.h>
#endif

int
main(int argc, char **argv)
{
    int ops = 0;

    // do this first so you abort before prompting the user
#if 1
    // skip over program name
    --argc;
    ++argv;

    if (argc < 1) {
        fprintf(stderr,"No output filename specified\n");
        exit(1);
    }

    FILE *fp = fopen(*argv, "wb");
    if (fp == NULL) {
        fprintf(stderr,"Unable to open '%s' -- %s\n",argv[0],strerror(errno));
        exit(1);
    }
#endif

    printf("Enter number of operations:");
    scanf("%d", &ops);

#if 0
    FILE *fp = fopen(argv[0], "wb");
#endif

    // int i = 0;
    int key = 0;
    char op;

    for (int i = 0; i < ops; i++) {
        printf("\nEnter a key:");
        // printf("\nhi");
        scanf("%d", &key);
        printf("\nh");
        fwrite(&key, sizeof(int), 1, fp);
        printf("\nNow enter an operation for that key:");
        scanf("%c", &op);
        fwrite(&op, sizeof(char), 1, fp);

    }
    fclose(fp);
    printf("Operations file written");

    return EXIT_SUCCESS;
}

更新:

当我之后运行我的程序时,我输入了我的密钥,它会打印出该密钥的输入操作,但是一旦发生这种情况,就会出现下一个密钥的提示。可能是什么原因造成的?

没有考虑换行符 ['\n'] 和/或空格。 scanf 可以将这些视为空格,因此在格式中添加空格可以处理:

scanf(" %d",&key);

通常,%d 在查找数字时会跳过空格,因此 %d 就足够了。

但是,这仍然不能解决真正的问题,因为问题在于 第二 scanf。上面的 [更改] scanf 将在格式之前删除空格

但是,它不会在数字之后去除空格。

因此,第二个 scanf("%c",&amp;op); [for a single char] 将由 previous 中的 newline 实现 /em> 输入行。

所以,我们需要:

scanf(" %c",&op);

以上是一个简单的修复。大多数情况下,您必须查阅 scanf 的联机帮助页并尝试使用格式说明符来转储换行符。

还有一些事情......

fwrite [有效地]:

fwrite(buf,number_of_elements,size_of_single_element,stream)

因此,您的 fwrite 参数与惯用语相反。

另外,当您为key 执行fwrite 时,它会将二进制 值(即4 个字节)写入输出文件。没关系,但是,这就是你真正想要的 [vs.正在做(例如)printf(fp,"%d");?

无论如何,这是更新后的代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#if 1
#include <errno.h>
#endif

#ifndef DEBUG
#define DEBUG       0
#endif

#define dbgprt(_fmt...) \
    do { \
        if (DEBUG) \
            printf("DEBUG: " _fmt); \
    } while (0)

int
main(int argc, char **argv)
{
    int ops = 0;

    // do this first so you abort before prompting the user
#if 1
    // skip over program name
    --argc;
    ++argv;

    if (argc < 1) {
        fprintf(stderr,"No output filename specified\n");
        exit(1);
    }

    FILE *fp = fopen(*argv, "wb");
    if (fp == NULL) {
        fprintf(stderr,"Unable to open '%s' -- %s\n",argv[0],strerror(errno));
        exit(1);
    }
#endif

    printf("Enter number of operations:");
    scanf("%d", &ops);

#if 0
    FILE *fp = fopen(argv[0], "wb");
#endif

    // int i = 0;
    int key = 0;
    char op;

    for (int i = 0; i < ops; i++) {
        printf("\nEnter a key:");
        // printf("\nhi");
#if 0
        scanf("%d", &key);
#else
        scanf(" %d", &key);
#endif
        dbgprt("key=%d\n",key);

        printf("\nh");
#if 0
        fwrite(&key, sizeof(int), 1, fp);
#else
        fwrite(&key, 1, sizeof(key), fp);
#endif

        printf("\nNow enter an operation for that key:");
        // NOTE/BUG: need to account for newline in scanf below
#if 0
        scanf("%c", &op);
#else
        scanf(" %c", &op);
#endif
        dbgprt("op='%c'\n",op);

#if 0
        fwrite(&op, sizeof(char), 1, fp);
#else
        fwrite(&op, 1, sizeof(op), fp);
#endif
    }

    fclose(fp);
    printf("Operations file written\n");

    return EXIT_SUCCESS;
}

【讨论】:

  • 但是argv[0]不是可执行文件名吗?
  • @sciencepiofficial 不,因为上面有--argc; ++argv;
  • 我傻了,没注意到这两行。
  • @CraigEstey 谢谢。原来我应该使用 argv[1]。但是,我还有另一个问题。当我之后运行我的程序时,我输入了我的密钥,它会打印出该密钥的输入操作,但是一旦发生这种情况,就会出现下一个密钥的提示。是什么原因造成的?
【解决方案2】:

问题是您使用的是argv[0],它返回可执行文件名称——您实际上是在覆盖并因此损坏了您的可执行文件。要获取您想要的文件,请使用argv[1]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-05
    • 2015-06-08
    • 1970-01-01
    • 2012-04-30
    • 2013-03-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多