Question

I'm currently helping a friend with his assignments from his university, and have a moral dilemma regarding the behaviour of realloc when passing a NULL pointer (manuals say it should work exactly as a normal malloc).

So here's the code:

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

typedef struct {
  char *line;
} node_t;

typedef struct {
  node_t **nodes;
  int line_count;
} buffer_t;

void print_LIFO(buffer_t buffer){
  int i;

  for(i=buffer.line_count;i>=0;i--){
    printf("%s",buffer.nodes[i]->line);
  }

}

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

  FILE *fp;

  char fline[2048];

  int i;

  buffer_t buffer;

  buffer.line_count = 0;

  if(argc < 2){
    //no params, no glory: read from STDIN

    //initial malloc so GDB and valgrind shut the f*** up
    buffer.nodes = (node_t **)malloc(sizeof(node_t *));

    while(fgets(fline,2048,stdin) != NULL){

      //do our stuff
      node_t ** tPtr = NULL;

      tPtr = (node_t **)realloc(buffer.nodes,sizeof(node_t *) * (buffer.line_count + 1));
      if(tPtr == NULL){
        //free memory!
        for(i=0;i<buffer.line_count;i++){
          free(buffer.nodes[i]->line); //free stuff alloc'ed with strdup
          free(buffer.nodes[i]); // free current node        
        }
        free(buffer.nodes);
        exit(EX_OSERR);
      }
      buffer.nodes = tPtr;
      buffer.nodes[buffer.line_count] = (node_t *)malloc(sizeof(node_t)); //alloc space for full node_t at the previously allocated node_t pointer

      if(buffer.nodes[buffer.line_count] == NULL){
        //free memory!
        for(i=0;i<buffer.line_count;i++){
          free(buffer.nodes[i]->line); //free stuff alloc'ed with strdup
          free(buffer.nodes[i]); // free current node
        } 
        free(buffer.nodes);
        exit(EX_OSERR);
      }

      buffer.nodes[buffer.line_count]->line = strdup(fline);

      buffer.line_count++;

    }   

    buffer.line_count--;

  } else {

    if(argv[1][1] == 'h'){
      printf("bocabajo: Uso: bocabajo [ fichero... ]\n");
      printf("bocabajo: Invierte el orden de las l ́neas de los ficheros (o de la entrada).\n");
      exit(EX_OK);
    }

    //check number of files to open, loop thru them
    for(i=1;i<argc;i++){
      fp = fopen(argv[i],"r");

      if(!fp){ // error check
        printf("bocabajo: Error(EX_NOINPUT),\n");
        printf("bocabajo+ El fichero \"%s\" no puede ser leido\n",argv[i]);
        exit(EX_NOINPUT);
      } 

      //initial malloc so GDB and valgrind shut the f*** up
      buffer.nodes = (node_t **)malloc(sizeof(node_t *));

      //do our magic
      while(fgets(fline,2048,fp) != NULL){

        //do our stuff
        node_t ** tPtr = NULL;

        tPtr = (node_t **)realloc(buffer.nodes,sizeof(node_t *) * (buffer.line_count + 1));
        if(tPtr == NULL){
          //free memory!
          for(i=0;i<buffer.line_count;i++){
            free(buffer.nodes[i]->line); //free stuff alloc'ed with strdup
            free(buffer.nodes[i]); // free current node
          } 
          free(buffer.nodes); // free last pointer
          exit(EX_OSERR);
        }
        buffer.nodes = tPtr;
        buffer.nodes[buffer.line_count] = (node_t *)malloc(sizeof(node_t)); //alloc space for full node_t at the previously allocated node_t pointer

        if(buffer.nodes[buffer.line_count] == NULL){
          //free memory!
          for(i=0;i<buffer.line_count;i++){
            free(buffer.nodes[i]->line); //free stuff alloc'ed with strdup
            free(buffer.nodes[i]); // free current node
          } 
          free(buffer.nodes); // free last pointer
          exit(EX_OSERR);
        }

        buffer.nodes[buffer.line_count]->line = strdup(fline);

        buffer.line_count++;

      } 

      buffer.line_count--;

      //close the file descriptor
      fclose(fp);
    }

  } 

  print_LIFO(buffer);

  //free memory
  for(i=0;i<=buffer.line_count;i++){
    free(buffer.nodes[i]->line); //free stuff alloc'ed with strdup
    free(buffer.nodes[i]); // free current node
  } 

  free(buffer.nodes);  

  exit(EX_OK);
}

Now, to the important part:

If I don't specifically do the malloc like this

//initial malloc so GDB and valgrind shut the f*** up
buffer.nodes = (node_t **)malloc(sizeof(node_t *));

