使用malloc时出错

时间:2015-07-17 06:58:16

标签: c malloc

我将char ** inputmain()传递给processInExp()函数,然后我再次将其从processInExp()函数传递给getInput()函数,以便在阅读时动态分配它通过文件。

内部getInput()函数input在检查时被正确分配内存,但在in processInExp()中使用它时会遇到运行时错误。可能是什么问题?

以下是我的代码:

int getInput(char ** input, const char * fileName)
{
    int numInput = 0;
    int i, j;
    char c;
    char tempInput[100];
    FILE * pFile;
    if((pFile = fopen(fileName, "r")) == NULL)
    {
        printf("Cannot read file %s\n", fileName);
        system("PAUSE");
        exit(1);
    }
    while(!feof(pFile))
    {
        c = fgetc(pFile);
        if(c == '\n') ++numInput;

    }
    /* printf("%d\n", numInput); */
    input = (char**)malloc(numInput * sizeof(char*)); /* #2 MALLOC input */
    rewind(pFile);
    for(i = 0; !feof(pFile); ++i)
    {
        fscanf(pFile, "%[^\n]%*c", tempInput);
        /* printf("%s\n", tempInput); */
        input[i] = (char*)malloc((strlen(tempInput) + 1) * sizeof(char)); /* #3 MALLOC input[] */
        strcpy(input[i], tempInput);
        /* printf("%s\n", input[i]); */ /* #4 PRINT OUT PERFECTLY */
        memset(tempInput, 0, sizeof(tempInput));
    }
    fclose(pFile);
    return numInput;
}
void processInExp(char ** input, char ** output, const char * fileName)
{
    int numFormula;
    int i;

    numFormula = getInput(input, fileName); /* #1 PASSING input */
    /* printf("%s\n", input[0]); */ /* #5 RUNTIME ERROR */
    output = (char**)malloc(numFormula * sizeof(char*));
    system("PAUSE");

    for(i = 0; i < numFormula; ++i)
    {
        convertIntoPost(input[i], output[i]);
        printf("%d. %s -> %s", (i + 1), input[i], output[i]);
    }

}

4 个答案:

答案 0 :(得分:3)

C使用pass-by-value进行函数参数传递。因此,从函数getInput()内部,您无法更改变量input并期望将更改反映在实际参数中,并传递给函数。为此,您需要传递指针变量,就像在这种情况下,您需要执行

   int getInput(char *** input, const char * fileName) { //notice the extra *

需要像

一样调用它
   char ** inp = NULL;
   getInput(&inp, ..........);

然后,getInput()将能够在函数内部*input分配内存,并将其反映到inp

否则,从getInput()返回后,实际参数仍然未初始化,并且进一步使用(在您的情况下,for函数中的processInExp()循环)将导致undefined behaviour

那就是说两件更重要的事情需要注意,

  1. malloc() C 'urlManager' => [ 'enablePrettyUrl' => true, 'showScriptName' => false, 'rules' => [ '<controller:\w+>/<id:\d+>' => '<controller>/view', '<controller:\w+>/<action:\w+>/<id:\d+>' => '<controller>/<action>', '<controller:\w+>/<action:\w+>' => '<controller>/<action>', ], ], 家人的回复价值see why not to cast
  2. 检查Why is while ( !feof (file) ) always wrong?

答案 1 :(得分:3)

虽然其他人已经指出了价值转移的问题,但还有另一个问题可以发生学习。无需预先读取文件以确定字符或行的数量,然后回退文件以读取每一行。

查看getline 返回读取的字符数。你需要做的就是保留一个sum变量,在读完所有行后,只需返回(或更新你提供的指针作为参数)就完成了。当然,您可以在阅读该行后致电fscanf,对fgetsstrlen执行相同操作。

以下是在确定字符数(没有newline)并将该信息返回给调用函数的同时一次读取文本文件的简短示例。正如您需要将指针传递给getInput中的指针数组一样,我们将使用作为参数传递的指针将linecharacter计数返回给我们的调用函数。如果您声明并调用函数来读取文件,如下所示:

size_t nline = 0;       /* placeholders to be filled by readtxtfile */
size_t nchar = 0;       /* containing number of lines/chars in file */
...
char **file = readtxtfile (fn, &nline, &nchar);

通过在调用函数中声明变量,然后将指针传递给变量作为参数(使用urnary &),您可以更新函数中的值并使这些值可用于{ {1}}(或您从main调用的任何函数。)

说明这些要点的简单示例可能是:

readtxtfile

使用/输出

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

#define NMAX 256

char **readtxtfile (char *fn, size_t *idx, size_t *sum);
void prn_chararray (char **ca);
void free_chararray (char **ca);

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

    size_t nline = 0;       /* placeholders to be filled by readtxtfile */
    size_t nchar = 0;       /* containing number of lines/chars in file */
    char *fn = argc > 1 ? argv[1] : NULL;/* if fn not given, read stdin */

    /* read each file into an array of strings,
     * number of lines/chars read updated in nline, nchar
     */
    char **file = readtxtfile (fn, &nline, &nchar);

    /* output number of lines read & chars read and from where  */
    printf ("\n read '%zu' lines & '%zu' chars from file: %s\n\n", 
            nline, nchar, fn ? fn : "stdin");

    /* simple print function to print all lines */
    if (file) prn_chararray (file);

    /* simple free memory function */
    if (file) free_chararray (file);

    return 0;
}

