A simple implementation of a mutable String in C The following code implements a simple interface to operate on mutable* Strings in C. It is composed of two files: one for the structure definition and the available operations, and the other with the actual implementation.

*This can be inexact, as only adding characters to the end is implemented.

Any comments on coding style and improvements are greatly appreciated.

String.h

#ifndef __STRING_H__
#define __STRING_H__

#include &lt;stdio.h&gt;
#include &lt;stdbool.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;

#define BLOCK_SIZE 256

typedef struct {
    char *data;
    int length;
    int blocks;
} String;

String * string_create();
void string_dispose(String *str);

bool string_empty(String *str);
void string_append_char(String *str, char c);

char * string_get_all(String *str);
char string_get(String *str, int pos);

int string_find(String *str, char c);

#endif /* __STRING_H__ */


String.c

#include "String.h"

/** Create a String */
String * string_create() {
    String *ans = calloc(1, sizeof *ans);

    if (ans != NULL) {
        ans-&gt;data = malloc( BLOCK_SIZE * sizeof *(ans-&gt;data) );

        ans-&gt;length = 0;
        ans-&gt;blocks = (ans-&gt;data == NULL) ? 0 : 1;
    }

    return ans;
}

/** Free the memory associated with a String */
void string_dispose(String *str) {
    if (str != NULL) {
        free(str-&gt;data);
        free(str);
    }
}

/** Is the String empty? */
bool string_empty(String *str) {
    if (str == NULL) return true;
    if (str-&gt;length == 0) return true;
    return false;
}

/** Add a character to the end of the String */
void string_append_char(String *str, char c) {
    if (str != NULL) {
        if (str-&gt;length == str-&gt;blocks * BLOCK_SIZE) {
            char *new_str = realloc( str-&gt;data, BLOCK_SIZE * (str-&gt;blocks + 1) * sizeof *(str-&gt;data));

            if (new_str != NULL) {
                str-&gt;data = new_str;
                ++(str-&gt;blocks);
            }
        }

        if (str-&gt;length &lt; str-&gt;blocks * BLOCK_SIZE) {
            str-&gt;data[str-&gt;length] = c;
            ++(str-&gt;length);
        }
    }
}

/** Get a C-String with the proper null-terminator */
char * string_get_all(String *str) {
    char *res = NULL;

    if (str != NULL) {
        res = malloc((str-&gt;length + 1) * sizeof *str-&gt;data);

        if (res != NULL) {
            memcpy(res, str-&gt;data, str-&gt;length);
            res[str-&gt;length] = '\0';
        }
    }

    return res;
}

/** Get a character at a given position in the String */
char string_get(String *str, int pos) {
    char res = '\0';

    if (str != NULL) {
        if (pos &gt;= 0 &amp;&amp; pos &lt; str-&gt;length) {
            res = str-&gt;data[pos];
        }
    }

    return res;
}

/** Get where the first occurrence of a character in the String is */
int string_find(String *str, char c) {
    int pos = -1;

    if (str != NULL) {
        pos = 0;
        while (str-&gt;data[pos] != c) ++pos;

        if (pos == str-&gt;length) pos = -1;
    }

    return pos;
}


This is a test file that uses some of the core methods implemented in a String. This file is called test_string.c.

#include &lt;stdio.h&gt;

#include "String.h"

int main() {
    String *str = string_create();

    char i;
    for (i = 'a'; i &lt;= 'z'; i++)
        string_append_char(str, i);

    char *cstr = string_get_all(str);

    printf("%s\n", cstr);

    int pos = string_find(str, 'f');

    printf("Character 'f' occurs at position %d.\n", pos);
    printf("Reading from the String, we get \"%c\".\n", string_get(str, pos));

    free(cstr);
    string_dispose(str);

    return 0;
}
Unsafe loop

This loop in string_find() is unsafe since it could read past the end of your buffer:


    while (str-&gt;data[pos] != c) ++pos;



You should add an additional check like this:

    while (pos &lt; str-&gt;length &amp;&amp; str-&gt;data[pos] != c) ++pos;


Short circuit error conditions

Rather than doing this:


 if (str != NULL) {
     // Rest of function indented
 }



it would be easier to read if you rewrote it like this:

if (str == NULL)
    return NULL;

// Rest of function, not indented


Other things

Your reallocation strategy will lead to \$O(n^2)\$ behavior when appending to long strings.  You might want to double the allocation instead of adding a fixed amount.

You might want to use size_t for your sizes and lengths instead if int, because an int might overflow at 32KB on some platforms.