【问题标题】:How to use fgets() in 2d-arrays (multiple dimension arrays)?如何在二维数组(多维数组)中使用 fgets()?
【发布时间】:2023-04-10 21:49:01
【问题描述】:
#include <stdio.h>
#include <stdlib.h>

void *salloc(int x){
    char **pointer;
    int i;
    pointer = malloc(sizeof(char)*x);
    if(pointer == NULL){
     exit(-1);
    }
    for(i=0; i<x; i++){
        pointer[i] = malloc(sizeof(char) * 20);
        if(pointer[i] == NULL){
            exit(-1);
        }
    }
return pointer;
}

void Input(int value, char **array){
    for(i = 0; i < value; i++){
        printf("%d ----\n", i);
        fgets(array[i], 20, stdin);
        printf("%d ----\n", i);
    }
}

int main(int argc, char *argv[]){
    char **array;
    int value = 2;
    array = salloc(value);
    Input(value, array);
    return 0;
}

一般的想法,可能是我错过了一些语法。 所以我想读一个带空格的字符串。如果我为 value 2 运行它,它将打印:

0 ----

0 ----

1 ----

“一些字符串”

我按回车后它崩溃了。 如果我使用 value 1 执行此操作: 它立即崩溃。 但是,如果我将 fgets() 替换为:

    scanf("%s", array[i]);

它有效(除了空格)。

那么 fgets() 如何在二维数组中工作?

因为我让它在一维数组中工作。并且由于某种原因,当数组只有 2 行时,我可以从第 2 行打印一维数组,所以它应该只能从第 0 行和第 1 行打印,对吧?

【问题讨论】:

  • 你能发布array的定义/声明吗?
  • 没有二维数组。提供minimal reproducible example 并说明您的实际问题。
  • 这里还有一个胖错误:malloc(sizeof(char)*x);。应该是sizeof(*pointer)sizeof(char*)

标签: c arrays multidimensional-array fgets


【解决方案1】:

这是一个演示程序,展示了如何将fgets 用于二维数组。

#include <stdio.h>

#define N   5
#define M   10

int main( void ) 
{
    char lines[N][M];
    size_t n = 0;

    while( n < N && fgets( lines[n], sizeof( *lines ), stdin ) != NULL ) ++n;

    for ( size_t i = 0; i < n; i++ ) puts( lines[i] );

    return 0;
}

如果以输入为例

One
Two
Three
Four
Five

那么程序输出将是相同的

One
Two
Three
Four
Five

【讨论】:

  • while 循环中的 != NULL 部分是什么意思?因为这可能是解决方案。
  • 否则它只是冲到最后一次迭代并等待一个字符串被读入?
  • @LifeisHard 当数组的所有元素都被填充或遇到一些错误(例如文件结尾)时,循环结束。
  • @LifeisHard 一点也不,我们初学者应该互相帮助。:)
  • 是否也可以使用 char* lines[N] ??
【解决方案2】:

当你这样做时

pointer = malloc(sizeof(char)*x);

您只分配x 字符(即字节),而不是指向字符的指针。改为

pointer = malloc(sizeof(char*)*x);

如果不进行更改,您可能会越界并出现未定义的行为。这正是您的代码中发生的情况,您仅分配 两个字节 来存储两个指针,而单个指针是四个或八个字节,因此您甚至没有为单个指针分配足够的内存指针。

未定义的行为是导致崩溃的常见原因,但有时它也可能似乎起作用。

