Home > Net >  Update array of strings in function
Update array of strings in function

Time:10-30

I have a working example of copy lines from a file into an array of strings. I want to move the code to copy the lines into a function to which I simply pass a pointer to the array of strings, where the lines will be stored, and a pointer to the file. However, I have tried to move the code into a function and keep getting seg faults. I have tried debugging using GDB and it seems like the problem is with the memory allocation to rows. But I can't work out what the problem is. realloc seems to be working correctly since I find the size of row increases on the 3rd iteration (using malloc_usable_size(*rows)), but then seg faults. I'm compiling with gcc -Wall -Wextra -pedantic -std=c99 -g c_programs/read_file_function.c on Linux.

Working example

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

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

  if (argc != 2)
  {
    fprintf(stderr, "Please supply a file path:\n%s <file path>\n", argv[0]);
    return EXIT_FAILURE;
  }
  FILE *fp = fopen(argv[1], "r");
  if (!fp)
  {
    perror("ERROR");
    return EXIT_FAILURE;
  }

  char **rows = (char **)malloc(sizeof(char *));
  char *lineBuf = NULL;
  size_t n = 0;
  size_t nLines = 0;
  ssize_t lineLength = 0;
  size_t i = 0;

  while ((lineLength = getline(&lineBuf, &n, fp)) != -1)
  {
    lineBuf[strcspn(lineBuf, "\n")] = 0;
    lineBuf[strcspn(lineBuf, "\r")] = 0;

    rows[i] = (char *)malloc(lineLength   1);
    strcpy(rows[i], lineBuf);
    i  ;
    nLines = i;
    rows = (char **)realloc(rows, (nLines   1) * sizeof(char *));
  }

  printf("nLines: %lu\n", nLines);
  printf("row 1: %s\n", rows[0]);
  printf("row 2: %s\n", rows[1]);
  printf("row 2: %s\n", rows[10]);

  return 0;
}

Non working function version

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

size_t readFile(FILE **fp, char ***rows)
{
  char *lineBuf = NULL;
  size_t n = 0;
  size_t nLines = 0;
  ssize_t lineLength = 0;
  size_t i = 0;

  while ((lineLength = getline(&lineBuf, &n, *fp)) != -1)
  {
    lineBuf[strcspn(lineBuf, "\n")] = 0;
    lineBuf[strcspn(lineBuf, "\r")] = 0;

    *rows[i] = (char *)malloc(lineLength   1);
    strcpy(*rows[i], lineBuf);
    i  ;
    nLines = i;
    *rows = (char **)realloc(*rows, (nLines   1) * sizeof(char *));
  }
  return nLines;
}

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

  if (argc != 2)
  {
    fprintf(stderr, "Please supply a file path:\n%s <file path>\n", argv[0]);
    return EXIT_FAILURE;
  }
  FILE *fp = fopen(argv[1], "r");
  if (!fp)
  {
    perror("ERROR");
    return EXIT_FAILURE;
  }

  char **rows = (char **)malloc(sizeof(char *));
  size_t nLines = readFile(&fp, &rows);

  printf("nLines: %lu", nLines);
  printf("row 1: %s", rows[0]);
  printf("row 2: %s", rows[1]);

  return 0;
}

CodePudding user response:

*rows[i] is doing *(rows[i]) - accessing ith element in the array of rows, and then dereferencing it. You want to do (*rows)[i] - dereference rows and then access ith element.

I advise to:

readFile(..., char ***rows0) {
    char **rows = NULL; // temporary internal variable

    ...
       // use rows normally
       rows = stuff();
    ...
   
    // when finished, assign once
    *rows0 = rows;
    return nLines;
}

But do not be a 3-star programmer. At best, use a structure, -> is easy to use. Like:

struct string {
    char *str;
};

struct lines {
    struct string *strs;
    size_t cnt;
};


// @return 0 on success, otherwise error
int readFile(...., struct lines *p) {
    // initialization
    p->cnt = 0;
    p->strs = NULL;

    ...
       void *pnt = realloc(p->strs, (p->cnt   1) * ....);
       if (!pnt) { /* handle error */ return -1; }
       p->strs = pnt;
       p->strs[p->cnt]->str = malloc(lineLenght   1);
       if (!p->strs[p->cnt]->str) { /* handle error */ return -2; }
       strcpy(p->strs[p->cnt]->str, lineBuf);
       p->cnt  ;
    ...

    return 0; /* success */
}
    

int main(int argc, char **argv) {
   struct lines p = {0};
   if (readFile(..., &p)) {
       /* handle error */
   }
   printf("nLines: %zu\n", p.cnt);

Do not pre-allocate memory. Initialize memory with NULL and call realloc before using memory. realloc(NULL is the same as malloc().

Check for allocation errors.

  • Related