C Reallocation of 2d array function - arrays

My reallocation does not work (Segmentation fault for 11.element), I would like to enlarge the array twice, the length of the column is constant according to the first input.I would like to allocate in function.
In the function vztvorPole I allocate an array of 10 rows and x columns.
char vytvorPole(char ***grid, int nrows, int ncols)
{
*grid = malloc( sizeof(*grid)*nrows);
if (*grid == NULL)
{
printf("ERROR\n");
return 1;
}
for(int i=0;i<nrows;i++)
{
(*grid)[i]=(char *) malloc (ncols*sizeof(*grid));
if((*grid)[i] == NULL)
{
printf("ERROR\n");
return 1;
}
}
return 0;
}
char realokuj(char ***grid, int nrows, int ncols)
{
char **docasne;
docasne = (char**)realloc (*grid, nrows*sizeof(*grid));
for(int i=nrows/2;i<nrows;i++)
{
(docasne)[i]=(char *) malloc (ncols*sizeof(*grid));
}
*grid = docasne;
}
int main (void)
{
char **diagonaly;
int rDiagonaly = 10;
int cDiagonaly = -1;
char *str = NULL;
size_t capacity = 0;
int first = 1;
int nr = 0;
printf("Vypln:\n");
while ( getline (&str, &capacity, stdin) != -1)
{
if(str[0] == '\n')
break;
if (first)
{
cDiagonaly = strlen (str);
vytvorPole(&diagonaly, rDiagonaly, cDiagonaly);
first = 0;
}
if (nr==rDiagonaly)
{
rDiagonaly *= 2;
realokuj(&diagonaly, rDiagonaly, cDiagonaly);
}
strcpy(diagonaly[nr],str);
nr++;
}
}

Your code is a bit more complicated than needed.
If you initialize diagnonaly (e.g. char **diagnoaly = NULL;), you can eliminate vytvorPole and just use realokuj. That is, if you also do: int rDiagonaly = 0;
That's because realloc will handle a NULL pointer just fine (i.e. it's equivalent to malloc in that case).
And, you don't need to do:
(docasne)[i]=(char *) malloc (ncols*sizeof(*grid));
in the subfunction.
It's better to use strdup in main. If we do that, we don't need cDiagonaly at all. And, we can have strings of varying lengths.
Passing a triple star pointer (e.g. char ***grid) has issues. You could just pass it as char ** and use return to update the value since you don't really use the existing return.
But, this can really be done from main without a separate function.
While it may be possible to fix your existing code, it really should be simplified:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(void)
{
#if 0
char **diagonaly;
int rDiagonaly = 10;
#else
char **grid = NULL;
int rmax = 0;
#endif
#if 0
int cDiagonaly = -1;
#endif
char *str = NULL;
size_t capacity = 0;
#if 0
int first = 1;
#endif
int nr = 0;
printf("Vypln:\n");
while (getline(&str,&capacity,stdin) != -1) {
if (str[0] == '\n')
break;
if (nr == rmax) {
rmax += 100;
grid = realloc(grid,sizeof(*grid) * (rmax + 1));
if (grid == NULL) {
perror("realloc");
exit(1);
}
// this is similar to what you did, but it's not necessary because
// of "grid[nr] = NULL;" below
#if 0
for (int idx = nr; idx < rmax; ++idx)
grid[idx] = NULL;
#endif
}
grid[nr++] = strdup(str);
grid[nr] = NULL;
}
return 0;
}
Here's the cleaned up version [without the cpp conditionals]:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(void)
{
char **grid = NULL;
int rmax = 0;
char *str = NULL;
size_t capacity = 0;
int nr = 0;
printf("Vypln:\n");
while (getline(&str,&capacity,stdin) != -1) {
if (str[0] == '\n')
break;
if (nr == rmax) {
rmax += 100;
grid = realloc(grid,sizeof(*grid) * (rmax + 1));
if (grid == NULL) {
perror("realloc");
exit(1);
}
}
grid[nr++] = strdup(str);
grid[nr] = NULL;
}
return 0;
}

