对将字符串获取到二维数组感到困惑

confused about get strings to a two dimensional array

我正在尝试编写一个简单的 C 程序来读取用户输入,并将其写回 stdout。我使用 gets()stdin 获取输入行(这是我的老师要求我做的)。我可以阅读行,但我无法摆脱 while 循环。例如,如果我输入 10 行,当我 运行 程序时,我发现我的代码正在读取第 11 行,这是空的,并且没有退出循环。 这是我的代码:

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

#define MAX_LINE_SIZE 100
#define INITIAL_BUFFER_SIZE 16

char** IncreaseBuffer(char** buffer, int* buffer_size);
void PrintLines(char** lines, int total_lines);
int MinLineIndex(char** buffer, int i, int j);
void SwapLines(char** buffer, int i, int j);

int main(){
    char** buffer;
    int* buffer_size;
    int* line_size;
    int s,t;
    int i=0;
    char* q;
    s = INITIAL_BUFFER_SIZE;
    buffer_size = &s;
    t = MAX_LINE_SIZE;
    line_size = &t;
    printf("Get lines:" );
    buffer = (char**)malloc(sizeof(char*)**buffer_size);`enter code here`
    buffer[0] = malloc(sizeof(char)**line_size);
    while(1){
        q = gets(buffer[i]);
        *buffer[i] = *q;
        if(buffer[i] == NULL){
            i = i - 1;
            char null = '[=10=]';
            buffer[i+1] = &null;
            *buffer[i+1] = '[=10=]';
            break;
        }
        i++;
        buffer[i] = malloc(sizeof(char)**line_size);
        printf("%d%s\n",i,buffer[i]);
    }
}

我做错了什么?

首先,最好使用 fgets 函数而不是 gets 函数,因为它的错误而被弃用。有关详细信息,请参阅 this question
之后,您必须使用 EOF (ctrl + D) 来停止循环,因为 fgets 读取仅包含换行符的空行。在您的代码中,您使用 *q 这是错误的,因为根据 gets 手册:

gets() returns s on success, and NULL on error or when end of file occurs while no characters have been read.

所以不能取消引用 q。我将您的代码更改为以下代码,它在我的计算机上运行良好:

int main(){
    char **buffer;
    int *buffer_size;
    int *line_size;
    int s,t;
    int i = 0;
    char *q;

    s = INITIAL_BUFFER_SIZE;
    buffer_size = &s;

    t = MAX_LINE_SIZE;
    line_size = &t;

    printf("Get lines:\n");
    buffer = malloc(sizeof(char *) * *buffer_size);
    buffer[0] = malloc(sizeof(char) * *line_size);
    while (1) {
        q = gets(buffer[i]);
        if (q == NULL) {
            i = i - 1;
            *buffer[i + 1] = '[=10=]';
            break;
        }
        i++;
        buffer[i] = malloc(sizeof(char)**line_size);
        printf("%d%s\n", i, buffer[i]);
    }
}

虽然您可以通过多种方式阅读未知数量的行,但您对声明指针的方法和坚持有点麻烦。无需将 buffer_sizeline_sizeq 声明为指针。可以为 st 赋予更具描述性的名称以帮助提高可读性。

首先,请理解,您并不是在下面创建一个二维数组。您正在使用 pointer-to-pointer-to-char 来声明一个 array of pointers ,您正在为其分配已分配块的地址保存每行输入的内存。您可以使用数组索引来访问每个单独的指针值(例如 buffer[0] 表示第一个指针,依此类推)。

由于您使用 buffer 声明了 INITIAL_BUFFER_SIZE 个指针,如果您将 s 重命名为 ptrcount,您可以使用 ptrcount 进行跟踪到当前分配的指针数,并在达到计数时 realloc 。使用 t,如果您不重新分配每行的字符数,则没有理由声明一个单独的变量来跟踪 MAX_LINE_SIZE,您可以简单地将其用作常量。

这通常很有用,特别是在您想要在为行分配存储之前检查从用户读取的内容的情况下,使用静态缓冲区进行初始读取,比如:

char buf[MAX_LINE_SIZE] = {0};

这允许您在 buffer[i] 中分配 space 并将字符串复制到其最终位置之前检查空行,以及删除试验 '\n' . (注意:strdup 可用于分配和复制到 buffer[i]

将各个部分放在一起,您可以完成 buffer 的填充(和释放)——如果您阅读超过 INITIAL_BUFFER_SIZE 行,则可以重新分配它,类似于以下内容:

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

enum { INITIAL_BUFFER_SIZE = 16, MAX_LINE_SIZE = 100 };

int main (void) {

    char buf[MAX_LINE_SIZE] = {0};
    char **buffer;
    int ptrcount, nlines;
    int i = 0;

    ptrcount = INITIAL_BUFFER_SIZE;

    printf ("Get lines:\n");    /* allocate ptrcount pointers */
    if (!(buffer = malloc (sizeof *buffer * ptrcount))) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        exit (EXIT_FAILURE);
    }

    while (fgets (buf, MAX_LINE_SIZE, stdin)) {
        size_t len = strlen (buf);
        if (len < 2) continue;      /* skip empty lines     */
        buf[len - 1] = 0;           /* strip trailing '\n'  */
        buffer[i++] = strdup (buf); /* allocate/copy buf    */

        if (i == ptrcount) {  /* pointer limit reached, realloc */
            void *tmp = realloc (buffer, 2 * ptrcount * sizeof *buffer);
            if (!tmp) {
                fprintf (stderr, "error: virtual memory exhausted.\n");
                break;
            }
            buffer = tmp;   /* assign tmp to buffer */
            ptrcount *= 2;  /* increase limit count */
        }
    }
    nlines = i;

    for (i = 0; i < nlines; i++) /* print the array */
        printf (" line[%2d] : %s\n", i, buffer[i]);

    for (i = 0; i < nlines; i++) /* free allocated memory */
        free (buffer[i]);
    free (buffer);

    return 0;
}

(注意: 因为 strdup 分配内存,你应该检查它的结果不是 NULL,就像你对 malloc或者realloc,留给你)

示例输入

$ cat data.txt
a quick brown fox jumps over the lazy dog.
my dog has fleas.
my cat does too.
and the fleas are colored.
red, white, and blue.

例子Use/Output

$ ./bin/chararray <data.txt
Get lines:
 line[ 0] : a quick brown fox jumps over the lazy dog.
 line[ 1] : my dog has fleas.
 line[ 2] : my cat does too.
 line[ 3] : and the fleas are colored.
 line[ 4] : red, white, and blue.

Memory/Error检查

如果您分配内存,您应该始终使用 valgrind 或用于 OS 的类似内存错误检查工具检查其使用情况。您应该确认所有分配的内存都已释放并且没有内存错误。它使用简单,所以没有理由不跳过这一步。

$ valgrind ./bin/chararray < data.txt
==21344== Memcheck, a memory error detector
==21344== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==21344== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==21344== Command: ./bin/chararray
==21344==
Get lines:
 line[ 0] : a quick brown fox jumps over the lazy dog.
 line[ 1] : my dog has fleas.
 line[ 2] : my cat does too.
 line[ 3] : and the fleas are colored.
 line[ 4] : red, white, and blue.
==21344==
==21344== HEAP SUMMARY:
==21344==     in use at exit: 0 bytes in 0 blocks
==21344==   total heap usage: 6 allocs, 6 frees, 255 bytes allocated
==21344==
==21344== All heap blocks were freed -- no leaks are possible
==21344==
==21344== For counts of detected and suppressed errors, rerun with: -v
==21344== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)