在 C 中使用 fgetc() 读入数组

Using fgetc() for reading into an array in C

我想使用 fgetc() 并将输入字符放入某个数组中,例如 char *ch。但显然我不能将字符(如 'M')放入数组中,它必须像 "M",我的意思是像字符串一样使用双引号。
这是我的代码,在获得用户输入后我得到 Segmentation fault (core dumped)
所以我想知道是否有任何方法或技巧可以做到这一点。
提前致谢。 :)

void main(){
    char *ch;
    int i = 0;
    while((ch[i] = fgetc(stdin)) != '\n'){
        i++;
    }
    return;
}

char *ch中,ch是一个指针,它的值应该是一个有效的内存位置,以便在该内存位置存储数据。在使用该指针之前,必须使用 malloc()realloc() 动态分配内存给 ch(如果 ch 最初指向 NULL,则使用 realloc)。否则 ch 可能包含垃圾值并且访问该内存位置会导致 Segmentation fault。我对您的程序进行了更改,它可能会对您有所帮助。

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

int main(){
    char *ch = NULL;
    int i = 0;
    ch = realloc(ch, i+1);
    while((ch[i] = fgetc(stdin)) != '\n'){
        ch = realloc(ch, i+1);
        i++;
    }
    ch[i] = 0;
    puts(ch);
    free(ch);
    return 0;
}

正如 David C. Rankin 在下面的评论中所说:

Calling realloc for every character read is horribly wasteful. Allocate 128, 256, ... 1024 (or some reasonably expected number of characters) read until the limit is reached and then realloc some sane additional number of characters, then repeat...

虽然您可以 realloc 读取每个字符,但这是一种非常低效的内存分配方式。与将字符分配给数组元素相比,对 malloc 的调用相对昂贵。最好分配一些合理预期的字符数,阅读直到达到限制,此时 realloc(更新限制),然后重复直到 EOF(或您选择的任何终止)。

实施很简单,但有一些细微之处需要注意。重新分配时,始终将 realloc 的结果分配给 临时指针 以便您可以 验证 realloc 在继续之前成功.为什么?如果 realloc 失败,则 returns NULL。如果您只是盲目地将结果分配给您的原始数组,并且 realloc 失败,(1)您刚刚丢失了指向原始数组的指针; (2) 您刚刚造成了内存泄漏,因为原始块未被触及——它没有被释放或移动。

考虑到这一点,您可以将字符读入数组,根据需要重新分配,直到 EOF 使用如下内容:

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

#define NCHAR 1024  /* must be at least 1 */

int main (int argc, char **argv) {

    int c;
    size_t n = 0, nchar = NCHAR;
    char *arr = malloc (sizeof *arr * nchar);
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    if (!arr) { /* validate memory allocation succeeded */
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return 1;
    }

    while ((c = fgetc (fp)) != EOF) {  /* for each char in file */
        arr[n++] = c;       /* assign char to array */

        if (n == nchar) {   /* realloc preserving space for nul */
            void *tmp = realloc (arr, nchar + NCHAR);
            if (!tmp) {           /* validate realloc succeeded */
                fprintf (stderr, "realloc() error: memory exhausted.\n");
                break; /* break read loop, using existing 'arr' */
            }
            arr = tmp;     /* assign reallocated pointer to arr */
            nchar += NCHAR;        /* update the value of nchar */
        }
    }
    arr[n] = 0;                  /* affirmatively nul-terminate */

    if (fp != stdin) fclose (fp);    /* close file if not stdin */

    for (size_t i = 0; i < n; i++)   /* output arr */
    putchar (arr[i]);

    free (arr);   /* free allocated memory */

    return 0;
}

注意:上面的,失败时,读取循环退出,保留arr中读取到那个时间点的所有字符。)

最后,在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指针到内存块的起始地址,因此,(2) 它可以在不再需要时释放

您必须使用内存错误检查程序来确保您没有写入 beyond/outside 您分配的内存块,试图读取或基于未初始化的值进行跳转,并最终确认你已经释放了你分配的所有内存。 (对于Linux valgrind是正常的选择。)

一个简单的示例(将 NCHAR 设置为 2 以强制进行多次重新分配)是:

$ valgrind ./bin/fgetc_realloc <../dat/captnjack.txt
==19710== Memcheck, a memory error detector
==19710== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==19710== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==19710== Command: ./bin/fgetc_realloc
==19710==
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
==19710==
==19710== HEAP SUMMARY:
==19710==     in use at exit: 0 bytes in 0 blocks
==19710==   total heap usage: 39 allocs, 39 frees, 1,560 bytes allocated
==19710==
==19710== All heap blocks were freed -- no leaks are possible
==19710==
==19710== For counts of detected and suppressed errors, rerun with: -v
==19710== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)

您想查找所有堆块都已释放 -- 没有泄漏是可能的错误摘要:0 个上下文中的 0 个错误(注意:在某些 OS 上,由于内存排除文件不完整,valgrind 不会报告 0