/* simple function using getline to read any text file and return
 * the lines read in an array of pointers. user is responsible for
 * freeing memory when no longer needed
 */
char **readtxtfile (char *fn, size_t *idx, size_t *sum)
{
    char *ln = NULL;                /* NULL forces getline to allocate  */
    size_t n = 0;                   /* line buf size (0 - use default)  */
    ssize_t nchr = 0;               /* number of chars actually read    */
    size_t nmax = NMAX;             /* check for reallocation           */
    char **array = NULL;            /* array to hold lines read         */
    FILE *fp = NULL;                /* file pointer to open file fn     */

    /* open / validate file or read stdin */
    fp = fn ? fopen (fn, "r") : stdin;
    if (!fp) {
        fprintf (stderr, "%s() error: file open failed '%s'.", __func__, fn);
        return NULL;
    }

    /* allocate NMAX pointers to char* */
    if (!(array = calloc (NMAX, sizeof *array))) {
        fprintf (stderr, "%s() error: memory allocation failed.", __func__);
        return NULL;
    }

    /* read each line from stdin - dynamicallly allocated   */
    while ((nchr = getline (&ln, &n, fp)) != -1)
    {
        /* strip newline or carriage rtn    */
        while (nchr > 0 && (ln[nchr-1] == '\n' || ln[nchr-1] == '\r'))
            ln[--nchr] = 0;

        *sum += nchr;               /* add chars in line to sum         */

        array[*idx] = strdup (ln);  /* allocate/copy ln to array        */

        (*idx)++;                   /* increment value at index         */

        if (*idx == nmax) {         /* if lines exceed nmax, reallocate */
            char **tmp = realloc (array, nmax * 2);
            if (!tmp) {
                fprintf (stderr, "%s() error: reallocation failed.\n", __func__);
                exit (EXIT_FAILURE); /* or return NULL; */
            }
            array = tmp;
            nmax *= 2;
        }
    }

    if (ln) free (ln);              /* free memory allocated by getline */
    if (fp != stdin) fclose (fp);   /* close open file descriptor       */

    return array;
}

/* print an array of character pointers. */
void prn_chararray (char **ca)
{
    register size_t n = 0;
    while (ca[n])
    {
        printf (" arr[%3zu]  %s\n", n, ca[n]);
        n++;
    }
}

/* free array of char* */
void free_chararray (char **ca)
{
    if (!ca) return;
    register size_t n = 0;
    while (ca[n])
        free (ca[n++]);
    free (ca);
}

内存/泄漏检查

每当您在代码中分配/释放内存时,请不要忘记使用内存检查器来确保代码中没有内存错误或泄漏:

$ ./bin/getline_ccount <dat/fc-list-fonts.txt

 read '187' lines & '7476' chars from file: stdin

 arr[  0]  andalemo.ttf: Andale Mono - Regular
 arr[  1]  arialbd.ttf: Arial - Bold
 arr[  2]  arialbi.ttf: Arial - Bold Italic
 arr[  3]  ariali.ttf: Arial - Italic
 arr[  4]  arialnbi.ttf: Arial
 arr[  5]  arialnb.ttf: Arial
 arr[  6]  arialni.ttf: Arial
 arr[  7]  arialn.ttf: Arial
 arr[  8]  arial.ttf: Arial - Regular
 arr[  9]  ARIALUNI.TTF: Arial Unicode MS - Regular
 arr[ 10]  ariblk.ttf: Arial
 arr[ 11]  Bailey Script Regular.ttf: Bailey Script - Regular
 arr[ 12]  Bailey_Script_Regular.ttf: Bailey Script - Regular
 arr[ 13]  Belwe Gotisch.ttf: Belwe Gotisch - Regular
 arr[ 14]  Belwe_Gotisch.ttf: Belwe Gotisch - Regular
 <snip>

