【问题标题】:Array Declaration causing weird behavior with while loop数组声明导致while循环的奇怪行为
【发布时间】:2018-03-12 10:25:22
【问题描述】:

我最近在我的 c 程序中遇到了非常奇怪的行为。我删除了大部分代码,只是为了找出问题发生在你们身上。

程序在当前状态下的目的是从 txt 文件中读取作业并将内容打印到屏幕上。这里是:

#include <stdio.h>

int main(){
    char* user;
    char process;
    int arrival;
    int duration;

    scanf("%*[^\n]"); //skipping header in the file.

    while(!feof(stdin))
    {
        scanf("%s\t%c\t%d\t%d\n", user, &process, &arrival, &duration);
        printf("%s\t%c\t%d\t%d\n", user, process, arrival, duration);
    }

    int x[5]; //<----- Causing the weird behaviour
}

我输入的文件是:

User    Process Arrival Duration
Jim     A       2       5
Mary    B       2       3
Sue     D       5       5
Mary    C       6       2

我遇到的问题是每当我声明 int x 数组时,无论是在代码的底部还是顶部,while 循环都会进入无限循环或程序段错误。

它将进入无限循环或段错误,具体取决于我声明数组的大小。例如,如果我声明数组的大小为 5,它会进入无限循环。但是,如果我声明数组的大小为 2,则会出现段错误。

我正在通过运行编译我的程序:

gcc -o myprog myprog.c

我通过运行管道文件:

cat jobs.txt | ./myprog

另外值得注意的是,程序在没有 int x 数组声明的情况下运行良好。

我完全不知道可能是什么问题,有什么想法吗?

【问题讨论】:

  • 您永远不会初始化 user 以指向有效的内存位置 -> scanf(在 while 循环内)调用 未定义的行为
  • 你会想看看Why is while ( !feof (file) ) always wrong?。请改用scanf 的返回值。
  • int x[2]; for 2,3,4 导致段错误。和x[5] 导致奇怪的行为

标签: c arrays segmentation-fault scanf infinite-loop


【解决方案1】:

由于指针user 未初始化,您的行为未定义。 user 必须指向能够存储所需内容的内存区域(例如,请参见 malloc())。

【讨论】:

  • 或者更好的是,设置一个合理的缓冲区大小,(例如char user[16] = "";)并检查scanf的返回以验证转换。
  • @DavidC.Rankin 可以确认此问题已解决!谢谢。我发现数组的声明以某种方式链接并暴露了奇怪的行为,这很奇怪,但我很高兴它已得到解决。
【解决方案2】:

正如您从另一个答案中发现的那样,您最初的问题在于 char *user;,它声明了一个 未初始化字符指针(例如,它不指向任何有效的内存块)。虽然您可以使用 malloccallocrealloc 进行动态分配,但对于您的情况而言,这可能过于复杂。

您真正需要的只是声明一个足以容纳用户名的字符数组。 16 chars 在这里绰绰有余。

下一个while (!feof(fp)) 几乎总是错误的。 (请参阅我评论中的链接)。 scanf 返回发生的有效转换次数。在您的情况下,使用"%s..%c..%d..%d"(4 个转换说明符),4 的返回将表明没有发生 匹配输入 失败。因此,不要使用 feof 检查,只需使用 scanf 返回,例如

    scanf("%*[^\n]"); //skipping header in the file.

    while (scanf ("%15s %15s %d %d", user, process, &arrival, &duration) == 4)
        printf ("%s\t%c\t%d\t%d\n", user, *process, arrival, duration);

(注意: 对于简化的scanf 格式字符串"%15s %15s %d %d"arrival 被声明为字符数组(见下文)并读取为字符串 (利用领先的空白跳过),然后使用*arrival 来挑选字符。如果您的输入空格分隔,这提供了一种更可靠的方式来读取您的输入而不是 *tab 分隔)

为避免在代码中使用幻数(例如16),如果您需要一个常量来表示数组中的最大字符数,请声明一个常量,例如

#define MAXC 16

int main (void) {
    char user[MAXC] = "", process[MAXC] = "";

注意:看起来"%15s %15s %d %d" 违反了这条规则,但遗憾的是,没有办法在scanf 中包含变量常量字段宽度说明符 防止在数组中读取超过 15 的字符——记住,你必须为最后一个字符——nul-terminating字符留出空间。

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

#include <stdio.h>

#define MAXC 16

int main (void) {
    char user[MAXC] = "", process[MAXC] = "";
    int arrival, duration;

    scanf("%*[^\n]"); //skipping header in the file.

    while (scanf ("%15s %15s %d %d", user, process, &arrival, &duration) == 4)
        printf ("%s\t%c\t%d\t%d\n", user, *process, arrival, duration);

    return 0;
}

使用/输出示例

$ ./bin/scanf_usr_arriv <dat/userprocarriv.txt
Jim     A       2       5
Mary    B       2       3
Sue     D       5       5
Mary    C       6       2

您可能还需要考虑使用 面向行的 输入函数(如 fgets)读取所有“输入行”,然后在结果缓冲区上调用 sscanf 以解析每个变量.这样做的好处是允许对正在读取的行进行单独验证,然后对解析行中的每个变量进行独立验证。

查看一下,如果您有任何问题,请告诉我。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-07
    相关资源
    最近更新 更多