从函数返回本地定义的字符串数组会产生内存错误

时间:2017-05-27 14:33:16

标签: c

我在c学校的任务中挣扎了好几天。任务是编写一个接受字符串数组及其长度的函数,然后检查数组中最短字符串的长度,最后收集长度等于传递数组中最短长度的字符串。给出主要函数并期望写好,它只传递字符串数组,然后调用函数循环返回的数组以打印它的内容。 main()使用了一个tmp指针,对我来说也不清楚,为什么这是必要的,如果有人可以对那个有所了解,也会受到赞赏。

所以我的代码看起来像这样:

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

char **foo(char **t, int size){

  int i;
  int current_length;
  int shortest_length = strlen(t[0]);
  int num_of_shortest = 0;


  //define shortest string length
  for(i = 0; i < size; i++)
  {
    current_length = strlen(t[i]);

    if(shortest_length > current_length)
      shortest_length = current_length;
  }
  //define the number of the shortest strings
  for(i = 0; i < size; i++)
  {
    current_length = strlen(t[i]);

    if(shortest_length == current_length)
      num_of_shortest++;
  }
  //define an array, to hold the shortest things in order of appearance in the passed array of strings
  static char **array_of_shortests;
  array_of_shortests = malloc(sizeof(char)*num_of_shortest);


  for(i = 0; i < size; i++)
  {
    current_length = strlen(t[i]);
    //allocate memory for the string stored in the array of shortest strings
    array_of_shortests[i] = (char *)malloc(strlen(t[i])+1);

    //fill up the array of the shortest strings
    if(shortest_length == current_length)
    {
      array_of_shortests[i] = t[i];
    }
  }


  return array_of_shortests;
}


int main()
{
    char *t[] = {"apple", "pineapple", "orange", "apple", "banana", "grape"};
    char **result = foo(t, sizeof(t) / sizeof(char *));
    char **tmp;
    for (tmp = result; *tmp; ++tmp)
        printf("%s\n", *tmp);
    free(result);
    return 0;
}

它编译得很好,但在运行时会产生以下错误:

[root@berlin tmp]# gcc -o practice_shortest_strings practice_shortest_strings.c
[root@berlin tmp]# ./practice_shortest_strings
apple


apple