跟随评论

您在评论中发布的代码有几个问题:

$ valgrind ./bin/getline_ccount <dat/fc-list-fonts.txt
==20259== Memcheck, a memory error detector
==20259== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==20259== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==20259== Command: ./bin/getline_readfile_function
==20259==

 read '187' line from file: stdin

 arr[  0]  andalemo.ttf: Andale Mono - Regular
 arr[  1]  arialbd.ttf: Arial - Bold
 arr[  2]  arialbi.ttf: Arial - Bold Italic
 arr[  3]  ariali.ttf: Arial - Italic
<snip>

==20259==
==20259== HEAP SUMMARY:
==20259==     in use at exit: 0 bytes in 0 blocks
==20259==   total heap usage: 189 allocs, 189 frees, 9,831 bytes allocated
==20259==
==20259== All heap blocks were freed -- no leaks are possible
==20259==
==20259== For counts of detected and suppressed errors, rerun with: -v
==20259== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

首先,阅读第一条评论中的链接,了解for(i = 0; !feof(pFile); ++i) { fscanf(pFile, "%[^\n]%*c", tempInput); /* printf("%s\n", tempInput); */ input[i] = (char*)malloc((strlen(tempInput) + 1) * sizeof(char)); strcpy(input[i], tempInput); printf("%s\n", input[i]); memset(tempInput, 0, sizeof(tempInput)); } for(i = 0; i < numInput; ++i) { convertIntoPost(input[i], output[i]); } 在使用循环时指示EOF时可能导致问题的原因。其次,函数具有feof,使用它们的优势能够告诉您是否正在使用正确的函数来完成作业。

您尝试使用return values阅读整行时遇到的困难应该告诉您一些事情......您选择格式说明符fscanf来支持您阅读的问题包含"%[^\n]%*c"的行是whitespace不适合该作业的确切原因。

为什么呢?创建fscanf系列函数以读取离散值。他们的scanf基于:

  

成功匹配和分配的输入项数

使用您的格式说明符,成功时读取的项目数为return1会读取并丢弃*%c,但不会添加到项目计数中。这在尝试读取可能包含空行的文件时会导致严重问题。那么会发生什么?您遇到newlineinput failure返回fscanf - 但它仍然是一个非常有效的行。当发生这种情况时,不读任何内容。您无法将退货检查为0,因为当您遇到空行时,您将永远循环...

使用格式说明符,您也无法检查>= 0。为什么?使用EOF函数系列:

  

如果<{1}}在之前达到,则会返回scanf的值   EOFend of input   发生。

在您的情况下永远不会发生这种情况,因为first successful conversion matching failure input failure(不是fscanf)并且没有发生end of input。您是否开始明白为什么matching failure可能不适合这项工作?

C库为fscanf输入提供了两个函数。它们是line-orientedfgets。两者都将整行文本读入行缓冲区。这将包括每行末尾的getline(包括空行)。因此,当您使用其中任何一个阅读文本时,最好通过使用newline字符覆盖来删除newline

使用哪个?使用null-terminating,您可以通过适当调整字符缓冲区的大小来限制读取的字符数。 fgets现在是C库的一部分,它提供了返回实际读取的字符数(奖励)的额外好处,但它会读取该行,无论它有多长,因为它动态分配缓冲区为了你。我更喜欢它,但只知道你需要检查它已读过的字符数。

由于我上面提供了一个getline示例,因此您可以使用getline更好地编写读取循环,如下所示:

fgets

接下来,您的分配不需要转换为 while (fgets (tempInput, MAXL, pFile) != NULL) { nchr = strlen (tempInput); while (nchr && (tempInput[nchr-1] == '\n' || tempInput[nchr-1] == '\r')) tempInput[--nchr] = 0; /* strip newlines & carriage returns */ input[i++] = strdup (tempInput); /* allocates & copies tempInput */ } numInput = i; (char *)malloc的返回只是指向所分配的内存块的指针(即地址)。 (无论你为分配内存都是一样的)不需要calloc。它总是sizeof (char)。所以写一下:

1

input[i] = malloc (strlen(tempInput) + 1); strcpy (input[i], tempInput); allocate的更方便的方法是使用copy。使用strdup,上面的两行变为简单:

strdup

接下来,不需要input[i++] = strdup (tempInput); /* allocates & copies */

memset

如果声明memset(tempInput, 0, sizeof(tempInput)); 保持100个字符,并且说:tempInput,则可以将tempInput[100]之前的字符串再次反复读入相同的缓冲区而不必为零记忆。为什么?蜇伤是99 char。在null-terminated ...

