Realloc, утечки памяти кучи - PullRequest
0 голосов
/ 06 января 2019

это мой счет и проблема с решением Leetcode. Но с утечками памяти https://leetcode.com/problems/count-and-say/

Мой make-файл

build:
    gcc main.c -Wall -g -o main; \
    $(PWD)/main; \

Мои команды сборки

make

Или с Вальгриндом:

make
valgrind --leak-check=yes ./main

Вывод: (правильно, проверено)

count and say 30 = 3113112221131112311332111213122112311311123112111331121113122112132113121113222112311311221112131221123113112221121113311211131122211211131221131211132221121321132132212321121113121112133221123113112221131112212211131221121321131211132221123113112221131112311332211211133112111311222112111312211311123113322112111312211312111322212321121113121112133221121321132132211331121321132213211231132132211211131221232112111312212221121123222112311311222113111231133211121321321122111312211312111322211213211321322123211211131211121332211231131122211311123113321112131221123113111231121123222112111331121113112221121113122113111231133221121113122113121113221112131221123113111231121123222112111312211312111322212321121113121112131112132112311321322112111312212321121113122122211211232221121321132132211331121321231231121113112221121321132132211322132113213221123113112221133112132123222112111312211312112213211231132132211211131221131211322113321132211221121332211231131122211311123113321112131221123113111231121113311211131221121321131211132221123113112211121312211231131122211211133112111311222112111312211312111322211213211321223112111311222112132113213221133122211311221122111312211312111322212321121113121112131112132112311321322112111312212321121113122122211211232221121321132132211331121321231231121113112221121321132132211322132113213221123113112221133112132123222112111312211312112213211231132132211211131221131211322113321132211221121332211213211321322113311213212312311211131122211213211331121321123123211231131122211211131221131112311332211213211321223112111311222112132113213221123123211231132132211231131122211311123113322112111312211312111322111213122112311311123112112322211213211321322113312211223113112221121113122113111231133221121321132132211331222113321112131122211332113221122112133221123113112221131112311332111213122112311311123112111331121113122112132113121113222112311311221112131221123113112221121113311211131122211211131221131211132221121321132132212321121113121112133221123113112221131112311332111213122112311311123112112322211322311311222113111231133211121312211231131112311211232221121113122113121113222123211211131221132211131221121321131211132221123113112211121312211231131122113221122112133221121321132132211331121321231231121113121113122122311311222113111231133221121113122113121113221112131221123113111231121123222112132113213221133112132123123112111312211322311211133112111312211213211311123113223112111321322123122113222122211211232221121113122113121113222123211211131211121311121321123113213221121113122123211211131221121311121312211213211321322112311311222113311213212322211211131221131211221321123113213221121113122113121113222112131112131221121321131211132221121321132132211331121321232221123113112221131112311322311211131122211213211331121321122112133221121113122113121113222123112221221321132132211231131122211331121321232221121113122113121113222123211211131211121332211213111213122112132113121113222112132113213221232112111312111213322112132113213221133112132123123112111311222112132113311213211221121332211231131122211311123113321112131221123113112221132231131122211211131221131112311332211213211321223112111311222112132113212221132221222112112322211211131221131211132221232112111312111213111213211231131112311311221122132113213221133112132123222112311311222113111231132231121113112221121321133112132112211213322112111312211312111322212321121113121112131112132112311321322112111312212321121113122122211211232221121311121312211213211312111322211213211321322123211211131211121332211213211321322113311213211322132112311321322112111312212321121113122122211211232221121321132132211331121321231231121113112221121321133112132112312321123113112221121113122113111231133221121321132122311211131122211213211321222113222122211211232221123113112221131112311332111213122112311311123112111331121113122112132113121113222112311311221112131221123113112221121113311211131122211211131221131211132221121321132132212321121113121112133221123113112221131112311332111213213211221113122113121113222112132113213221232112111312111213322112132113213221133112132123123112111312211322311211133112111312212221121123222112132113213221133112132123222113223113112221131112311332111213122112311311123112112322211211131221131211132221232112111312111213111213211231132132211211131221131211221321123113213221123113112221131112211322212322211231131122211322111312211312111322211213211321322113311213211331121113122122211211132213211231131122212322211331222113112211

Из Вальгринда