【讨论】:

    【解决方案3】:

    还请注意,当使用fgets(或任何面向行的输入函数)进行输入时,fgets 将读取并包含 '\n' 在每行读取的末尾。您应该执行 2 个额外的测试/操作。 (1) 您应该测试fgets 读取的最后一个字符实际上是'\n' 字符。如果不是,则表明您的输入已被fgets 截断,其长度为您在fgets 的第二个参数中指定的长度,并且该行的其他字符仍未读取。如果没有这个检查和一些处理超过指定宽度的行的方法,你接下来调用fgets 将读取 current 行的剩余字符作为你的 next 行输入。

    (2) 您应该删除 fgets 包含的换行符,以防止您的字符串在末尾包含嵌入的 '\n' 字符。 (如果您只是从行中解析数字而不是将其存储为字符串,则可以通过几种不同的方式进行处理)。但是,对于一般情况,您使用strlen 来定位字符串的结尾,然后用nul-terminating 字符覆盖'\n'

    除上述之外,仅在读取一行数据后为array 中的每个单独指针分配内存可能更有意义,以防止代码中过度分配空间。由于您指定每个字符串的分配大小将为20,因此可以使用该大小的简单字符缓冲区来获取输入,并且在确认输入后,您可以在array 中为该行分配存储空间。包含检查和分配在input 中的函数的简短示例是:

    #define MAXC 20  /* max chars per read  */
    ...
    void Input (int value, char **array)
    {
        int i = 0;
        size_t len = 0;
        char buf[MAXC] = {0};
    
        while (i < value && fgets (buf, MAXC, stdin)) {
            printf ("%d ----\n", i);
    
            len = strlen (buf);     /* get length */
            if (len + 1 == MAXC && buf[len-1] != '\n') /* validate read */
                fprintf (stderr, "warning: chars exceed MAXC, line[%d]\n", i);
            else
                buf[--len] = 0;     /* strip '\n' */
    
            printf ("%d ----\n", i);
            array[i++] = strdup (buf);  /* allocate/copy */
        }
    }
    

    最后,为什么选择void作为函数类型?如果值成功读入数组,为什么不返回读入array 的值的数量,否则返回0。这至少可以指示您读取的成功或失败,并提供一种将分配的行数返回给调用函数的方法。

    包含调整后分配、必要检查和有用返回类型的代码示例如下:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define MAXC 20
    
    void *salloc (int x)
    {
        char **pointer;
    
        pointer = malloc (sizeof *pointer * x);
        if (pointer == NULL)
            exit(-1);
    
        return pointer;
    }
    
    int input (int value, char **array)
    {
        int i = 0;
        size_t len = 0;
        char buf[MAXC] = {0};
    
        while (i < value && fgets (buf, MAXC, stdin)) {
            printf ("%d ----\n", i);
    
            len = strlen (buf);     /* get length */
            if (len + 1 == MAXC && buf[len-1] != '\n') /* validate read */
                fprintf (stderr, "warning: chars exceed MAXC, line[%d]\n", i);
            else
                buf[--len] = 0;     /* strip '\n' */
    
            printf ("%d ----\n", i);
            array[i++] = strdup (buf);  /* allocate/copy */
        }
        return i;
    }
    
    int main (int argc, char *argv[]) {
        int i, nlines, value = argc > 1 ? atoi (argv[1]) : 2;
        char **array;
    
        array = salloc (value);
    
        if (!(nlines = input (value, array)))   /* validate input */
            return 1;
    
        for (i = 0; i < nlines; i++)    /* print input  */
            printf (" array[%2d] : %s\n", i, array[i]);
    
        for (i = 0; i < nlines; i++)    /* free memory */
            free (array[i]);
        free (array);
    
        return 0;
    }
    

    测试输入文件

    以下是第 1 行(第 2 行)超过 S 20 个字符的测试输入文件:

    $ cat dat/captnjack.txt
    This is a tale
    Of Captain Jack Sparrow
    A Pirate So Brave
    On the Seven Seas.
    

    示例输出

    $ ./bin/readarray 10 <../dat/captnjack.txt
    0 ----
    0 ----
    1 ----
    warning: chars exceed MAXC, line[1]
    1 ----
    2 ----
    2 ----
    3 ----
    3 ----
     array[ 0] : This is a tale
     array[ 1] : Of Captain Jack Spa
     array[ 2] : rrow
     array[ 3] : A Pirate So Brave
     array[ 4] : On the Seven Seas.
    

    不要忘记使用像valgrind 这样的内存错误检查程序来验证您分配的内存的使用情况,并确保在不再需要它时释放它。如果您有任何其他问题,请告诉我。

    【讨论】:

      猜你喜欢
      • 2018-01-27
      • 2019-07-09
      • 1970-01-01
      • 2019-07-29
      • 1970-01-01
      • 2011-08-15
      • 2017-08-27
      • 2020-07-30
      • 1970-01-01
      相关资源
      最近更新 更多