I'm allocating an array of pointers that are dynamically allocated and I try to free them at the end of my program. The problem is that I always get a "invalid free()" error on valgrind, though I really couldn't find what is wrong. For an example: I'm allocating memory using malloc for argv[0] and argv1, and then try to free them in the for loop. I allocate the array of pointers using:
char ucom[10], bin[15] = "/bin/";
char* str = (char *) malloc(MAX_LINE_LENGTH*sizeof(char));
char** argv = (char **) malloc(sizeof(char*)); //ALLOCATING MEMORY FOR ARGV
int status, i = 0;
printf("Shell2$**");
strcpy(bin, "/bin/");
fgets(str, MAX_LINE_LENGTH, stdin);
char *token;
token = strsep(&str, " ");
while(token != NULL && i < 4){
if(token[strlen(token)-1] == '\n')
token[strlen(token)-1] = '\0';
argv[i] = (char *) malloc(MAX_ARGUMENT_LENGTH*sizeof(char)); //ALLOCATING MEMORY FOR POINTERS INSIDE ARGV, running two times in my example
printf("\n\nI:%d\n\n",i);
if(argv[i] == NULL) printf("Memory Allocation Problem");
argv[i] = token;
token = strsep(&str, " ");
i++;
argv = (char **)realloc(argv, (i+2)*sizeof(char*));
}
then I try to free them up:
wait(&status);
for(int f = 0; f < i; f++){
if(argv[f] != NULL)
free(argv[f]); //Free runs two times as the number of time malloc has been called, but fails at the second free.
}
free(str);
free(argv);
even though the malloc ran 2 times in my example, which allocated memory for argv[0] and argv1, when the for loop at the end tries to free argv1, it fails and valgrind says it's a free invalid, but it succeeded freeing argv[0].
Thank you all in advance!
The output from valgrind: LINK
There is more wrong than just what @FredLarson and I have already pointed out.
The function strsep()
actually modifies the pointer handed to it, so char *token = strsep(&str, " ")
; changes the pointer. Since str
was gotten from malloc
, it can't be freed properly.
If you want to call strsep()
on malloc'd memory, you have to save a copy of the original pointer and free it at the end.
Also, the not strictly portable but highly available function strdup()
is really helpful for making a copy of a string, so rather than allocate a bunch of memory and then copy a string to it, you can just strdup()
that string (and free it later).
#define _BSD_SOURCE // for strsep
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_LINE_LENGTH 80
#define MAX_ARGUMENT_LENGTH 10 // made up numbers
int main()
{
char *str = malloc(MAX_LINE_LENGTH);
char *str_save = str;
char **argv = malloc(sizeof(char*)); //ALLOCATING MEMORY FOR ARGV
int i = 0;
printf("Shell2$**"); fflush(stdout); // make sure user sees the prompt
strcpy(bin, "/bin/");
fgets(str, MAX_LINE_LENGTH, stdin);
char *token;
while ( (token = strsep(&str, " ")) != NULL && i < 4 )
{
if(token[strlen(token)-1] == '\n')
token[strlen(token)-1] = '\0';
argv[i] = strdup(token);
if(argv[i] == NULL) printf("Memory Allocation Problem");
i++;
argv = realloc(argv, i * sizeof(char*));
}
argv[i] = NULL; // GOOD IDEA TO ADD THIS
// run the shell
// wait(&status);
for(int f = 0; f < i; f++){
if(argv[f] != NULL)
free(argv[f]);
}
free(str_save);
free(argv);
}
This does essentially what yours does, but in practice you probably don't even need to allocate the memory for the input line. Why not just define a local buffer? Then you don't have to allocate for the line buffer or the tokens because they all live in linebuffer
:
#define _BSD_SOURCE // for strsep
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_LINE_LENGTH 80
int main()
{
char **argv = malloc(sizeof(char*)); //ALLOCATING MEMORY FOR ARGV
int i = 0;
printf("Shell2$**"); fflush(stdout); // make sure user sees the prompt
char linebuf[MAX_LINE_LENGTH];
fgets(linebuf, sizeof linebuf, stdin);
char *token;
char *str = linebuf;
while ( (token = strsep(&str, " ")) != NULL && i < 4 )
{
if (token[strlen(token)-1] == '\n')
token[strlen(token)-1] = '\0';
argv[i++] = token;
argv = realloc(argv, i * sizeof(char*));
}
argv[i] = NULL; // GOOD IDEA TO ADD THIS
// run the shell
// wait(&status);
free(argv);
}
This removes a whole lot of complications with - as far as I can tell - no less safety.