如何在二维数组(多维数组)中使用 fgets()?

How to use fgets() in 2d-arrays (multiple dimension arrays)?

#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 ----

"some string"

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

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

有效(空格除外)。

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

因为我让它在一维数组中工作。出于某种原因,当数组只有 2 行时,我可以从第 2 行打印一维数组,所以它应该只能从第 0 行和第 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

当你这样做时

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

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

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

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

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

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

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

除上述之外,仅在读取一行数据后才为 array 中的每个单独指针分配内存可能更有意义,以防止在您的 space 中过度分配代码。由于您指定每个字符串的分配大小为 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作为功能类型?如果值成功读入数组,为什么不 return 读入 array 的值的数量,否则 0 。这将至少允许一些指示读取成功或失败,并提供一种方法 returning 分配回调用函数的行数。

您的代码包含调整后的分配、必要的检查和有用的 return 类型的示例是:

#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 行)超过 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 这样的 内存错误检查 程序来验证您对您分配的内存的使用,并确保您在分配内存时已释放它不再需要。如果您还有其他问题,请告诉我。