==1796== Memcheck, a memory error detector
==1796== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et 
al.
==1796== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright 
info
==1796== Command: ./main
==1796== 
==1796== Invalid read of size 1
==1796==    at 0x100000930: countAndSay (main.c:62)
==1796==    by 0x100000ECA: main (main.c:209)
==1796==  Address 0x100df6f80 is 0 bytes inside a block of size 2 
free'd
==1796==    at 0x1000AC2DA: realloc (vg_replace_malloc.c:829)
==1796==    by 0x100000D10: countAndSay (main.c:172)
==1796==    by 0x100000ECA: main (main.c:209)
==1796==  Block was alloc'd at
==1796==    at 0x1000AB2C5: malloc (vg_replace_malloc.c:302)
==1796==    by 0x100000892: countAndSay (main.c:40)
==1796==    by 0x100000ECA: main (main.c:209)
==1796== 
==1796== Invalid read of size 1
==1796==    at 0x10000096B: countAndSay (main.c:73)
==1796==    by 0x100000ECA: main (main.c:209)
==1796==  Address 0x100df6f80 is 0 bytes inside a block of size 2 
free'd
==1796==    at 0x1000AC2DA: realloc (vg_replace_malloc.c:829)
==1796==    by 0x100000D10: countAndSay (main.c:172)
==1796==    by 0x100000ECA: main (main.c:209)
==1796==  Block was alloc'd at
==1796==    at 0x1000AB2C5: malloc (vg_replace_malloc.c:302)
==1796==    by 0x100000892: countAndSay (main.c:40)
==1796==    by 0x100000ECA: main (main.c:209)
==1796== 
==1796== Invalid read of size 1
==1796==    at 0x1000009D7: countAndSay (main.c:89)
==1796==    by 0x100000ECA: main (main.c:209)
==1796==  Address 0x100df6f80 is 0 bytes inside a block of size 2 
free'd
==1796==    at 0x1000AC2DA: realloc (vg_replace_malloc.c:829)
==1796==    by 0x100000D10: countAndSay (main.c:172)
==1796==    by 0x100000ECA: main (main.c:209)
==1796==  Block was alloc'd at
==1796==    at 0x1000AB2C5: malloc (vg_replace_malloc.c:302)
==1796==    by 0x100000892: countAndSay (main.c:40)
==1796==    by 0x100000ECA: main (main.c:209)
==1796== 
==1796== Invalid read of size 16
==1796==    at 0x100655A65: _platform_memchr$VARIANT$Base (in 
/usr/lib/system/libsystem_platform.dylib)
==1796==    by 0x1002E99E9: __sfvwrite (in 
/usr/lib/system/libsystem_c.dylib)
==1796==    by 0x1002F35FE: __vfprintf (in 
/usr/lib/system/libsystem_c.dylib)
==1796==    by 0x100318058: __v2printf (in 
/usr/lib/system/libsystem_c.dylib)
==1796==    by 0x1002EF741: vfprintf_l (in 
/usr/lib/system/libsystem_c.dylib)
==1796==    by 0x1002ED7CA: printf (in 
/usr/lib/system/libsystem_c.dylib)
==1796==    by 0x100000EE0: main (main.c:210)
==1796==  Address 0x10545d410 is 1 bytes after a block of size 4,463 
alloc'd
==1796==    at 0x1000AB2C5: malloc (vg_replace_malloc.c:302)
==1796==    by 0x1000AC30C: realloc (vg_replace_malloc.c:829)
==1796==    by 0x100000C67: countAndSay (main.c:157)
==1796==    by 0x100000ECA: main (main.c:209)
==1796== 
==1796== Conditional jump or move depends on uninitialised value(s)
==1796==    at 0x100655A7D: _platform_memchr$VARIANT$Base (in 
/usr/lib/system/libsystem_platform.dylib)
==1796==    by 0x1002E99E9: __sfvwrite (in 
/usr/lib/system/libsystem_c.dylib)
==1796==    by 0x1002F35FE: __vfprintf (in 
/usr/lib/system/libsystem_c.dylib)
==1796==    by 0x100318058: __v2printf (in 
/usr/lib/system/libsystem_c.dylib)
==1796==    by 0x1002EF741: vfprintf_l (in 
/usr/lib/system/libsystem_c.dylib)
==1796==    by 0x1002ED7CA: printf (in 
/usr/lib/system/libsystem_c.dylib)
==1796==    by 0x100000EE0: main (main.c:210)
==1796== 
count and say 30 = 3113112221131112311332111213122112311311123112111331121113122112132113121113222112311311221112131221123113112221121113311211131122211211131221131211132221121321132132212321121113121112133221123113112221131112212211131221121321131211132221123113112221131112311332211211133112111311222112111312211311123113322112111312211312111322212321121113121112133221121321132132211331121321132213211231132132211211131221232112111312212221121123222112311311222113111231133211121321321122111312211312111322211213211321322123211211131211121332211231131122211311123113321112131221123113111231121123222112111331121113112221121113122113111231133221121113122113121113221112131221123113111231121123222112111312211312111322212321121113121112131112132112311321322112111312212321121113122122211211232221121321132132211331121321231231121113112221121321132132211322132113213221123113112221133112132123222112111312211312112213211231132132211211131221131211322113321132211221121332211231131122211311123113321112131221123113111231121113311211131221121321131211132221123113112211121312211231131122211211133112111311222112111312211312111322211213211321223112111311222112132113213221133122211311221122111312211312111322212321121113121112131112132112311321322112111312212321121113122122211211232221121321132132211331121321231231121113112221121321132132211322132113213221123113112221133112132123222112111312211312112213211231132132211211131221131211322113321132211221121332211213211321322113311213212312311211131122211213211331121321123123211231131122211211131221131112311332211213211321223112111311222112132113213221123123211231132132211231131122211311123113322112111312211312111322111213122112311311123112112322211213211321322113312211223113112221121113122113111231133221121321132132211331222113321112131122211332113221122112133221123113112221131112311332111213122112311311123112111331121113122112132113121113222112311311221112131221123113112221121113311211131122211211131221131211132221121321132132212321121113121112133221123113112221131112311332111213122112311311123112112322211322311311222113111231133211121312211231131112311211232221121113122113121113222123211211131221132211131221121321131211132221123113112211121312211231131122113221122112133221121321132132211331121321231231121113121113122122311311222113111231133221121113122113121113221112131221123113111231121123222112132113213221133112132123123112111312211322311211133112111312211213211311123113223112111321322123122113222122211211232221121113122113121113222123211211131211121311121321123113213221121113122123211211131221121311121312211213211321322112311311222113311213212322211211131221131211221321123113213221121113122113121113222112131112131221121321131211132221121321132132211331121321232221123113112221131112311322311211131122211213211331121321122112133221121113122113121113222123112221221321132132211231131122211331121321232221121113122113121113222123211211131211121332211213111213122112132113121113222112132113213221232112111312111213322112132113213221133112132123123112111311222112132113311213211221121332211231131122211311123113321112131221123113112221132231131122211211131221131112311332211213211321223112111311222112132113212221132221222112112322211211131221131211132221232112111312111213111213211231131112311311221122132113213221133112132123222112311311222113111231132231121113112221121321133112132112211213322112111312211312111322212321121113121112131112132112311321322112111312212321121113122122211211232221121311121312211213211312111322211213211321322123211211131211121332211213211321322113311213211322132112311321322112111312212321121113122122211211232221121321132132211331121321231231121113112221121321133112132112312321123113112221121113122113111231133221121321132122311211131122211213211321222113222122211211232221123113112221131112311332111213122112311311123112111331121113122112132113121113222112311311221112131221123113112221121113311211131122211211131221131211132221121321132132212321121113121112133221123113112221131112311332111213213211221113122113121113222112132113213221232112111312111213322112132113213221133112132123123112111312211322311211133112111312212221121123222112132113213221133112132123222113223113112221131112311332111213122112311311123112112322211211131221131211132221232112111312111213111213211231132132211211131221131211221321123113213221123113112221131112211322212322211231131122211322111312211312111322211213211321322113311213211331121113122122211211132213211231131122212322211331222113112211
==1796== 
==1796== HEAP SUMMARY:
==1796==     in use at exit: 29,301,895 bytes in 40,712 blocks
==1796==   total heap usage: 80,412 allocs, 39,700 frees, 58,326,719 
bytes allocated
==1796== 
==1796== 72 bytes in 3 blocks are possibly lost in loss record 25 of 51
==1796==    at 0x1000ABD72: calloc (vg_replace_malloc.c:755)
==1796==    by 0x10075A7C2: map_images_nolock (in 
/usr/lib/libobjc.A.dylib)
==1796==    by 0x10076D4E0: map_images (in /usr/lib/libobjc.A.dylib)
==1796==    by 0x100007C64: dyld::notifyBatchPartial(dyld_image_states, 
bool, char const* (*)(dyld_image_states, unsigned int, dyld_image_info 
const*), bool, bool) (in /usr/lib/dyld)
==1796==    by 0x100007E39: dyld::registerObjCNotifiers(void (*) 
(unsigned int, char const* const*, mach_header const* const*), void (*) 
(char const*, mach_header const*), void (*)(char const*, mach_header 
const*)) (in /usr/lib/dyld)
==1796==    by 0x10022571D: _dyld_objc_notify_register (in / 
/usr/lib/system/libdyld.dylib)
==1796==    by 0x10075A073: _objc_init (in /usr/lib/libobjc.A.dylib)
==1796==    by 0x1001AFB34: _os_object_init (in 
/usr/lib/system/libdispatch.dylib)
==1796==    by 0x1001AFB1B: libdispatch_init (in 
/usr/lib/system/libdispatch.dylib)
==1796==    by 0x1000BE9C2: libSystem_initializer (in 
/usr/lib/libSystem.B.dylib)
==1796==    by 0x100019AC5: 
ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&) 
(in /usr/lib/dyld)
==1796==    by 0x100019CF5: 
ImageLoaderMachO::doInitialization(ImageLoader::LinkContext const&) (in 
/usr/lib/dyld)
==1796== 
==1796== 168 bytes in 56 blocks are definitely lost in loss record 28 
of 51
==1796==    at 0x1000AB2C5: malloc (vg_replace_malloc.c:302)
==1796==    by 0x1000AC30C: realloc (vg_replace_malloc.c:829)
==1796==    by 0x100000D10: countAndSay (main.c:172)
==1796==    by 0x100000ECA: main (main.c:209)
==1796== 
==1796== 570 bytes in 1 blocks are possibly lost in loss record 37 of 
51
==1796==    at 0x1000AC2DA: realloc (vg_replace_malloc.c:829)
==1796==    by 0x100000DCD: countAndSay (main.c:184)
==1796==    by 0x100000ECA: main (main.c:209)
==1796== 
==1796== 1,512 bytes in 378 blocks are definitely lost in loss record 
38 of 51
==1796==    at 0x1000AB2C5: malloc (vg_replace_malloc.c:302)
==1796==    by 0x1000AC30C: realloc (vg_replace_malloc.c:829)
==1796==    by 0x100000DCD: countAndSay (main.c:184)
==1796==    by 0x100000ECA: main (main.c:209)
==1796== 
==1796== 4,462 bytes in 1 blocks are definitely lost in loss record 45 
of 51
==1796==    at 0x1000AB2C5: malloc (vg_replace_malloc.c:302)
==1796==    by 0x100000867: countAndSay (main.c:29)
==1796==    by 0x100000ECA: main (main.c:209)
==1796== 
==1796== 17,848 bytes in 1 blocks are definitely lost in loss record 46 
of 51
==1796==    at 0x1000AB2C5: malloc (vg_replace_malloc.c:302)
==1796==    by 0x100000857: countAndSay (main.c:28)
==1796==    by 0x100000ECA: main (main.c:209)
==1796== 
==1796== 19,257 (240 direct, 19,017 indirect) bytes in 1 blocks are d 
definitely lost in loss record 48 of 51
==1796==    at 0x1000AB2C5: malloc (vg_replace_malloc.c:302)
==1796==    by 0x100000839: countAndSay (main.c:19)
==1796==    by 0x100000ECA: main (main.c:209)
==1796== 
==1796== 61,644 bytes in 406 blocks are definitely lost in loss record 
49 of 51
==1796==    at 0x1000AB2C5: malloc (vg_replace_malloc.c:302)
==1796==    by 0x1000AC30C: realloc (vg_replace_malloc.c:829)
==1796==    by 0x100000C67: countAndSay (main.c:157)
==1796==    by 0x100000ECA: main (main.c:209)
==1796== 
==1796== 80,493 bytes in 379 blocks are definitely lost in loss record 
50 of 51
==1796==    at 0x1000AC2DA: realloc (vg_replace_malloc.c:829)
==1796==    by 0x100000D10: countAndSay (main.c:172)
==1796==    by 0x100000ECA: main (main.c:209)
==1796== 
==1796== 29,093,648 bytes in 39,299 blocks are definitely lost in loss 
record 51 of 51
==1796==    at 0x1000AC2DA: realloc (vg_replace_malloc.c:829)
==1796==    by 0x100000DCD: countAndSay (main.c:184)
==1796==    by 0x100000ECA: main (main.c:209)
==1796== 
==1796== LEAK SUMMARY:
==1796==    definitely lost: 29,260,015 bytes in 40,521 blocks
==1796==    indirectly lost: 19,017 bytes in 29 blocks
==1796==      possibly lost: 642 bytes in 4 blocks
==1796==    still reachable: 200 bytes in 6 blocks
==1796==         suppressed: 22,021 bytes in 152 blocks
==1796== Reachable blocks (those to which a pointer was found) are not 
shown.
==1796== To see them, rerun with: --leak-check=full --show-leak- 
kinds=all
==1796== 
==1796== For counts of detected and suppressed errors, rerun with: -v
==1796== Use --track-origins=yes to see where uninitialised values come 
from
==1796== ERROR SUMMARY: 124 errors from 15 contexts (suppressed: 11 
from 11)

