在尝试声明字符串数组时C中的分段错误。

时间:2016-07-31 16:38:52

标签: c arrays string file segmentation-fault

" fruits.txt"是一个以数字开头的文本文件,比如n,后跟n个fruits的名称。我想将这n个名称存储到一个字符串数组中,但是在尝试声明该数组时,我得到了" Segmentation故障(核心转储)错误。

#include<stdio.h>
#include<stdlib.h>
int main()
{
    FILE *fp;
    int count;
    fp = fopen("fruits.txt", "r");
    if(fp == NULL)
    {
        printf("Can't open file!!");
        exit(0);
    }

    fscanf(fp, "%d", &count);
    printf("%d\n", count);
    char *fruits[count]; // This line is giving Segmentation fault.
    fclose(fp);

    return 0;
}  

3 个答案:

答案 0 :(得分:1)

根据您在阅读水果名称时提供存储(内存)的方式,您有两个选项(2)使用鲜为人知的'm'字段修饰符('a'对于较旧的实现,所以如果你正在使用windoze,请阅读文档以做出决定(或者只是尝试两者并看看哪个有效)。

您的代码存在的直接问题是使用fopen文件模式调用了"w"。只有当fruits.txt 已经过时时才会有效,因为"w"模式不会创建文件(如果它不存在)。正确的模式"w+"(或"a""a+",其中任何一个也会创建一个不存在的文件)。只需将模式更改为"w+",即可将水果信息写入新创建的fruits.txt

在某些MS编译器上使用VLA(可变长度数组)的问题可能会出现问题。您只需要查看您的版本和更改日志或文档(或者只是尝试阅读错误或警告。更糟糕的情况是,您可以使用指针指向类型,或者一个足够大小的静态数组。鉴于你使用fscanf,这种方法将在下面继续。具体来说:

    char *fruits[count]; /* here you delare an array of pointers to type char   */
                        /* utilizing a variable length array. some MS compiler */
                        /* version do not handle VLA's, VS13/VS15 should be ok */

    for (i = 0; i < count; i++)       /* read & allocate for each array element */
        if (fscanf (fp, " %ms", &fruits[i]) != 1) {    /* m allocates, a for ms */
            fprintf (stderr, "error: read/allocation for fruits[%d] failed.\n", i);
            exit (EXIT_FAILURE);
        }

(注意" %ms"上面使用的格式字符串,它注意到接受分配块的指针必须是指向<{1}}的指针指针 - 分配/文件指针的地址,例如char *

代码的其余部分应该非常熟悉,除非现在代码中的所有输入和其他关键点都已经验证,方法是检查由任何函数提供的&fruits[i]以确保没有错误条件存在直到那一点,并且没有一个是由有问题的操作引起的。这是您对代码操作有信心的唯一方法。养成这个习惯。

把它放在一起,你可以想出以下内容。

return

代码的基本编译字符串(在#include <stdio.h> #include <stdlib.h> int main (void) { int count, i; FILE *fp; if (!(fp = fopen ("fruits.txt", "w+"))) { /* "w+" required to create file */ fprintf (stderr, "error: file open failed 'fruits.txt'.\n"); exit (EXIT_FAILURE); } fputs ("4 Apple Banana mango berry", fp); /* write string to fruits.txt */ fclose(fp); if (!(fp = fopen ("fruits.txt", "r"))) { /* validate file open for reading */ fprintf (stderr, "error: file open failed 'fruits.txt'.\n"); exit (EXIT_FAILURE); } if (fscanf (fp, " %d", &count) != 1) { /* read all fruir from fruits.txt */ fprintf (stderr, "error: in read of value from 'fruits.txt'.\n"); exit (EXIT_FAILURE); } printf ("\n Quantity read from 'fruits.txt' is '%d'.\n\n", count); char *fruits[count]; /* here you delare an array of pointers to type char */ /* utilizing a variable length array. some MS compiler */ /* version do not handle VLA's, VS13/VS15 should be ok */ for (i = 0; i < count; i++) /* read & allocate for each array element */ if (fscanf (fp, " %ms", &fruits[i]) != 1) { /* m allocates, a for ms */ fprintf (stderr, "error: read/allocation for fruits[%d] failed.\n", i); exit (EXIT_FAILURE); } fclose(fp); /* close file for the last time */ for (i = 0; i < count; i++) /* output array */ printf (" fruit[%d] : %s\n", i, fruits[i]); for (i = 0; i < count; i++) /* free allocated memory */ free (fruits[i]); return 0; } 中)和abc.c中的可执行文件可能是:

bin/abc

示例使用/输出

$ gcc -Wall -Wextra -o bin/abc abc.c -std=gnu11

在任何动态分配内存的代码中,对于分配的任何内存块,您都有2个职责:(1)始终保留指向起始地址的指针内存块,(2)当不再需要时,它可以释放

必须使用内存错误检查程序,以确保您没有在已分配的内存块之外/之外写入,尝试读取或基于未初始化的值跳转,最后确认您已拥有释放了你分配的所有内存。对于Linux $ ./bin/abc Quantity read from 'fruits.txt' is '4'. fruit[0] : Apple fruit[1] : Banana fruit[2] : mango fruit[3] : berry 是正常的选择。

valgrind

始终确认释放所有堆块 - 不可能泄漏并且同样重要错误摘要:0个上下文中的0个错误。 (虽然注意:某些操作系统没有提供足够的记忆排除文件(排除系统和操作系统内存不被报告为正在使用的文件),这将导致$ valgrind ./bin/abc ==30980== Memcheck, a memory error detector ==30980== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==30980== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info ==30980== Command: ./bin/abc ==30980== Quantity read from 'fruits.txt' is '4'. fruit[0] : Apple fruit[1] : Banana fruit[2] : mango fruit[3] : berry ==30980== ==30980== HEAP SUMMARY: ==30980== in use at exit: 0 bytes in 0 blocks ==30980== total heap usage: 10 allocs, 10 frees, 1,561 bytes allocated ==30980== ==30980== All heap blocks were freed -- no leaks are possible ==30980== ==30980== For counts of detected and suppressed errors, rerun with: -v ==30980== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1) 报告有些记忆尚未被释放(尽管事实上你完成了自己的工作,并且释放了你分配并在你控制之下的障碍。

为了解决一个简单的问题需要付出很多,而且我们甚至没有谈到读取文件的首选方式本来就是{{1}一次阅读和整行}或POSIX valgrind,然后解析来自读取行的各个水果,或标记化fgets。花时间消化cocde并自己回答你必须查找的两个编译器问题(1)支持getline的VLA和(2)strtok或{{1}你的编译器使用它来分配。

答案 1 :(得分:0)

使用fscanf时,您必须始终检查返回值。只有当函数返回一个成功的值时(请阅读fscanf文档),程序才能继续。

答案 2 :(得分:0)

我尝试过以下代码并找到了工作。 这里的区别首先是我创建了文件并填充了其中的内容。

如果我没有填写文件中的内容,那么我会得到分段错误(不是在所有编译器上)。 所以在你的情况下,似乎fscanf()正在阅读一些垃圾并返回一些大垃圾号码。 正如其他人也建议的那样,请检查fscanf返回的内容。如果它返回-1,表示文件中没有找到任何内容。

Component.onCompleted: {
    listView.currentIndex = -1;
}

这打印4没有错误/错误。