The automatic assignment checker will spit its brains out, as well as Valgrind when running on my local computer

An example:

*** glibc detected *** ./bocabajo: realloc(): invalid pointer: 0x000000387d221188 ***
======= Backtrace: =========
/lib64/libc.so.6[0x387d476126]
/lib64/libc.so.6(realloc+0x2e2)[0x387d47bee2]
./bocabajo[0x400a69]
/lib64/libc.so.6(__libc_start_main+0xfd)[0x387d41ecdd]
./bocabajo[0x4008f9]
======= Memory map: ========
00400000-00402000 r-xp 00000000 fd:02 1836590                            /home/jail/homefi/dep/pps/pps/2013-2014/tarea-2.2/u120182/bocabajo
00601000-00602000 rw-p 00001000 fd:02 1836590                            /home/jail/homefi/dep/pps/pps/2013-2014/tarea-2.2/u120182/bocabajo
0119e000-011bf000 rw-p 00000000 00:00 0                                  [heap]
387d000000-387d020000 r-xp 00000000 fd:00 262331                         /lib64/ld-2.12.so
387d21f000-387d220000 r--p 0001f000 fd:00 262331                         /lib64/ld-2.12.so
387d220000-387d221000 rw-p 00020000 fd:00 262331                         /lib64/ld-2.12.so
387d221000-387d222000 rw-p 00000000 00:00 0 
387d400000-387d58a000 r-xp 00000000 fd:00 262340                         /lib64/libc-2.12.so
387d58a000-387d789000 ---p 0018a000 fd:00 262340                         /lib64/libc-2.12.so
387d789000-387d78d000 r--p 00189000 fd:00 262340                         /lib64/libc-2.12.so
387d78d000-387d78e000 rw-p 0018d000 fd:00 262340                         /lib64/libc-2.12.so
387d78e000-387d793000 rw-p 00000000 00:00 0 
387e000000-387e083000 r-xp 00000000 fd:00 265418                         /lib64/libm-2.12.so
387e083000-387e282000 ---p 00083000 fd:00 265418                         /lib64/libm-2.12.so
387e282000-387e283000 r--p 00082000 fd:00 265418                         /lib64/libm-2.12.so
387e283000-387e284000 rw-p 00083000 fd:00 265418                         /lib64/libm-2.12.so
3888400000-3888416000 r-xp 00000000 fd:00 271606                         /lib64/libgcc_s-4.4.7-20120601.so.1
3888416000-3888615000 ---p 00016000 fd:00 271606                         /lib64/libgcc_s-4.4.7-20120601.so.1
3888615000-3888616000 rw-p 00015000 fd:00 271606                         /lib64/libgcc_s-4.4.7-20120601.so.1
7f48c4947000-7f48c494a000 rw-p 00000000 00:00 0 
7f48c4960000-7f48c4963000 rw-p 00000000 00:00 0 
7fff78ff2000-7fff79007000 rw-p 00000000 00:00 0                          [stack]
7fff79151000-7fff79152000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

Valgrind says something along the line of:

==20165== Invalid free() / delete / delete[]
==20165==    at 0x4C245E2: realloc (vg_replace_malloc.c:525)
==20165==    by 0x4008A0: main (bocabajo.c:47)
==20165==  Address 0x340 is not stack'd, malloc'd or (recently) free'd

So... my question is, why does the debugger even complain? Doesn't the manual specify that realloc should act as a normal malloc when the pointer passed to it is NULL?

The program when not attached to any type of debugger works just fine (and when attached does in fact work OK, but the debugger complains like I've mentioned above, though the automatic assignment checker regards it as a failure).

Can anyone shed some light as to why this is happening?

Was it helpful?

Solution

I think you should set line_count to 1 after the initial malloc

 buffer.nodes = malloc(sizeof(node_t *));
 buffer.line_count = 1;

I would also remove the trailing buffer.line_count-- and instead handle it in the print_LIFO, it seems more robust. In addition instead of passing a copy of buffer, pass a pointer to the struct instead - if nothing else it should be faster.

void print_LIFO(buffer_t* buffer) 
{
  int i;

  for(i=buffer->line_count;i>0;i--)
  {
    printf("%s",buffer->nodes[i-1]->line);
  }
}

always good to have some tests in a function e.g.

void print_LIFO(buffer_t* buffer) 
{
  int i;

  if ( buffer != NULL && buffer->line_count > 0 )
  {    
    for(i=buffer->line_count;i>0;i--)
    {
      printf("%s",buffer->nodes[i-1]->line);
    }
  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top