main.c

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

#define MAX_SEQUENCE_COUNT 30
#define BUFFER_MAX 4462

char* countAndSay(int n) {

    int msc;
    if (n > 0 && n <= 30) {
         msc = MAX_SEQUENCE_COUNT;
    } else {
         fprintf(stderr, "Error: The range for this count and say method is 1...30\n");
         exit(1);
    }

    //Holds the array of sequences
    char **out_buffer = malloc(msc * sizeof(char*));
    //Holds current sequence
    char *out;
    int size = n;

    //Holds previous sequence
    char *prev_chunk = NULL;

    //memory for the counts and says
    int *counts = malloc(BUFFER_MAX*sizeof(int));
    char *says = malloc(BUFFER_MAX*sizeof(char));

    //index into the buffer memory of sequences
    int index = 0;

    //solve iteratively
    while (size > 0) {
        //base condition
        //size counts down to 0, filling chunks, populating the next chunk to be processed
        //filled chunks are placed in the out buffer at the index which is counting 
        if (index == 0) {
            out = malloc(2 * sizeof(char));
            out[0] = '1';
            out[1] = '\0';
            prev_chunk = out;
            size -= 1;
            index += 1;
            out_buffer[0] = out;
            continue;
       }

       //from 0 to index - 1 get the chunk to be processed, use it to put together 
    //the count and say chunk for adding to the index slot
        for (int s = 0; s < index; s++) {           
            if (s == 0) {
                prev_chunk = out_buffer[0];
            } else {
                prev_chunk = out_buffer[index];
            }

            //count the length of the current chunk
            int length = 0;
            for (int e = 0; e <= BUFFER_MAX; e++) {
                if (prev_chunk[e]) {
                   length += 1;
                } else {
                   break;
                }
            }

            //The count of says at each iteration
            int count = 0;

            //say is a char byte from the previous chunk memory
            char say = prev_chunk[0];
            //skip is used in the iteration process 
            int skip = 0;

            //The idx into memory for the counts and says
            int idx = 0;

            //iteratively generate the count and say chunk for this index
            for (int i = 0; i < length; i++) {
                if (skip > 0) {
                    if (i < length - 1) {
                        say = prev_chunk[i + 1];
                    }
                    skip -= 1;
                    continue;
                }
                if (prev_chunk[i] == say) {
                    count += 1;
                    counts[idx] = count;
                    says[idx] = say;
                    //if at the end of the iteration add one
                    //as a terminator for the counts, says, pairs
                    if (i == length - 1) {
                        idx += 1;
                        break;
                    }
                } else {
                    count = 0;
                    if (i == length - 1) {
                        //finish off
                        idx += 1;
                        count += 1;
                        counts[idx] = count;
                        says[idx] = prev_chunk[i];
                        say = says[idx];
                        idx += 1;
                    } else {
                        idx += 1;
                        count += 1;
                        counts[idx] = count;
                        says[idx] = prev_chunk[i];
                        char next_say = prev_chunk[i + 1];
                        say = prev_chunk[i];
                        //Takes care of itself with idx
                        if (next_say != prev_chunk[i]) {
                            count = 0;
                            continue;
                        } 

                        int y = i;
                        while (next_say == say && y < length -1) {
                            count += 1;
                            //dont need to up the index because we are counting howmany there are
                            says[idx] = say;
                            counts[idx] = count;
                            //skip because this is the next loop 
                            skip += 1;
                            //subtract 1 because we want this to be in the final index slot
                            //count because we added an index
                            y += 1;
                            next_say = prev_chunk[y + 1];
                        }
                        idx += 1;
                        count = 0;
                    }
                }
            }      

            //Could have just generated the sequence from above but felt like doing this manually at the time
            //If I get around to it ill change  
            int chunk_offset = 0;
            char *temp = NULL;
            for (int u = 0; u < idx; u++) {
                 int c = counts[u];
                 //TODO: factor out or use sprintf, or maybe not, maybe this is just faster
                 char cc;
                 if (c >= 10) {
                     cc = 'A' + c - 10;
                 } else {
                     cc = '0' + c;
                 }

                 char say = says[u];
                 if (u == idx - 1) {
                     temp = realloc(temp, chunk_offset + 3);
                     if (chunk_offset > 0) {
                        for (int y = 0; y < chunk_offset; y++) {
                            temp[y] = out[y];
                        }

                        temp[chunk_offset] = cc;
                        temp[chunk_offset + 1] = say;
                        temp[chunk_offset + 2] = '\0';
                    } else {
                        temp[0] = cc;
                        temp[1] = say;
                        temp[2] = '\0';
                    }

                    out = realloc(out, chunk_offset + 3);
                    out = temp;
                    temp = NULL;
                    free(temp);
                } else {
                    temp = realloc(temp, chunk_offset + 2);
                    for (int ii = 0; ii < chunk_offset; ii++) {
                        temp[ii] = out[ii];
                    }
                    temp[chunk_offset] = cc;
                    temp[chunk_offset + 1] = say;
                    chunk_offset += 2;
                    out = realloc(out, chunk_offset + 2);
                    out = temp;
                    temp = NULL;
                    free(temp);
               }
           }        
         out_buffer[index] = out;
         out = NULL;
         free(out);
       }
     index += 1;
     size -= 1;
   }

   free(prev_chunk);
   prev_chunk = NULL;
   free(counts);
   counts = NULL;
   free(says);
   says = NULL;

   return out_buffer[n - 1];
 }