grape
*** Error in `./practice_shortest_strings': free(): invalid next size (fast): 0x0000000000809010 ***
======= Backtrace: =========
/lib64/libc.so.6(+0x7c503)[0x7f88d95e0503]
./practice_shortest_strings[0x40081d]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x7f88d9585b35]
./practice_shortest_strings[0x400549]
======= Memory map: ========
00400000-00401000 r-xp 00000000 fd:00 100943188                          /tmp/practice_shortest_strings
00600000-00601000 r--p 00000000 fd:00 100943188                          /tmp/practice_shortest_strings
00601000-00602000 rw-p 00001000 fd:00 100943188                          /tmp/practice_shortest_strings
00809000-0082a000 rw-p 00000000 00:00 0                                  [heap]
7f88d4000000-7f88d4021000 rw-p 00000000 00:00 0
7f88d4021000-7f88d8000000 ---p 00000000 00:00 0
7f88d934e000-7f88d9363000 r-xp 00000000 fd:00 69263259                   /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f88d9363000-7f88d9562000 ---p 00015000 fd:00 69263259                   /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f88d9562000-7f88d9563000 r--p 00014000 fd:00 69263259                   /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f88d9563000-7f88d9564000 rw-p 00015000 fd:00 69263259                   /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f88d9564000-7f88d971a000 r-xp 00000000 fd:00 67658381                   /usr/lib64/libc-2.17.so
7f88d971a000-7f88d991a000 ---p 001b6000 fd:00 67658381                   /usr/lib64/libc-2.17.so
7f88d991a000-7f88d991e000 r--p 001b6000 fd:00 67658381                   /usr/lib64/libc-2.17.so
7f88d991e000-7f88d9920000 rw-p 001ba000 fd:00 67658381                   /usr/lib64/libc-2.17.so
7f88d9920000-7f88d9925000 rw-p 00000000 00:00 0
7f88d9925000-7f88d9945000 r-xp 00000000 fd:00 67149962                   /usr/lib64/ld-2.17.so
7f88d9b37000-7f88d9b3a000 rw-p 00000000 00:00 0
7f88d9b41000-7f88d9b44000 rw-p 00000000 00:00 0
7f88d9b44000-7f88d9b45000 r--p 0001f000 fd:00 67149962                   /usr/lib64/ld-2.17.so
7f88d9b45000-7f88d9b46000 rw-p 00020000 fd:00 67149962                   /usr/lib64/ld-2.17.so
7f88d9b46000-7f88d9b47000 rw-p 00000000 00:00 0
7ffdfbf81000-7ffdfbfa2000 rw-p 00000000 00:00 0                          [stack]
7ffdfbfbb000-7ffdfbfbd000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

请在这里帮助我,我没有想法......坦克帮助很多!

2 个答案:

答案 0 :(得分:0)

发布的代码包含几个问题。有关详细信息,请参阅问题的评论。

以下建议代码:

  1. 干净地编译
  2. 使用有意义的参数和变量名称
  3. 子函数名称:foo()很糟糕,它没有说明函数的作用。如果有选择,我更喜欢:selectShortestStrings()
  4. 使用calloc()而不是malloc(),因此内存已预先初始化为所有NULL
  5. 允许所有字符串具有相同长度的可能性
  6. 正确检查从系统函数返回的错误指示
  7. 遵循公理:每行只有一个语句,并且(最多)每个语句一个变量声明。
  8. 始终缩进代码。每个开口支架后缩进&#39; {&#39 ;,在每个结束支架之前都是假的&#39;}&#39;。每个缩进级别使用4个空格
  9. 通过一个空白行分隔代码块(for,if,else,while,do ... while,switch,case,default)。
  10. 将每个函数分隔2个空行。
  11. 使用size_t而不是int来避免隐含的转化&#39;
  12. 未修改main()功能 虽然签名应该是:int main( void ) 通过有意义的子函数名称,代码块分离和类似修改,可读性将大大增强
  13. 使用lowerCamelCase以便于变量名称和一致性的可读性。
  14. 包含为何包含每个头文件的文档
  15. 将子函数foo()放在main()之后,以便读取代码向下进行而不是向上跳回。
  16. 将变量i的范围本地化在使用它的每个代码块中。
  17. `lowerCamelCase:

    的定义

    lowerCamelCase(CamelCase的一部分)是一种命名约定,其中名称由多个单词组成,这些单词作为单个单词连接在一起,并且新单词中的每个单词的第一个字母(第一个单词除外)大写形成名称的词。

    现在建议的代码:

    #include <stdio.h>    // printf()
    #include <stdlib.h>   // calloc(), free(), exit(), EXIT_FAILURE
    #include <string.h>   // strlen()
    
    // prototypes
    char **foo( char **, size_t );
    
    
    
    
    
    int main()
    {
        char *t[] = {"apple", "pineapple", "orange", "apple", "banana", "grape"};
        char **result = foo(t, sizeof(t) / sizeof(char *));
        char **tmp;
        for (tmp = result; *tmp; ++tmp)
            printf("%s\n", *tmp);
        free(result);
        return 0;
    }
    
    
    char **foo( char **stringPtrs, size_t size )
    {
        // using 'size_t' because 'strlen()' returns a 'size_t'
        size_t currentLength;
        size_t shortestaLength = strlen(stringPtrs[0]);
        size_t numOfShortest = 0;
    
    
        //define shortest string length
        for( size_t i = 0; i < size; i++)
        {
            currentLength = strlen( stringPtrs[i]);
    
            if(shortestaLength > currentLength)
              shortestaLength = currentLength;
        }
    
        //define the number of the shortest strings
        for( size_t i = 0; i < size; i++)
        {
            currentLength = strlen(stringPtrs[i]);
    
            if(shortestaLength == currentLength)
              numOfShortest++;
        }
    
        // define an array, to hold the pointer to the shortest things
        // in order of appearance in the passed array of strings
        char **shortestPtrs = NULL;
    
        // note printing loop in main() checks for NULL pointer
        // so, just incase all the entries are the same length
        // allocate room for extra entry and set all to NULL
        shortestPtrs = calloc(numOfShortest+1, sizeof( char* ));
        if( !shortestPtrs )
        {
            perror( "calloc failed" );
            exit( EXIT_FAILURE );
        }
    
        // implied else, calloc successful
    
        // cannot have NULL pointer in middle of array
        // so indexing of output has to be separate from index of input
        size_t shortestIndex = 0;
    
        for( size_t i = 0; i < size; i++)
        {
            currentLength = strlen(stringPtrs[i]);
    
            //fill up the array of the shortest strings
            if(shortestaLength == currentLength)
            {
                shortestPtrs[ shortestIndex ] = stringPtrs[i];
                shortestIndex++;
            }
        }
    
        return shortestPtrs;
    } // end function: foo
    

    结果输出为:

    apple
    apple
    grape
    

