Last active
April 15, 2020 06:56
-
-
Save bricef/5480446 to your computer and use it in GitHub Desktop.
Dynamically growing a string in C.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#define SMARTY_SIZE_INIT 16 | |
typedef struct { | |
char * str; // a null terminated C string | |
char * end; // a pointer to the null byte, to be able to repeatedly append | |
// without using strlen() every time. | |
size_t size; // currently allocated size for *str, so we know when we | |
// need to grow. | |
} smarty; | |
/* | |
* Creates a new string, | |
* if there is not enough memory, will return a null pointer instead. | |
*/ | |
smarty * smarty_new(){ | |
// allocate space for the smarty struct | |
smarty * ss = malloc(sizeof(smarty)); | |
// quit if no memory | |
if(NULL == ss) goto smarty_new_fail; | |
// allocate space for the string itself | |
ss->str = malloc(SMARTY_SIZE_INIT); | |
//quit and cleanup if this failed | |
if(NULL == ss->str ) goto smarty_new_fail; | |
// set up the other bits of the smarty string | |
ss->end = ss->str; | |
*ss->end = '\0'; | |
ss->size = SMARTY_SIZE_INIT; | |
return ss; | |
smarty_new_fail: | |
free(ss); | |
return NULL; | |
} | |
/* | |
* Append to a smarty string, growing the allocated memory if needed | |
* on error, will return the longest string possible, which may truncate the | |
* suffix. | |
*/ | |
void smarty_append(smarty * ss, char * suffix){ | |
// iterate through suffix by character. Could be more efficient by using | |
// memcopy or similar, but the idea is the same. | |
while(*suffix != '\0'){ | |
// Check to see if the string needs to grow or not. | |
// also make sure we leave space for the null-termination byte at the end | |
if(! ((ss->end - ss->str) < (ss->size-1)) ){ | |
// store the offset, since the end ptr will not be valid if realloc | |
// moves our memory block | |
size_t offset = ss->end - ss->str; | |
// allocate bigger space, grows by doubling (good amortization) | |
char* newstr = realloc(ss->str, (ss->size * 2) ); | |
// if we're out of memory, we bail out. | |
if(NULL == newstr){ ss->end--; break; } | |
// reset the smarty string's internal to reflect the new size and | |
// possible memory block | |
ss->str = newstr; | |
ss->size = ss->size * 2; | |
ss->end = ss->str + offset; | |
} | |
//append chars to the string | |
*(ss->end) = *suffix; | |
suffix++; | |
ss->end++; | |
} | |
// make sure we're always null terminated correctly. | |
*(ss->end) = '\0'; | |
} | |
/* | |
* clean things up when we're not needed | |
*/ | |
void smarty_destroy(smarty * ss){ | |
free(ss->str); | |
free(ss); | |
} | |
int main(void){ | |
smarty * ss = smarty_new(); | |
smarty_append(ss, "Hello 1 "); | |
smarty_append(ss, "Hello 2 "); | |
smarty_append(ss, "Hello 3 "); | |
smarty_append(ss, "Hello 4 "); | |
smarty_append(ss, "Hello 5 "); | |
smarty_append(ss, "Hello 6 "); | |
smarty_append(ss, "Hello 7 "); | |
smarty_append(ss, "Hello 8 "); | |
smarty_append(ss, "Hello 9 "); | |
printf("->%s\n", ss->str); | |
smarty_destroy(ss); | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
➤ gcc -g smartystring.c -o smarty && valgrind --leak-check=full ./smarty | |
==16785== Memcheck, a memory error detector | |
==16785== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al. | |
==16785== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info | |
==16785== Command: ./smarty | |
==16785== | |
==16785== | |
==16785== HEAP SUMMARY: | |
==16785== in use at exit: 0 bytes in 0 blocks | |
==16785== total heap usage: 5 allocs, 5 frees, 252 bytes allocated | |
==16785== | |
==16785== All heap blocks were freed -- no leaks are possible | |
==16785== | |
==16785== For counts of detected and suppressed errors, rerun with: -v | |
==16785== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Of course, the true answer is to use a well tested library such as Glib's strings