int main(int argc, char **argv) {
    char *cs = countAndSay(30);
    printf("count and say 30 = %s\n", cs);
}

Я знаю, что это не лучший алгоритм, но он работает. Насколько я понимаю, realloc можно использовать таким образом, чтобы избежать накапливая память malloc'd в кучу внутри цикла вроде как это работает. Я подозреваю, что таким образом он перемещает память в места, которые нелегко найти и освободить. Я на правильном пути с этим мышлением? Простое перераспределение

char *e = NULL;
for (int i = 0; i < 10; i++) {
    e = realloc(e, i + 1);
    if (i == 9) {
        e[i] = '\0';
    } else {
        e[i] = 'e';
    }
}
printf("%s\n", e);
free(e);

Доходность от валгринда:

==4421== LEAK SUMMARY:
==4421==    definitely lost: 0 bytes in 0 blocks
==4421==    indirectly lost: 0 bytes in 0 blocks
==4421==      possibly lost: 72 bytes in 3 blocks
==4421==    still reachable: 200 bytes in 6 blocks
==4421==         suppressed: 22,021 bytes in 152 blocks

Я выполнял это с Вальгриндом, пытавшимся решить проблему утечки. Я также видел подобное решение и попробовал их без удачи: «Освобожденный указатель не был выделен». ошибка после malloc, realloc .