Is something like that okay?
while ( getline (&str, &capacity, stdin) != -1)
{
if(str[0] == '\n')
break;
if (first)
{
cDiagonaly = strlen (str);
diagonaly = (char**) malloc(sizeof(*diagonaly)*rDiagonaly);
if (nr==rDiagonaly)
{
rDiagonaly *= 2;
docasne = (char**)realloc (diagonaly, rDiagonaly*sizeof(*diagonaly));
diagonaly=docasne;
}
diagonaly[nr]=(char*) malloc (cDiagonaly*sizeof(**diagonaly));
strcpy(diagonaly[nr],str);
nr++;
}

Related

Why is the integer in the struct a garbage values even though it was changed?

So, I implemented split in C, now I know strtok exists, but I wanted to implement it, so my function returns a struct that has the string array and the length, which is decided by the number of times the delimiter occurs and whether it's the first value or not, here's the code.
split.h
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int count_delm(char *, char);
char *make_str(char);
struct split_output {
char **arr;
int size;
};
typedef struct split_output split_arr;
split_arr *split(char *, char);
split.c
#include "split.h"
int count_delm(char *str, char delm) {
register int lpvar = 0;
register int counter = 0;
while (str[lpvar] != '\0') {
if (str[lpvar] == delm) {
counter++;
}
lpvar++;
}
return counter;
}
char *make_str(char ch) {
char *ret_str = (char *)malloc(2);
ret_str[0] = ch;
ret_str[1] = '\0';
return ret_str;
}
split_arr *split(char *str, char delm) {
int num_delm = count_delm(str, delm);
char **final_arr;
register int lpvar = 0;
register int arr_counter = 0;
char *concat_str = (char *)malloc(2);
int ret_size = 0;
if (str[0] == delm) {
concat_str = make_str(str[1]);
final_arr = (char **)malloc(sizeof(char *) * (num_delm));
ret_size = num_delm;
lpvar++;
} else {
concat_str = make_str(str[0]);
final_arr = (char **)malloc(sizeof(char *) * (num_delm + 1));
ret_size = num_delm + 1;
}
while (1) {
if (str[lpvar + 1] != '\0') {
if (str[lpvar + 1] != delm) {
concat_str = strcat(concat_str, make_str(str[lpvar + 1]));
lpvar++;
} else {
final_arr[arr_counter] = concat_str;
arr_counter++;
if (str[lpvar + 2] != '\0') {
lpvar++;
lpvar++;
concat_str = make_str(str[lpvar]);
}
}
} else {
arr_counter++;
final_arr[arr_counter] = concat_str;
printf("%s is the last at pos %d\n", concat_str, arr_counter);
break;
}
}
split_arr *ret_struct = (split_arr *)malloc(sizeof(split_arr));
(*ret_struct).size = ret_size;
(*ret_struct).arr = final_arr;
return ret_struct;
}
That was the code of the split implementation, here's the code that tests it.
test.c
#include "split.h"
int main() {
char *x = "lryabruahsdfads";
split_arr output = *(split(x, 'a'));
char **read = output.arr;
int len = output.size;
int loop = 0;
printf("size: %d", len - 1);
for (loop = 0; loop < len; loop++) {
puts(*(read + loop));
}
return 0;
}
Here's the output when executed:
ds is the last at pos 4
size: 3lry
bru
hsdf
Segmentation fault (core dumped)
Why is output.size 3lry? I de-referenced the struct pointer to get the struct so there's nothing wrong with that. I can't find the error, I've been trying to debug for almost an hour.
In your split.c file over here:
else{
arr_counter++; //comment this line
final_arr[arr_counter]=concat_str;
printf("%s is the last at pos %d\n",concat_str,arr_counter);
break;
}
You are incrementing variable arr_counter which you should not because you already incremented it inside the if. Just comment out this line and your code works fine.
And your output is fine just add a line return to the printf statements like:
printf("size: %d \n",len-1);

Why is one of my struct members undefined after attempting to change it in a function? I passed the struct as reference

I am writing code for a search_tree() function which fills a chArray struct (dynamic char array) with all the files in a directory and its sub-directories.
However, I'm having problems with the add_elements() function, which is meant to add elements to the char array and adjust its size accordingly.
The array.size member is undefined after the function call, even though I passed the chArray variable as reference to the function.
Why is this happening?
-Apart from this I'm having memory allocation errors, but I'm nearly sure the reason for this is this issue with array.size being undefined.
Thank you for any help!!
P.S. I've included my entire code below, sorry for all the random print statements and weird names - this was me trying to figure out what was wrong.
//
// Created by mario on 05/03/2022.
//
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <stddef.h>
#ifndef errno
extern int errno;
#endif
typedef struct {
int init;
int numEls;
size_t size;
char **elements;
} chArray;
void initArray(chArray *arr, int n, ...) {
size_t c_size = 0;
va_list ptr;
arr = malloc(sizeof(chArray));
*arr = (chArray) {.init = 1, .numEls = n, .size = sizeof(chArray), .elements = NULL};
va_start(ptr, n);
for (int i = 0; i < n; i++) {
char *arg = va_arg(ptr, char*);
c_size += strlen(arg) + 1;
printf("%lu\n", c_size);
if (i == 0) {
arr->elements = (char **) malloc(strlen(arg) + 1);
}
else {
arr->elements = realloc(arr->elements, arr->size + strlen(arg) + 1);
}
*(arr->elements + i) = arg;
}
va_end(ptr);
arr->size = c_size + offsetof(chArray, elements);
printf("%lu\n", arr->size);
}
void add_elements(chArray *arr, int n, ...) {
va_list ptr;
size_t new_size = arr->size;
va_start(ptr, n);
for (int i = 0; i < n; i++) {
char *f_arg = va_arg(ptr, char *);
new_size += strlen(f_arg) + 1;
}
va_end(ptr);
arr = realloc(arr, new_size);
va_start(ptr, n);
for (int i = arr->numEls; i < n+arr->numEls; i++) {
char *arg = va_arg(ptr, char*);
arr->size += strlen(arg) + 1;
if (i == 0) {
arr->elements = (char **) malloc(strlen(arg) + 1);
}
else {
arr->elements = realloc(arr->elements, arr->size + strlen(arg) + 1);
}
*(arr->elements + i) = arg;
}
va_end(ptr);
arr->numEls += n;
}
int search_tree(char *tree, char *contains, chArray *ends) {
struct dirent *entry;
DIR *dir;
struct stat buff;
mode_t mode;
char *not1 = ".";
char *not2 = "..";
unsigned long int tree_length = strlen(tree);
char *last = tree + tree_length - 1;
#ifdef _WIN32
if (last != '\\') {
char *slash = "\\\0";
#else
if (*last != '/') {
char *slash = "/\0";
#endif
if (*last == '\\') {
char *temp = (char *) malloc(tree_length + 1);
memset(temp, '\0', tree_length + 1);
strncpy(temp, tree, tree_length - 1);
tree = 0;
tree = (char *) malloc(tree_length + 2);
strcpy(tree, temp);
strcat(tree, slash);
free(temp);
}
else {
char *temp = (char *) malloc(tree_length + 2);
memset(temp, '\0', tree_length + 2);
strcpy(temp, tree);
tree = 0;
tree = (char *) malloc(tree_length + 2);
strcpy(tree, temp);
strcat(tree, slash);
free(temp);
}
}
if (ends->init != 1) {
printf("Once\n");
initArray(ends, 0);
}
if ((dir = opendir(tree)) == NULL) {
perror("An error occurred");
fprintf(stderr, "Errno = %d", errno);
return -1;
}
while ((entry = readdir(dir)) != NULL) {
size_t length = strlen(tree) + strlen(entry->d_name) + 1;
char *file_path = (char *) malloc(length);
memset(file_path, '\0', length);
strcpy(file_path, tree);
strcat(file_path, entry->d_name);
if (stat(file_path, &buff) != -1) {
mode = buff.st_mode;
if (S_ISDIR(mode) && (strcmp(entry->d_name, not1) != 0) && (strcmp(entry->d_name, not2) != 0)) {
search_tree(file_path, contains, ends);
for (int i = 0; i < ends->numEls; i++) {
printf("%s\n", *(ends->elements));
}
} else if (S_ISREG(mode)) {
if (contains == NULL) {
add_elements(ends, 1, file_path);
for (int i = 0; i < ends->numEls; i++) {
printf("%s\n", *(ends->elements));
}
}
else {
if (strstr(file_path, contains) != NULL) {
add_elements(ends, 1, file_path);
for (int i = 0; i < ends->numEls; i++) {
printf("%s\n", *(ends->elements));
}
}
}
}
}
else {
perror("An error occurred");
fprintf(stderr, "Errno = %d", errno);
return -1;
}
}
closedir(dir);
return 0;
}
int main(int argc, char *argv[]) {
char *path = "/Users/mario/python";
char *toComp = "python";
// printf("%d\n", strcmp(strstr(path, "python"), toComp));
chArray test;
initArray(&test, 3, "Hello", "There", "Goodbye");
printf("%lu\n", test.size);
add_elements(&test, 10, "Soyez", "Jiduan", "Chifa3n", "Chifa4n", "Chifa5n", "Chif6an", "Chifan7", "Chifa8n", "Chifan9", "Chifan10");
for (int i = 0; i < 6; i++) {
printf("%s\n", *(test.elements + i));
}
chArray ends;
char *tree = "/Users/mario/Desktop";
search_tree(tree, ".py", &ends);
printf("%lu\n", sizeof(ends));
printf("%s\n%s\n", *ends.elements, *(ends.elements + 1));
chArray *random = (chArray * ) malloc(48);
printf("%lu\n", sizeof(*random));
return 0;
}
Output:
36
4486016032
search_tree(4025,0x117c5a600) malloc: *** error for object 0x7ff7b48d0838: pointer being realloc'd was not allocated
search_tree(4025,0x117c5a600) malloc: *** set a breakpoint in malloc_error_break to debug
The 36 is the array's size printed from within the add_elements() function, and the second number is the same but printed just after the function.
What is going on?

How to dynamically allocate memory for every element in 2D array in C

how to dynamically allocate memory for every element in 2D array?
For example, I have an address which consist of street name(1.column) and street number(2.column) e.g. St.Paul 87/45. And I need to dynamically allocate memory for them.
int main()
{
int c = 1, r = 1; //c-column, r-row
char st_name[] = "St.Paul";
char st_number[] = "87/45";
char *arr = (char *)malloc(c*r*sizeof(char));
c = 0;
r = 0;
*arr[r][c++] = (char *)malloc((strlen(st_name)) * sizeof(char));
*arr[r][c] = (char *)malloc((strlen(st_number)) * sizeof(char));
return 0;
}
Of course it´s not working.
Thanks.
What you need is a matrix, but each matrix item is a c-string so an array.
You can do it using 3 starts pointer, like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
int c = 1, r = 1; //c-column, r-row
char st_name[] = "St.Paul";
char st_number[] = "87/45";
char ***arr = malloc(r*sizeof(char *));
c = 0;
r = 0;
if (arr != NULL)
{
arr[r] = malloc(c*sizeof(char *));
if (arr[r] != NULL)
{
arr[r][c] = malloc(strlen(st_name)+1);
if (arr[r][c] != NULL)
{
c++
arr[r][c] = malloc(strlen(st_number)+1);
if (arr[r][c] != NULL)
{
sprintf( arr[0][0], st_name);
sprintf( arr[0][1], st_number);
printf ("arr[0][0] = %s\n", arr[0][0]);
printf ("arr[0][1] = %s\n", arr[0][1]);
}
}
}
}
return 0;
}
Or you can do that using a struct that defines each item of your array, like
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct item
{
char *name;
char *number;
};
int main(void)
{
char st_name[] = "St.Paul";
char st_number[] = "87/45";
int r = 1;
struct item *arr = malloc(r*sizeof(struct item));
if (arr != NULL)
{
arr[0].name = malloc(strlen(st_name)+1);
arr[0].number = malloc(strlen(st_number)+1);
if ((arr[0].name != NULL) && (arr[0].number != NULL))
{
sprintf( arr[0].name, st_name);
sprintf( arr[0].number, st_number);
printf ("arr[0][0] = %s\n", arr[0].name);
printf ("arr[0][1] = %s\n", arr[0].number);
free(arr[0].name);
free(arr[0].number);
}
free(arr);
}
return 0;
}

Function returns address of local variable, in c

Keep getting an error on my return ret before the main () class (end of process request)
buddy.c: In function `process_request':
buddy.c:89: warning: function returns address of local variable
Error I receive , what I'm trying to do is print the results I get from my process_request to my print near the end of my main() function, help?
//used a flag
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#define F_SIZE 2
#define A_SIZE 2
#define BUDDY_SIZE 4*1024 // in bytes
// compile using gcc-o buddy buddy.c -lm
// block information
struct block_info
{
char AF_flag; // flag
int data; // data in the block
};
typedef struct block_info block;
block buddy_block[BUDDY_SIZE]; // entire buddy system to be used in this array
int block_count = 0; // number of blocks in buddy_block
int get_block_size(int num)
{
int i = 0;
for (i = 0; num < pow(2.0, (double)i); ++i);
return (int)(pow(2.0, (double)i));
}
char *process_request(char *s, int len)
{
block b;
block n;
int i, j, count, block_size = 0;
int first_buddy_size = 0;
int second_buddy_size = 0;
char ret[BUDDY_SIZE] = { 0 };
char *response[BUDDY_SIZE] = { 0 };
if (!s)
return NULL;
first_buddy_size = buddy_block[0].data;
second_buddy_size = buddy_block[1].data;
block_size = get_block_size(atoi(s));
// get the matching FREE block in the power of 2
if (*s == 'A')
{ // Allocation request
int i = 0;
char *buff = NULL;
// split the block
char strf[F_SIZE] = { 0 };
char stra[A_SIZE] = { 0 };
strf[0] = 'F';
stra[0] = 'A';
for (i = 0; block_size <= first_buddy_size / 2; ++i)
{
first_buddy_size /= 2;
sprintf(buff, "%d", first_buddy_size);
response[i] = strcat(strf, buff);
}
sprintf(buff, "%d", block_size);
response[i] = strcat(stra, buff);
// update the array
count = i;
for (i = 0, j = count; j; --j, ++i)
{
char *str = response[j];
buddy_block[i].AF_flag = *str++;
while (*str)
buddy_block[i].data = *str;
}
}
else if (*s == 'F')
{ // Free request
for (i = 1; i < block_count; ++i)
{ // traversing through the array
if (buddy_block[i].data = block_size)
{ // b.AF_flag = 'B';
i << 1;
}
}
}
// update array
count = i;
for (i = 0, j = count; j; --j, ++i)
{
char *str = response[j];
buddy_block[i].AF_flag = *str++;
while (*str)
buddy_block[i].data = *str;
}
return ret; // ------------error: warning functions returns address
// of local variable----------
}
int main(int argc)
{
block t;
int i;
char ch;
char *ret = NULL;
char line[20];
t.AF_flag = 'X'; // some junk means memory block not even accessed
t.data = 0;
for (i = 0; i < BUDDY_SIZE; i++)
buddy_block[i] = t; // initialize with 0 bytes and no information about
// Allocation/Free
// initially there is only one Free block of 4K bytes
t.AF_flag = 'F';
t.data = BUDDY_SIZE;
buddy_block[0] = t; // started the buddy block to 4096 bytes, all free to be
// allocated
++block_count;
while (1)
{
// get user input
char request[5] = { 0 }; // 'F4096' or 'A4096', max 5 chars
int correct_input = 0;
char ch;
for (i = 0, ch = 'X'; ch != '\n'; ++i)
{
ch = getchar();
if ((i == 0) && (ch != 'A' || ch != 'F'))
{
printf("Illegal token!!! : should be A or F");
correct_input = 0;
break;
}
if (ch < '0' && ch > '9')
{ // illegal code
printf("Illegal token!!! : should be 0 and 9");
}
correct_input = 1;
request[i] = ch;
}
if (correct_input)
{
// process user input
ret = process_request(request, sizeof(request));
printf("%d", ret); // [512](512A)(128A)(128F)(256F)(1024F)(2048F)
// //fprintf(stderr, "I am in stderr");
fflush(stdout);
}
}
return 0;
}
You have allocated ret on the stack. Although it is not forbidden to return an address to that the stack will be reused by any function that is called afterwards thus overwriting whatever was at that address.
You may want to consider moving this data onto the caller's stack or into dynamic memory.
char * foo() {
char string[] = "Hello world\n";
return string;
}
int main () {
printf("%s", foo());
}
Will most likely not print "Hello World!".
One right way would be:
void foo(char * buffer) {
memcpy(buffer, "Hello world\n", sizeof("Hello world\n"));
}
int main () {
char buffer[100];
foo(&buffer);
printf("%s", buffer);
}
Or with dynamic memory (prone to memory leaks):
char * foo() {
char * string = malloc(sizeof("Hello world\n"));
memcpy(string, "Hello world\n", sizeof("Hello world\n"));
return string;
}
int main () {
char * string = foo();
printf("%s", string);
free(string);
}
It means exactly what it says. You are doing
char* process_request(char*s, int len) {
...
char ret[BUDDY_SIZE] = {0};
...
return ret;
}
ret is an address to a memory location. The issue is that such memory location points to a local variable. A local variable lies in the stack, and its memory may be (probably will) reused for other variables when you call new functions.
To avoid that, return a pointer to a memory location that has been dynamically allocated (that means malloc and friends).
You are returning a local pointer from a function and that is an undefined value.
char ret[BUDDY_SIZE] = {0};
SO, your compiler is throwing that error. Assign your pointer dynamically and the error should go away.

c - string manipulation to struct member

Based on my previous post, I came up with the following code. I'm sure there is a better way of doing it. I'm wondering, what would that be?
It does split the string if greater than max chars OR if # is found. Any ideas would be appreciated!
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct my_struct {
char *str;
};
int main () {
struct my_struct *struc;
int max = 5;
char *tmp = "Hello World#Foo Bar In here#Bar Foo dot com#here#there";
struc = malloc (20 * sizeof (struct my_struct));
int strIdx = 0, offSet = 0;
char *p = tmp;
char *tmpChar = malloc (strlen (tmp) + 1), *save;
save = tmpChar;
while (*p != '\0') {
if (offSet < max) {
offSet++;
if (*p == '#') {
if (offSet != 1) {
*tmpChar = '\0';
struc[strIdx++].str = strndup (save, max);
save = tmpChar;
}
offSet = 0;
} else
*tmpChar++ = *p;
} else { // max
offSet = 0;
*tmpChar = '\0';
struc[strIdx++].str = strndup (save, max);
save = tmpChar;
continue;
}
p++;
}
struc[strIdx++].str = strndup (save, max); // last 'save'
for (strIdx = 0; strIdx < 11; strIdx++)
printf ("%s\n", struc[strIdx].str);
for (strIdx = 0; strIdx < 11; strIdx++)
free (struc[strIdx].str);
free (struc);
return 0;
}
Output at 5 chars max:
Hello
Worl
d
Foo B
ar In
here
Bar F
oo do
t com
here
there
Alright, I'll take a crack at it. First, let me say that my formatting changes were for me. If you don't like lonely {s, that's fine.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 5
struct string_bin
{
char *str;
};
int main ()
{
struct string_bin *strings;
char *tmp = "Hello World#Foo Bar In here#Bar Foo dot com#here#there";
char *p = tmp;
strings = malloc (20 * sizeof (struct string_bin));
memset(strings, 0, 20 * sizeof (struct string_bin));
int strIdx = 0, offset = 0;
char *cursor, *save;
strings[strIdx].str = malloc(MAX+1);
save = strings[strIdx].str;
while (*p != '\0')
{
if (offset < MAX && *p != '#')
{
*(save++) = *(p++);
offset++;
continue;
}
else if (*p == '#')
*p++;
offset = 0;
*save = '\0';
strings[++strIdx].str = malloc(MAX+1);
save = strings[strIdx].str;
}
*save = '\0';
for (strIdx = 0; strings[strIdx].str != NULL; strIdx++)
{
printf ("%s\n", strings[strIdx].str);
free (strings[strIdx].str);
}
free (strings);
return 0;
}
The big change is that I got rid of your strdup calls. Instead, I stuffed the string directly into its destination buffer. I also made more calls to malloc for individual string buffers. That lets you not know the length of the input string ahead of time at the cost of a few extra allocations.

Resources