答案 1 :(得分:0)

您的代码中存在两个主要问题。首先,您为array_of_shortests的分配分配num_of_shortest(字符)而不是num_of_shortest(指针) - 导致您的体系结构的分配不足sizeof (a pointer)({ x86_64上的{1}}。在验证分配并将最后一个 sentinel 指针设置为8时,您可以按如下方式更正它:

NULL

您的下一个错误是(1)/* define an array of pointer to holding the address to the shortest * strings in order of appearance in the passed array of strings */ if (!(array_of_shortests = malloc (sizeof *array_of_shortests * (num_of_shortest + 1)))) return NULL; array_of_shortests[num_of_shortest] = NULL; /* set sentinel NULL */ 中每个字符串的不必要的存储分配,(2)索引array_of_shortests的不正确,以及(3)无法释放您分配的内存对于每个字符串,在调用array_of_shortests[i]以释放free (result)中的指针之前。

由于main中的字符串已经有足够的存储空间作为array_of_shortests引用的字符串文字,所以你关心的只是指向最短文字的地址并存储t中的指针地址(您不需要再分配内存来再次复制/存储字符串文字)。你只需要分配array_of_shortests,你已经在上面做了num_of_shortest + 1为你的 sentinel NULL 提供空间,用于你在{{1}中使用的迭代方案然后,将每个最短的地址分配给例如+1

main

然后回到array_of_shortests[ndx++],你可以像你一样迭代,打印和int i, ndx = 0, ... for (i = 0; i < size; i++) { current_length = strlen (t[i]); /* you allocated 'pointers' no need to allocate storage for each, * the pointers can hold the address of the shortest in 't'. * NOTE: you cannot use array_of_shortests[i] */ if (current_length == shortest_length) array_of_shortests[ndx++] = t[i]; } 指针。将各个部分放在一起,你可以做到:

main

示例使用/输出

free

内存使用/错误检查

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

char **foo (char **t, int size) {

    /* valdiate parameters - minimally */
    if (!t || !*t || size < 1) {
        fprintf (stderr, "foo() error: invalid paramater.\n");
        return NULL;
    }

    char **array_of_shortests = NULL;
    int i,
        ndx = 0,
        current_length,
        shortest_length = strlen (t[0]),
        num_of_shortest = 0;

    /* define shortest string length */
    for (i = 0; i < size; i++) {
        current_length = strlen (t[i]);

        if (current_length < shortest_length)
            shortest_length = current_length;
    }

    /* define the number of the shortest strings */
    for (i = 0; i < size; i++) {
        current_length = strlen (t[i]);

        if (shortest_length == current_length)
            num_of_shortest++;
    }

    /* define an array of pointer to holding the address to the shortest
     * strings in order of appearance in the passed array of strings
     */
    if (!(array_of_shortests = 
            malloc (sizeof *array_of_shortests * (num_of_shortest + 1))))
        return NULL;

    array_of_shortests[num_of_shortest] = NULL; /* set sentinel NULL */

    for (i = 0; i < size; i++) {
        current_length = strlen (t[i]);
        /* you allocated 'pointers' no need to allocate storage for each,
         * the pointers can hold the address of the shortest in 't'.
         * NOTE: you cannot use array_of_shortests[i]
         */
        if (current_length == shortest_length)
            array_of_shortests[ndx++] = t[i];
    }

    return array_of_shortests;
}

int main (void) {

    char *t[] = { "apple", "pineapple", "orange", 
                  "apple", "banana", "grape" },
        **result = foo (t, sizeof t / sizeof *t),
        **tmp = result;

    if (!result) {
        fprintf (stderr, "foo() error: result is NULL.\n");
        return 1;
    }

    while (*tmp)
        printf ("%s\n", *tmp++);

    free (result);  /* free array of pointers */

    return 0;
}

将指针传递给 $ ./bin/shorteststrs apple apple grape 而不是使用Sentinel

虽然向$ valgrind ./bin/shorteststrs ==23647== Memcheck, a memory error detector ==23647== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al. ==23647== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info ==23647== Command: ./bin/shorteststrs ==23647== apple apple grape ==23647== ==23647== HEAP SUMMARY: ==23647== in use at exit: 0 bytes in 0 blocks ==23647== total heap usage: 1 allocs, 1 frees, 32 bytes allocated ==23647== ==23647== All heap blocks were freed -- no leaks are possible ==23647== ==23647== For counts of detected and suppressed errors, rerun with: -v ==23647== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) 添加额外指针并将其设置为num_of_shortest没有任何问题,但您的另一个选择是在array_of_shortests中声明NULL并简单地通过,并在num_of_shortest内更新指针。这提供了一种在main中提供foo的方法。 E.g。

num_of_shortest

示例使用/输出

main

内存使用/错误检查

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

char **foo (char **t, int size, int *num_of_shortest) {

    /* valdiate parameters - minimally */
    if (!t || !*t || size < 1 || !num_of_shortest) {
        fprintf (stderr, "foo() error: invalid paramater.\n");
        return NULL;
    }

    char **array_of_shortests = NULL;
    int i,
        ndx = 0,
        current_length,
        shortest_length = strlen (t[0]);

    *num_of_shortest = 0;

    /* define shortest string length */
    for (i = 0; i < size; i++) {
        current_length = strlen (t[i]);

        if (current_length < shortest_length)
            shortest_length = current_length;
    }

    /* define the number of the shortest strings */
    for (i = 0; i < size; i++) {
        current_length = strlen (t[i]);

        if (shortest_length == current_length)
            (*num_of_shortest)++;
    }

    /* define an array of pointer to holding the address to the shortest
     * strings in order of appearance in the passed array of strings
     */
    if (!(array_of_shortests =
            malloc (sizeof *array_of_shortests * *num_of_shortest)))
        return NULL;

    for (i = 0; i < size; i++) {
        current_length = strlen (t[i]);
        /* you allocated 'pointers' no need to allocate storage for each,
         * the pointers can hold the address of the shortest in 't'.
         * NOTE: you cannot use array_of_shortests[i]
         */
        if (current_length == shortest_length)
            array_of_shortests[ndx++] = t[i];
    }

    return array_of_shortests;
}

int main (void) {

    int i, n = 0;   /* pass address of n to foo */
    char *t[] = { "apple", "pineapple", "orange",
                  "apple", "banana", "grape" },
        **result = foo (t, sizeof t / sizeof *t, &n);

    if (!result) {
        fprintf (stderr, "foo() error: result is NULL.\n");
        return 1;
    }

    for (i = 0; i < n; i++)
        printf ("%s\n", result[i]);

    free (result);  /* free array of pointers */

    return 0;
}

注意:在分配中保存“指针”(这是无关紧要的,但值得注意)。

仔细看看,如果您有其他问题,请告诉我。