Я полагаю, что главная проблема, с которой я здесь сталкиваюсь, заключается в том, что я malloc "вне" в первый раз. После этого я перераспределяю его каждый раз в цикле (ах). Valgrind дает самые большие утечки в строке main.c: 184 "out = realloc (out, chunk_offset + 2);". Кажется, realloc просто решает поместить эту память где-то в кучу, и я не могу добраться до нее. Я знаю, что адрес может измениться с realloc, но я все еще не смог получить его. Как я могу определенно потеряться до 0 в моем контратаках.

Ответы [ 3 ]

0 голосов
/ 08 января 2019

Давайте начнем с этого блока:

             if (u == idx - 1) {
                 temp = realloc(temp, chunk_offset + 3);
                 if (chunk_offset > 0) {
                    for (int y = 0; y < chunk_offset; y++) {
                        temp[y] = out[y];
                    }

                    temp[chunk_offset] = cc;
                    temp[chunk_offset + 1] = say;
                    temp[chunk_offset + 2] = '\0';
                } else {
                    temp[0] = cc;
                    temp[1] = say;
                    temp[2] = '\0';
                }

                out = realloc(out, chunk_offset + 3);
                //    *** MEMORY LEAK ***
                out = temp;
                temp = NULL;
                //    NOT NEEDED
                free(temp);
            } else {
                temp = realloc(temp, chunk_offset + 2);
                for (int ii = 0; ii < chunk_offset; ii++) {
                    temp[ii] = out[ii];
                }
                temp[chunk_offset] = cc;
                temp[chunk_offset + 1] = say;
                chunk_offset += 2;
                out = realloc(out, chunk_offset + 2);
                //    *** MEMORY LEAK ***
                out = temp;
                temp = NULL;
                //    NOT NEEDED
                free(temp);
           }

