Home > Net >  realloc not working despite using it on a malloc'ed string
realloc not working despite using it on a malloc'ed string

Time:09-17

I'm not really sure why this is happening but in the code below I made a simple program to help me understand how malloc works. I was basically trying to see how resizing arrays works in term of memory addresses.

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

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

    
    char* buff;
    buff = malloc(10);
    buff = "hi";
    printf("%c\n", buff[0]);
    printf("%p\n", &buff[0]);

   if (realloc(buff, (size_t) 20) == NULL){
       printf("no\n");
       exit(1);
   }
    
    printf("%p\n", &buff[0]);
    
    return 0;
}

But when I run it, realloc returns NULL. Here it is running with valgrind.

valgrind ./test
==3421== Memcheck, a memory error detector
==3421== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==3421== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==3421== Command: ./test
==3421==
h
0x400734
==3421== Invalid free() / delete / delete[] / realloc()
==3421==    at 0x4C2CE8E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3421==    by 0x400673: main (in /home/puf311/cs3733/practice/test)
==3421==  Address 0x400734 is not stack'd, malloc'd or (recently) free'd
==3421==
no==3421==
==3421== HEAP SUMMARY:
==3421==     in use at exit: 10 bytes in 1 blocks
==3421==   total heap usage: 2 allocs, 1 frees, 15 bytes allocated
==3421==
==3421== LEAK SUMMARY:
==3421==    definitely lost: 10 bytes in 1 blocks
==3421==    indirectly lost: 0 bytes in 0 blocks
==3421==      possibly lost: 0 bytes in 0 blocks
==3421==    still reachable: 0 bytes in 0 blocks
==3421==         suppressed: 0 bytes in 0 blocks
==3421== Rerun with --leak-check=full to see details of leaked memory
==3421==
==3421== For counts of detected and suppressed errors, rerun with: -v
==3421== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

As far as I know, the string is malloc'ed, and I definitely didn't free it earlier. So, what's going on here?

CodePudding user response:

Here:

buff = malloc(10);
buff = "hi";

You've reassigned the address held in buff...

You probably meant:

strcpy( buff, "hi" );

And, you need to capture the returned pointer from realloc() in case that function has relocated your data to grow/shrink the buffer.

Further, ALWAYS check return codes from any system call. You probably test if fopen() returned NULL. Well, memory allocation can fail, too!

char *buff = malloc( 10 );
if( buff == NULL ) {
    fprintf( stderr, "malloc failed\n" );
    exit( EXIT_FAILURE ); // probably not going to get much further
}

EDIT: That was too easy. Here's an annotated version that (omitting typos) should show how you should/could use realloc()

int main() {
    char *buff = malloc( 10 );
    if( buff == NULL ) {
        fprintf( stderr, "Failed to launch!\n" );
        exit( EXIT_FAILURE );
    }

    strcpy( buff, "hi" ); // should fit!
    puts( buff ); // working???

    printf( "%c\n", buff[0] ); // should be 'h'

    printf( "%p\n", &buff[0] ); // should be the same as...
    printf( "%p\n",  buff    ); // this

    { // NB: 'tmp' is only within the 'scope' of this pair of braces
        char *tmp = realloc( buff, 20 ); // delicately
        if ( tmp == NULL ) {
            // Here it is, but this need not be a "hard error"
            // Report problem, but could perhaps continue with same sized buffer, somehow...
            fprintf( stderr, "Cannot grow the buffer, but,,, ");
            printf( "still have '%s' and 'Good-bye'\n", buff );
            free( buff );
            exit( EXIT_FAILURE);
        }
        buff = tmp; // Got it. (Don't replace if "soft error" of tmp being NULL, of course)
    }

    printf( "%p\n", buff ); // just to see if block relocated

    free( buff ); // don't forget about this

    return 0;
}

Caveat: It's up to you, the programmer who 'reserves' blocks of heap memory, to track how big that block is and not "scribble" outside of that playground. free() will do the right thing, but between malloc() and free(), and made more complex with realloc() there is a lot of opportunity to overrun the buffer and invoke "undefined behaviour".

Taking this one step further...

In fact, on some occasions the code is better using only realloc() that "acts like malloc()" when it receives a NULL as the first parameter...

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

int main() {
    char sentence[] = "The  quick brown   fox jumps   over the lazy dog";
    char **words = NULL;

    // Chop up a mutable string (sentence), isolating each word
    // accessible via its own pointer.
    // realloc() "grows" the array of pointers to words.
    int i = 0; 
    for( char *p = sentence; (p = strtok( p, " " )) != NULL; p = NULL ) {
        char **tmp = realloc( words, (i 1) * sizeof *tmp );
        if ( tmp == NULL ) {
            fprintf( stderr, "realloc failed on %d - %s\n", i, p );
            free( words );
            exit( EXIT_FAILURE);
        }
        words = tmp;
        words[ i   ] = p;
        printf( "cnt(%d) ", i ); // debugging
    }
    putchar( '\n' ); // debugging

    while( i-- ) // reverse order
        puts( words[i] ); 

    free( words );

    return 0;
}

Output

cnt(1) cnt(2) cnt(3) cnt(4) cnt(5) cnt(6) cnt(7) cnt(8) cnt(9)
dog
lazy
the
over
jumps
fox
brown
quick
The
  • Related