之后,你不关心缓冲区中的内容

可以接受很多东西。在一个简短的例子中将它们放在一起,你可以做类似的事情:

null-terminator

示例输出

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

#define MAXL 256

/* dummy function */
void convertIntoPost (char *in, char **out)
{
    size_t i = 0, len = strlen (in);
    *out = calloc (1, len + 1);

    for (i = 0; i < len; i++) {
        (*out)[len-i-1] = in[i];
    }
}

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

    char tempInput[MAXL] = {0};
    char **input = NULL, **output = NULL;
    size_t i = 0, numInput = 0;
    size_t nchr = 0;
    FILE *pFile = NULL;

    pFile = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!pFile) {
        fprintf (stderr, "error: file open failed '%s'.\n", 
                argv[1] ? argv[1] : "stdin");
        return 1;
    }

    input  = calloc (1, MAXL);  /* allocate MAXL pointer for input & output   */
    output = calloc (1, MAXL);  /* calloc allocates and sets memory to 0-NULL */

    if (!input || !output) {    /* validate allocation */
        fprintf (stderr, "error: memory allocation failed.\n");
        return 1;
    }

    while (fgets (tempInput, MAXL, pFile) != NULL) {
        nchr = strlen (tempInput);
        while (nchr && (tempInput[nchr-1] == '\n' || tempInput[nchr-1] == '\r'))
            tempInput[--nchr] = 0;
        input[i++] = strdup (tempInput);    /* allocates & copies */
    }
    numInput = i;

    fclose (pFile);

    /* call convertIntoPost with input[i] and &output[i] */
    for (i = 0; i < numInput; ++i) {
        convertIntoPost (input[i], &output[i]);
        printf (" input[%2zu]: %-25s  output[%2zu]: %s\n",
                i, input[i], i, output[i]);
    }

    /* free all memory */
    for (i = 0; i < numInput; ++i) {
        free (input[i]), free (output[i]);
    }
    free (input), free (output);

    return 0;
}

编写代码的注意事项

始终在启用警告的情况下编译代码。这样编译器可以帮助指出代码可能含糊不清的区域等。要在编译时启用警告,只需将$ ./bin/feoffix ../dat/captnjack.txt input[ 0]: This is a tale output[ 0]: elat a si sihT input[ 1]: Of Captain Jack Sparrow output[ 1]: worrapS kcaJ niatpaC fO input[ 2]: A Pirate So Brave output[ 2]: evarB oS etariP A input[ 3]: On the Seven Seas. output[ 3]: .saeS neveS eht nO -Wall添加到编译字符串即可。 (如果你真的想要所有警告,请添加-Wextra(定义:过分关注琐碎的细节))。花时间阅读并理解编译器告诉你的警告(它们真的很好,你会很快知道每个意味着什么)。然后......解决问题,以便您的代码编译而不用任何警告。

只有罕见且有限的情况才允许“理解并选择允许”#要保留的警告(例如在使用无法访问源代码的库时)

所以把它们放在一起,当你编译代码时,你应该至少编译以下代码进行测试和开发:

-pedantic

使用gcc -Wall -Wextra -o progname progname.c -g gcc选项告诉编译器生成其他调试信息,以便与调试器-g一起使用(了解它)。

如果您已经解决了所有错误,并且已准备好对代码进行最终编译,那么您需要添加优化级别gdb之类的优化(首都-On [不为零]其中O是级别'n'1, 2, or 3是默认值),0基本上是-Ofast,还有一些额外的优化)。您可能还想考虑尽可能使用-O3告诉编译器inline您的函数以消除函数调用开销。所以对于最终编译,你会想要类似的东西:

-finline-functions

优化可以使性能提高10倍并缩短程序执行时间(在某些情况下,性能提升1000%(通常会提高300-500%))。非常值得添加几个开关。

答案 2 :(得分:2)

正如Sourav所提到的,C使用pass-by-value进行参数传递,因此processInExp范围内的输入变量具有先前在main中分配的内存地址的值。

打印input[0]时会导致分段错误。这是因为printf正在尝试打印位于相对于先前分配的内存的地址的字符串,而不是分配给您复制字符串的getInput函数中的输入的内存。

解决方案是将指针传递给输入,因此您的函数签名将如下所示:int getInput(char *** input, const char * fileName)。然后,您需要将对input的任何引用更改为*input才能取消引用指针,并将input的指针传递给getInput,如下所示:{ {1}}。

答案 3 :(得分:0)

  

C语言无一例外都是按值传递的。

函数无法更改实际参数的值。