В обеих частях if вы увеличиваете размер out, но затем сразу же перезаписываете out значением temp, что приводит к утечке памяти, на которую указывал out.

Поскольку temp содержит нужные значения, вам больше не нужно, что находится в out, поэтому избавьтесь от realloc на out и вместо free того, что было ранее. Также нет необходимости в free(temp), поскольку он указывает на NULL, и вы можете заменить вызовы realloc на malloc, поскольку temp всегда будет NULL в этой точке:

             if (u == idx - 1) {
                 temp = malloc(chunk_offset + 3);
                 if (chunk_offset > 0) {
                    for (int y = 0; y < chunk_offset; y++) {
                        temp[y] = out[y];
                    }

                    temp[chunk_offset] = cc;
                    temp[chunk_offset + 1] = say;
                    temp[chunk_offset + 2] = '\0';
                } else {
                    temp[0] = cc;
                    temp[1] = say;
                    temp[2] = '\0';
                }
            } else {
                temp = malloc(chunk_offset + 2);
                for (int ii = 0; ii < chunk_offset; ii++) {
                    temp[ii] = out[ii];
                }
                temp[chunk_offset] = cc;
                temp[chunk_offset + 1] = say;
                chunk_offset += 2;
           }
           free(out);
           out = temp;

Тогда у вас есть это в нижней части вашего for цикла:

for (int s = 0; s < index; s++) {
     ...
     out_buffer[index] = out;
     out = NULL;
     free(out);
}

Вы перезаписываете содержимое out_buffer[index] на каждой итерации, что приводит к утечке памяти. Сначала вам нужно будет free старого содержимого, а также в конце избавиться от ненужного free(out), так как в этот момент оно содержит NULL. Это также означает, что вам нужно инициализировать out_buffer[index] в NULL перед входом в цикл.

out_buffer[index] = NULL;
for (int s = 0; s < index; s++) {
     ...
     free(out_buffer[index]);
     out_buffer[index] = out;
     out = NULL;
}

Тогда у вас есть проблема здесь:

    if (index == 0) {
        out = malloc(2 * sizeof(char));
        out[0] = '1';
        out[1] = '\0';
        prev_chunk = out;
        size -= 1;
        index += 1;
        out_buffer[0] = out;
        continue;
   }

Что не может установить out в NULL перед следующей итерацией цикла, что приведет к освобождению out_buffer[0] и последующему чтению из свободной памяти. Итак, добавьте это здесь:

    if (index == 0) {
        out = malloc(2 * sizeof(char));
        out[0] = '1';
        out[1] = '\0';
        prev_chunk = out;
        size -= 1;
        index += 1;
        out_buffer[0] = out;
        out = NULL;
        continue;
   }

Затем в конце:

free(prev_chunk);
prev_chunk = NULL;
free(counts);
counts = NULL;
free(says);
says = NULL;

return out_buffer[n - 1];

Вы не хотите free(prev_chunk);, поскольку он указывает на старую копию out, которая уже была освобождена. Вы также не освобождаете out_buffer или любые строки, на которые оно указывает. Конечно, вы не хотите освобождать возвращаемую строку, поэтому пропустите ее:

char *rval = out_buffer[n - 1];
for (int i = 0; i < n - 1; i++) {
     free(out_buffer[i]);
}
free(out_buffer);
free(counts);
free(says);

return rval;

Наконец, убедитесь, что вы free результат этой функции, как только main будет сделано с ней:

char *cs = countAndSay(30);
printf("count and say 30 = %s\n", cs);
free(cs);

Теперь у вас есть чистый прогон через valgrind без утечек памяти или неправильных ошибок чтения / записи / освобождения.

