【问题标题】:C easy code with filling array带有填充数组的 C 简单代码
【发布时间】:2017-04-07 03:34:41
【问题描述】:

我正在尝试使用名为data.txt 的文件填充数组。我不知道代码有什么问题。我得到分段错误:11 错误。

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

void input(int arr[]){
    FILE* f;
    int x, i=0;
    f= fopen("data.txt","r");
    while (arr[i] != EOF){
        fscanf(f,"%d",&x);
        arr[i] = x;
        i++;
    }
    fclose(f);
}

int main(){
    int arr[50];
    input(&arr[50]);
    printf("%d", arr[0]);
}

【问题讨论】:

  • arr[i] 永远不会等于 EOF,因此您将永远循环并最终出现段错误。您还将数组末尾的第一个元素的地址传递给input(),因此您尝试写入从一开始就不属于您的内存。
  • input(&amp;arr[50]) 应该是input(arr)
  • 在使用文件指针之前,你没有检查你是否成功打开了输入文件。这是导致崩溃的一个简单原因(并且很容易修复 - 始终检查来自 fopen() 或任何其他类似打开的函数的返回值)。您不检查来自fscanf() 的返回值;这也是一个错误。 (见How do we check the return values from scanf()?
  • 语句:while (arr[i] != EOF) 在循环的每次迭代中都在查看arr[] 中尚未初始化的条目!建议将while() 和下一条语句替换为:while( 1 == fscanf(f,"%d",&amp;x) ) 并且循环对可以读取的整数数量没有限制,但实际数组仅声明了 50 个条目,因此 while() 语句应该是:while( i&lt;50 &amp;&amp; 1 == fscanf(f,"%d",&amp;x) )
  • 在 C 中,数组偏移量/索引从 0 开始并持续到(数组的长度 -1),并且此语句:input(arr[50]); 正在传递(数组末尾之后的值) ) 到函数input()。该行应为:input(arr);

标签: c arrays scanf


【解决方案1】:

您正在将数字读入x(您将其复制到arr[i]),然后将arr[i+1]EOF 进行比较。这不是必须的。

试试这个

while (fscanf(f, "%d", &arr[i]) == 1) 
    i++;

但这会违反许多安全限制。如果i 大于某个限制,最好也尽早进行绑定检查和中断,但该限制应该传递给函数。

另一个错误是您如何将参数传递给输入。通过input(arr) 而不是input(&amp;arr[50])。如果你想使用&amp;,请使用input(&amp;arr[0])

【讨论】:

  • 哦——当然不是! (除了缺少f 作为fscanf() 的第一个参数...)例如,为什么不while (fscanf(f, "%d", &amp;arr[i]) == 1) i++;?无限循环通常很糟糕。有时是必要的,但这不是那些时候之一。是的,如果 fscanf() 在某个时间返回 0,您将终止,但如果它返回 EOF,您将无限期地旋转。
  • @JonathanLeffler 我希望这会更好吗?
  • 好吧,我想是的。我还是更喜欢while (fscanf(f, "%d", &amp;arr[i]) == 1) i++; 作为循环。我正在跳过数组边界检查,因为大小没有传递给函数。这样做有好处。一种是当转换失败时i 不会增加,因此您不必担心循环后是否减少i 以报告创建了多少条目。它也不需要单独的状态变量。
  • 再次,我为 0 和 EOF 而不是 1 编写了单独的条件,以便更清楚我们正在检查的内容。由于OP在他的问题中写了EOF。
  • 好的;意见(你和我的)不同。我正在测试转换的值的数量是否与格式字符串中列出的转换规范的数量相匹配。您正在检查两种状态。我不需要一个变量来保存状态(虽然如果我想检查,很容易添加一个:int n_cvt; while ((n_cvt = fscanf(f, "%d", &amp;arr[i])) == 1) i++;)。请注意,如果要扫描 7 个值,我只需要更改格式字符串、变量列表以及值 1 到 7。您必须考虑如何处理返回值 EOF、0、1、 2、3、4、5、6。太辛苦了!
【解决方案2】:

这将更接近我的代码版本:

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

static int input(int size, int arr[])
{
    const char file[] = "data.txt";
    FILE *f = fopen(file, "r");
    if (f == NULL)
    {
        fprintf(stderr, "Failed to open file '%s' for reading\n", file);
        exit(EXIT_FAILURE);
    }

    int i;
    for (i = 0; i < size && fscanf(f, "%d", &arr[i]) == 1; i++)
        ;

    fclose(f);
    return i;
}

int main(void)
{
    int arr[50];
    int num = input(50, arr);
    for (int i = 0; i < num; i++)
        printf("%d: %d\n", i, arr[i]);
    return 0;
}

在函数前使用static是为了平息-Wmissing-prototypesmain() 函数告诉input() 函数数组中有多少元素,因此input() 函数可以避免溢出缓冲区(堆栈溢出,不少于)。 input() 函数告诉main() 函数它读取了多少值,因此main() 函数不会访问未初始化的数据。对关键的函数调用进行错误检查——fopen()fscanf()

代码在运行 macOS Sierra 10.12.4 的 Mac 上使用 GCC 6.3.0 和下面的命令行(源文件为 rf19.c)干净地编译:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes \
>     -Wstrict-prototypes -Wold-style-definition rf19.c -o rf19
$

我生成了一个数据文件,其中包含 23 个 10 到 99 之间的随机整数,输出为:

$ ./rf19
0: 48
1: 33
2: 77
3: 42
4: 78
5: 51
6: 85
7: 56
8: 55
9: 56
10: 16
11: 38
12: 39
13: 52
14: 34
15: 63
16: 20
17: 23
18: 23
19: 19
20: 39
21: 44
22: 71
$

这不是非常有用的信息,但总比没有好。

代码仍然存在缺陷,我不打算修复 - 有些比其他的更严重。例如,文件名是固定的——这是不行的。 input() 函数中的代码出错退出;那不一定可以。它会在标准错误上生成错误消息——这比标准输出要好,但在 GUI 应用程序中不是一个好主意。输出浪费了大量的横向空间;使用显示的数据,每个输出行可以获得 10 个值(每行将使用大约 70 个字符),但打印更复杂,所以我没有展示它。该代码将 EOF 和数据中的单词或标点字符视为相同;这可能会或可能不重要,具体取决于您的应用程序。输入在第 50 次输入后停止;也许您需要知道是否有更多条目可供阅读。如果没有指定文件,我可能会将命令行参数作为文件名处理,或者处理标准输入 - Unix 'filter command' 习惯用法。我可能会做一些比仅打印前 50 个值更令人兴奋的事情。我可能会将文件读取代码与文件打开/关闭代码放在一个单独的函数中。

【讨论】:

    猜你喜欢
    • 2011-05-29
    • 2012-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-23
    相关资源
    最近更新 更多