袁庭宁
袁庭宁

Reputation: 1

How do I use free() properly to free memory when using malloc for char?

I attempt to malloc char** to store string, and free this, but I got this error. I can't understand why. The steps are as follows:

1: char **pid_array = (char **)malloc(sizeof(char *) * MAX_LEN);

2: pid_array[0] = (char *)malloc(sizeof(char) * SINGLE_LEN * MAX_LEN);

3: free(pid_array); free(pid_array[0]);

The detailed code follows:

#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define MAX_LEN 1000
#define SINGLE_LEN 10

int isPid(char *str) {
  int len = strlen(str);
  for (int i = 0; i < len; i++) {
    if (isdigit(str[i]) == 0) {
      return 1;
    }
  }
  return 0;
}
void getFileName(char *dir_path, char *pid_array[], int *len) {
  DIR *dir = opendir(dir_path);
  if (dir == NULL) {
    fprintf(stderr, "path open failed!\n");
    exit(EXIT_FAILURE);
  }
  chdir(dir_path);
  struct dirent *ent;
  int i = 0;
  while ((ent = readdir(dir)) != NULL) {
    if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
      continue;
    }
    int size = strlen(ent->d_name);
    if (isPid(ent->d_name) == 0) {
      pid_array[i++] = ent->d_name;
    }
  }
  *len = i;
  closedir(dir);
}

int main(int argc, char *argv[]) {
  int pflag, nflag, vflag;
  pflag = 0;
  nflag = 0;
  vflag = 0;
  int opt;
  while ((opt = getopt(argc, argv, "pvn")) != -1) {
    switch (opt) {
      case 'p':
        pflag = 1;
        break;
      case 'v':
        vflag = 1;
        break;
      case 'n':
        nflag = 1;
        break;
    }
  }
  printf("pflag=%d; nflag=%d; vflag=%d; optind=%d\n", pflag, nflag, vflag, optind);
  char **pid_array = (char **)malloc(sizeof(char *) * MAX_LEN);


  pid_array[0] = (char *)malloc(sizeof(char) * SINGLE_LEN * MAX_LEN);
  for(int i=0; i < MAX_LEN; i++){
     pid_array[i]=pid_array[i-1]+SINGLE_LEN;
  }
/*
  for (int i = 0; i < MAX_LEN; i++) {
    pid_array[i] = (char *)malloc(sizeof(char) * SINGLE_LEN);
    assert(pid_array[i] != NULL);
  }
*/
  for (int i = 0; i < MAX_LEN; i++) {
    free(pid_array[i]);
  }

  int *pid_array_len = (int *)malloc(sizeof(int));
  getFileName("/proc", pid_array, pid_array_len);
  for (int i = 0; i < *pid_array_len; i++) {
    printf("%d\n", atoi(pid_array[i]));
  }

  free(pid_array);
  free(pid_array[0]);
  free(pid_array_len);
  return 0;
}

The error is follow: error

Upvotes: 0

Views: 52

Answers (1)

arfneto
arfneto

Reputation: 1765

The steps as noted are not correct.

if pid_array is char** then

  • *pid_array is char*
  • **pid_array is char

And you need to construct them as such. And free them in the reverse order. If you intend to have a vector of pointers at pid_array then your case is very very common: every C program gets one for free. The main prototype can be declared as

    int main(int argc, char**argv);

The system knows how many char* to pass to the program, but in your case maybe the simplest (safest) way is to use encapsulation and build a block like this

typedef struct
{
    size_t argc;
    char** argv;
} Block;

I will let an example below.

a way to free the block properly

If you insist in using just the pointer you can easily adapt this. Anyway a possible implementation is

Block* delete (Block* blk)
{
    if (blk == NULL) return NULL;
    fprintf(
        stderr, "Deleting block of %llu strings\n",
        blk->argc);
    for (int i = 0; i < blk->argc; i += 1)
        free(blk->argv[i]);
    free(blk->argv);
    free(blk);
    fprintf(stderr, "Deleted...\n");
    return NULL;
}

The reason to return a pointer is to create a simple way to assure the pointer is invalidated as in

    my_block = delete (my_block);