Кстати, в этом коде много недостатков. На каждой итерации внешнего цикла while вы генерируете весь список от 1 до текущего значения n. Таким образом, на первой итерации вы вычисляете n = 1, затем на следующей - n = 1,2, затем на следующей - n = 1,2,3 и т. Д.

Вам нужен только один цикл. Это также означает, что вам не нужно повторно использовать текущее значение out_buffer, а вместо этого можно просто сослаться на предыдущее. Я оставлю эти изменения в качестве упражнения для читателя.

0 голосов
/ 09 января 2019

Если мы посмотрим, как работает алгоритм Look-and-Say, мы обнаружим, что каждый символ (или последовательность одинаковых символов) создает пару символов в выходных данных. Таким образом, если длина ввода составляет N символов, длина вывода составляет не более 2 N символов.

Давайте рассмотрим функцию, которая генерирует строку next в последовательности Look-and-Say:

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

char *look_and_say(const char *src)
{
    const size_t  srclen = (src) ? strlen(src) : 0;
    char         *dst;

    if (srclen < 1) {
        /* Nothing to look or say. */
        return NULL;
    }

    /* The result can be up to twice as long as the source,
       plus the string-terminating nul char. */
    dst = malloc(2*srclen + 1);
    if (!dst) {
        /* Not enough memory for the result. */
        return NULL;
    }

    {
        const char *const  end = src + srclen;
        char              *pos = dst;

        while (src < end) {
            const char *const was = src;

            /* Skip repeated source characters. */
            do {
                src++;
            } while (src < end && *src == *was);

            /* The longest allowed sequence is 9. */
            if ((size_t)(src - was) > 9) {
                free(dst);
                return NULL;
            }

            *(pos++) = '0' + (size_t)(src - was);
            *(pos++) = *was;
        }

        *pos = '\0';

        return dst;
    }
}

Выше неважно, что является входной строкой; Вы можете поставить что угодно. Если входная строка NULL или пуста, она вернет NULL. Если он не может выделить память (вдвое больше длины входной строки, плюс один символ для завершающего строку символа nul '\0'), он возвращает NULL. Если символ повторяется более 9 раз подряд, функция возвращает NULL.

Последовательность Look-and-Say - OEIS A005150 в Онлайн-энциклопедии целочисленных последовательностей, которая указывает на то, что RG Wilson v показал в 2004 году только цифры 1, 2, и 3 существуют в последовательности. Таким образом, для целочисленной последовательности можно открыть код проверки, повторяется ли цифра (два или три раза).

