I've some functions targeted to simplify working with strings in some of my other projects. I'd like feedback on this code and whether or not the implementation is efficient and memory safe. I work with both normal strings (char*) and string arrays (char**) in this code.
Here's stringfuncs.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "stringfuncs.h"
#define ALLOC_FAIL(ptr, location, alloc_type) \
if (ptr == NULL) \
{ \
fprintf(stderr, #alloc_type " failed in " #location); \
abort(); \
}
char* str_append(char element, char* str, const int end_index, int* size)
{
// Inserts a char to given string at given index
if (end_index == *size)
{
// Reallocate if needed
str = realloc(str, (*size *= 2) * sizeof(*str));
if (str == NULL)
{
fprintf(stderr, "realloc failed in str_append");
abort();
}
}
str[end_index] = element;
return str;
}
char** strarr_append(char* elementstr, char** strarr, const int end_index, int* size)
{
// Inserts a string to given array at given index
if (end_index == *size)
{
// Reallocate if needed
strarr = realloc(strarr, (*size *= 2) * sizeof(*strarr));
if (strarr == NULL)
{
fprintf(stderr, "realloc failed in str_append");
abort();
}
}
strarr[end_index] = elementstr;
return strarr;
}
char* trunc_string(char* str, const int end_index)
{
// Reallocate string for the amount of memory it needs
str = realloc(str, (end_index + 1) * sizeof(*str));
// Null terminate the string
str[end_index] = '\0';
return str;
}
char** trunc_strarray(char** strarr, const int index)
{
// Reallocate string array for the amount of memory it needs
strarr = realloc(strarr, (index + 1) * sizeof(*strarr));
return strarr;
}
char* get_string(const char* prompt)
{
// A function to get string user input
int index, size = 1;
char element;
char* string = malloc(size * sizeof(*string));
ALLOC_FAIL(string, get_string, malloc);
// Print the given prompt
printf("%s", prompt);
for (index = 0; (element = getchar()) != EOF && element != '\n'; index++)
{
// Record every character input until user presses enter (and or we encounter EOF)
string = str_append(element, string, index, &size);
}
// Truncate and null terminate the string
string = trunc_string(string, index);
return string;
}
char** split_string(const char delimiter, const char* string, int* length)
{
// Variables to keep track of splitarr
int arrsize = 2, arrindex = 0;
// Variables to keep track of elementstr
int strsize = 2, strindex = 0;
// Set up splitarr and elementstr with an initial size;
char** splitarr = malloc(arrsize * sizeof(*splitarr));
ALLOC_FAIL(splitarr, split_string, malloc);
char* elementstr = malloc(strsize * sizeof(*elementstr));
ALLOC_FAIL(elementstr, split_string, malloc);
for (int index = 0; string[index] != '\0'; strindex++, index++)
{
if (string[index] == delimiter)
{
// elementstr ends here
// Truncate and null terminate the string
elementstr = trunc_string(elementstr, strindex);
// Add string to string array
splitarr = strarr_append(elementstr, splitarr, arrindex, &arrsize);
arrindex++;
// Cleanup
strsize = 1;
strindex = -1;
elementstr = realloc(NULL, strsize * sizeof(*elementstr));
ALLOC_FAIL(elementstr, split_string, realloc);
}
else
{
// non-delimiter character, append to elementstr
elementstr = str_append(string[index], elementstr, strindex, &strsize);
}
}
// Truncate and null terminate the final string
elementstr = trunc_string(elementstr, strindex);
// Add final string to string array
splitarr = strarr_append(elementstr, splitarr, arrindex, &arrsize);
// Truncate the string array
splitarr = trunc_strarray(splitarr, arrindex);
// Assign the length of the array
*length = arrindex + 1;
return splitarr;
}
char** destroy_strarr(char** strarr, int length)
{
// Free all strings inside an array of strings and the array itself
int index = 0;
while (index < length)
{
// Free the elements and assign the pointer to NULL
free(strarr[index]);
strarr[index++] = NULL;
}
// Free the array itself and assign to NULL
free(strarr);
strarr = NULL;
return strarr;
}
Here's the corresponding stringfuncs.h
#pragma once
/*
Take string input from user
Pass in a string prompt to display to the user prior to input
Returns a pointer to the input string
*/
char* get_string(const char* prompt);
/*
Split given string by delimiter into an array of strings
Pass in the address of a variable to store the length of the array
Returns a pointer to the array of strings
*/
char** split_string(const char delimiter, const char* string, int* length);
/*
Free all the memory used by an array of strings
Assigns all the string elements as NULL
Returns NULL on success
*/
char** destroy_strarr(char** strarr, int length);
And example use-
#include<stdio.h>
#include<stdlib.h>
#include "stringfuncs.h"
int main()
{
int length;
char* input = get_string("> ");
char** strarr = split_string(' ', input, &length);
strarr = destroy_strarr(strarr, length);
free(input);
input = NULL;
return 0;
}
Primarily concerned about split_string and get_string, the rest are helpers.
Note: This targets C only, not C++