使用fgetc()读取C中的数组

时间:2016-07-27 19:47:55

标签: c arrays pointers fgetc

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

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

2 个答案:

答案 0 :(得分:2)

char *ch中,ch是一个指针,它的值应该是一个有效的内存位置,用于在该内存位置存储数据。在使用该指针之前,必须使用malloc()realloc()(在最初ch指向NULL时使用realloc)动态地将内存分配给ch。否则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在下面的评论中所说:

  

为每个读取的字符调用realloc是非常浪费的。   分配128,256,... 1024(或一些合理预期的数量   读取,直到达到限制,然后重新分配一些理智   额外的字符数,然后重复......

答案 1 :(得分:2)

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

实施是直截了当的,但有一些细微之处需要注意。重新分配时,请始终将realloc的结果分配给临时指针,以便在继续之前验证 realloc成功。为什么?如果realloc失败,则返回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)当不再需要时,它可以释放

必须使用内存错误检查程序,以确保您没有在已分配的内存块之外/之外写入,尝试读取或基于未初始化的值跳转,最后确认您已释放所有内存你分配的记忆。 (对于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个错误(注意:在某些操作系统上,valgrind会由于内存排除文件不完整而无法报告0