Длина каждого члена в этой последовательности образует другую целочисленную последовательность, OEIS A005341 . Оказывается, что длина члена i составляет примерно 1,56 × 1,304 i (т.е. (size_t)(1.0 + 1.56*exp(0.26544*i)). 30-й член в последовательности составляет 4462 символа.

Если бы мы хотели сгенерировать каждое значение в последовательности, мы могли бы использовать один динамически управляемый буфер (для хранения генерируемого значения) и сохранить дубликат каждого результата:

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

char *sdup(const char *src, const size_t len)
{
    char *s;

    s = malloc(len + 1);
    if (!s) {
        fprintf(stderr, "sdup(): Not enough memory for a duplicate of %zu-character string.\n", len);
        return NULL;
    }

    memcpy(s, src, len);
    s[len] = '\0';

    return s;
}

/* Initial buffer size, at least 1. */
#ifndef  INITIAL_BUFFER_SIZE
#define  INITIAL_BUFFER_SIZE  1
#endif

char **look_and_say(const size_t  count)
{
    char  **table;
    char   *src, *end;
    char   *dst, *pos;
    size_t  len, max = INITIAL_BUFFER_SIZE;
    size_t  i, k;

    if (count < 1) {
        fprintf(stderr, "look_and_say(): Count is less than 1.\n");
        return NULL;
    }

    /* Allocate memory for the array of pointers. */
    table = calloc(count + 2, sizeof *table);
    if (!table) {
        fprintf(stderr, "look_and_say(): Not enough memory for an array of %zu strings.\n", count);
        return NULL;
    }

    /* First and last entries are NULLs; sentinels, if you will. */
    table[0] = NULL;
    table[count + 1] = NULL;

    /* Allocate memory for the dynamic buffer. */
    dst = malloc(max);
    if (!dst) {
        fprintf(stderr, "look_and_say(): Not enough memory for a %zu-character work buffer.\n", max);
        free(table);
        return NULL;
    }

    /* The sequence starts with "1". */
    dst[0] = '1';
    len = 1;

    /* Save that. */
    table[1] = sdup(dst, len);

    /* Loop over the rest of the entries to be generated. */
    for (i = 2; i <= count; i++) {
        /* The source is the last item in the sequence. */
        src = table[i - 1];
        end = table[i - 1] + len;

        /* Ensure we have enough room for the next value in the sequence. */
        if (2*len > max) {
            /* TODO: Better growth policy! */
            max = 2*len;

            pos = realloc(dst, max);
            if (!pos) {
                fprintf(stderr, "look_and_say(): Not enough memory to grow work buffer to %zu chars.\n", max);
                free(dst);
                for (k = 1; k < i; k++)
                    free(table[k]);
                free(table);
                return NULL;
            }
            dst = pos;
        } else
            pos = dst;

        /* Source is [src, end[. pos is the next position in work buffer. */
        while (src < end) {
            const int  nc = *(src++);
            int        nn = '1';

            /* Skip if source is repeated twice or three times. */
            if (*src == nc) {
                src++;
                nn++;   /* 2 */
                if (*src == nc) {
                    src++;
                    nn++; /* 3 */
                }
            }

            *(pos++) = nn;
            *(pos++) = nc;
        }

        /* Length of the generated value. */
        len = (size_t)(pos - dst);

        /* Save to table. */
        table[i] = sdup(dst, len);
        if (!table[i]) {
            free(dst);
            for (k = 1; k < i; k++)
                free(table[i]);
            free(table);
            return NULL;
        }
    }

    /* Dynamic buffer is no longer needed. */
    free(dst);

    return table;
}

#ifndef  LIMIT
#define  LIMIT  30
#endif

int main(void)
{
    char **sequence;
    size_t i;

    sequence = look_and_say(LIMIT);
    if (!sequence)
        exit(EXIT_FAILURE);

    for (i = 1; i <= LIMIT; i++)
        printf("%zu. %s\n", i, sequence[i]);

    for (i = 1; i <= LIMIT; i++)
        free(sequence[i]);
    free(sequence);
    return EXIT_SUCCESS;
}

На этом этапе мы должны отметить, что у нас уже есть алгоритм O ( N ) для генерации следующего значения в последовательности, где N является длина предыдущего значения. Чтобы достичь лучшей производительности, чем линейная, требуется лучший алгоритм, но, насколько мне известно, для этого не существует тривиального решения, лучше линейного.

Конечно, мы можем оптимизировать приведенный выше код на уровне кода; но его временная сложность уже достаточно хороша.

Если бы мы хотели «обмануть», мы могли бы заметить, что длины первых тридцати членов в последовательности составляют 1, 2, 2, 4, 6, 6, 8, 10, 14, 20, 26, 34, 46, 62, 78, 102, 134, 176, 226, 302, 408, 528, 678, 904, 1182, 1540, 2012, 2606, 3410, 4462. Это означает, что если мы выделим достаточно памяти для указателей и 19019 символов (сумма длин + 30 для символов конца строки), нам нужно только одно выделение. Если мы зарезервируем нулевой указатель для NULL, то это распределение будет malloc(19019 + 31 * sizeof (char *)).

Однако, продолжая этот путь, мы получаем следующий код или что-то очень похожее:

static const char  term_1[] = "1";
static const char  term_2[] = "11";
static const char  term_3[] = "21";
static const char  term_4[] = "1211";
static const char  term_5[] = "111221";
/* term_6[] through term_30[] omitted */

static const char *sequence[31] = {
    NULL,
    term_1,  term_2,  term_3,  term_4,  term_5,
    term_6,  term_7,  term_8,  term_9,  term_10,
    term_11, term_12, term_13, term_14, term_15,
    term_16, term_17, term_18, term_19, term_20,
    term_21, term_22, term_23, term_24, term_25,
    term_26, term_27, term_28, term_29, term_30
};

Это генерирует около 19 КиБ только для чтения (неизменяемых) данных в сгенерированном двоичном файле. Это не будет проблемой даже для многих микроконтроллеров, если эта последовательность будет иметь решающее значение для работы.

Если использование памяти является проблемой, можно просто упаковать каждую отдельную цифру в два бита, сохраняя разумное время доступа, и в этом случае для данных используется только около 5 КиБ памяти.

0 голосов
/ 06 января 2019

Ваша проблема в том, что вы освобождаете указатель, выделенный в конце цикла, даже если вы сохранили указатель для потомков или чего-то еще.

На основании этого кода я бы сказал, что вы хотите сделать несколько вещей:

  1. Не освобождайте память, с которой вы не закончили только потому, что вы сделали с переменной, которая держала указатель.
  2. Измените это значение на malloc, а не на realloc, потому что вам явно требуется отдельный пробел для каждого прохода в цикле.
  3. Получите редактор, который поможет вам с отступом, или научитесь настраивать редактор, который вы используете, чтобы помочь с отступом. Это может звучать как мнение, но это похоже на проблему, которую я обнаружил, намного легче диагностировать, если ваш отступ что-то значит. Это, вероятно, также поможет вам получить помощь, потому что немногие опытные программисты хотят иметь дело с такими противоречивыми отступами. У меня сложилось впечатление, что я один из немногих, кто видит что-то подобное и решает вставить это в vim, а затем набрать =gg, чтобы переформатировать все, чтобы быть разумным. (Примечание: если вы не используете vi, nvi или vim, не используйте его только потому, что я использую - все три из этих редакторов имеют большую кривую обучения, и большинство редакторов программирования могут делать то же самое.)

И просто для подтверждения последнего пункта: как только я переформатировал его, чтобы иметь последовательный отступ, проблема выскочила на меня. У Тома почти все получилось, но я думаю, он думал, что это конец вашей функции, а не конец вашей петли.

...