In the example

  • A block is created
  • is filled with strings of random size
  • the strings are printed
  • the block is deleted

main for the example

int main(void)
{
    srand(220630);
    const int size     = MAX_LEN;
    Block*    my_block = build(size);
    fill(my_block);
    show(my_block, "a vector of numbered strings");
    my_block = delete (my_block);
    return 0;
}

the output

a vector of numbered strings
25 strings:
1        "#000#k"
2        "#001#swfsxji"
3        "#002#cn"
4        "#003#akmxhksqgb"
5        "#004#dqnegzryobmhucldx"
6        "#005#iiuqddvuvukkrs"
7        "#006#jxvlsolocgnvgjcrwh"
8        "#007#zylbzumyhmeswxuno"
9        "#008#ex"
10       "#009#ixinxqyxqydnswb"
11       "#010#ylxelydzqgs"
12       "#011#absdfpdjvgwhxcmzekr"
13       "#012#sceqzvmjskkrmszpth"
14       "#013#n"
15       "#014#rsmkrqhssjniqgphjp"
16       "#015#dgojvpflydevwudvv"
17       "#016#qbmaolgrskkqghhkgb"
18       "#017#uzsunopqpdawg"
19       "#018#rvdeaiooylywf"
20       "#019#zfejmgqxu"
21       "#020#fjubcmllylxqahvbfh"
22       "#021#zwanyivra"
23       "#022#vooropiugmuya"
24       "#023#js"
25       "#024#qzecia"

Deleting block of 25 strings
Deleted...

The complete C code

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

#define MAX_LEN 25

typedef struct
{
    size_t argc;
    char** argv;
} Block;

Block* build(size_t ttl);
Block* delete (Block* blk);
int fill(Block* bl);
int show(Block* blk, const char* title);

int main(void)
{
    srand(220630);
    const int size     = MAX_LEN;
    Block*    my_block = build(size);
    fill(my_block);
    show(my_block, "a vector of numbered strings");
    my_block = delete (my_block);
    return 0;
}

Block* build(size_t ttl)
{
    if (ttl == 0) return NULL;
    Block* blk = (Block*)malloc(sizeof(Block));
    if (blk == NULL) return NULL;
    blk->argc = (ttl > MAX_LEN) ? MAX_LEN : ttl;
    blk->argv = (char**)malloc(ttl * sizeof(char*));
    if (blk->argv == NULL) return NULL;
    for (int i = 0; i < ttl; i += 1)
        *(blk->argv + i) = NULL;
    return blk;
}

int fill(Block* bl)
{
    const char prefix[]   = "#nnn#";  // common prefix
    char       buffer[30] = {0};
    char       data[20]   = {0};
    for (int i = 0; i < bl->argc; i += 1)
    {
        int rest = 1 + rand() % 19;
        for (int j = 0; j < rest; j += 1)
            data[j] = 'a' + rand() % 26;  // a single letter
        data[rest]  = 0;  // terminates string
        int res     = sprintf(buffer, "#%03d#%s", i, data);
        bl->argv[i] = (char*)malloc(strlen(buffer) + 1);
        strcpy(bl->argv[i], buffer);
    }
    return 0;
}

int show(Block* blk, const char* title)
{
    if (title != NULL) printf("%s\n", title);
    printf("%llu strings:\n", blk->argc);
    for (int i = 0; i < MAX_LEN; i += 1)
        printf("%d\t \"%s\"\n", 1 + i, *(blk->argv + i));
    printf("\n");
    return 0;
}

Block* delete (Block* blk)
{
    if (blk == NULL) return NULL;
    fprintf(
        stderr, "Deleting block of %llu strings\n",
        blk->argc);
    for (int i = 0; i < blk->argc; i += 1)
        free(blk->argv[i]);
    free(blk->argv);
    free(blk);
    fprintf(stderr, "Deleted...\n");
    return NULL;
}

// https://stackoverflow.com/questions/72809939/
// how-do-i-use-free-properly-to-free-memory-when
// -using-malloc-for-char

Upvotes: 1

Related Questions