3

If you have a function in C that takes ownership of whatever is passed into it, such as a function that adds a struct to a vector buffer by-value, and this struct value contains a member pointer to a character array (a string).

During the buffer's cleanup routine, it should release the strings that it owns, but what if some strings are allocated at runtime, but others are allocated at compiletime using a string literal.

There is no safe and standard (non-proprietary) way to detect if a char* points to read-only memory, so what is this hypothetical freeVector function to do with a pointer to a char buffer?

struct Element {
    int   id;
    char* name;
}

struct Vector {
    size_t maxIndex;
    size_t length;
    struct Element buffer[];
}

void addToVector(struct Vector* vector, struct Element element) {
    // lazy-reallocation logic here if maxIndex => length
    vector->buffer[ vector->maxIndex++ ] = element; // by-value copy
}

void freeVector(struct Vector* vector) {
    for(size_t i = 0; i < vector->maxIndex; i++ ) {
        free( vector->buffer[ i ].name ); // segfault/AV if name is a literal
    }
}
Dai
  • 141,631
  • 28
  • 261
  • 374
  • Your element structure needs a flag that lets you know whether you can free that name or not. – Alexis Wilke Sep 19 '15 at 03:38
  • @AlexisWilke well, yes, but I'm wondering if there's a better way. – Dai Sep 19 '15 at 03:39
  • There are proprietary ways to know whether a pointer is in the heap or the startup data, but it could be slower (under Unix, it's just a pointer compare, but Windows requires an API call...) And since you said "non-proprietary"... – Alexis Wilke Sep 19 '15 at 03:43
  • 1
    Your other options are (1) always `strdup` string literals before handing them to your data structure (could be cheaper if string literals are rare) or (2) add a garbage collector to your runtime (allows zero-copy sharing of const strings). – rici Sep 19 '15 at 03:47
  • @rici Can you elaborate on the sharing of const strings? – Dai Sep 19 '15 at 03:49
  • @Dai: If you have a garbage collector *and* you don't allow strings to be mutated, then you don't need to copy a string in order to include it in more than one data structure. That's why languages with garbage collectors (python, java, lua, ...) often have immutable strings. – rici Sep 19 '15 at 03:56
  • 1
    Note that string literals are not the only source of problems: you will run into a different set of problems if you ever pass a local character array, or a file scope (`static`) or global character array to the code. All of those were not allocated by `malloc()` and relatives, and therefore cannot be released with `free()`. Further, local variables are apt to go out of scope, and their space will be reused, leading to all sorts of problems. It is by far simplest to dictate that your code always copies the string it is passed — punting on who releases any allocated strings to the calling code. – Jonathan Leffler Sep 19 '15 at 05:10
  • Related if not a duplicate: http://stackoverflow.com/q/1576300/694576 – alk Sep 19 '15 at 08:44

1 Answers1

3

The blessing and the curse of C is that it lets this totally up to you. Two choices are to allocate everything on the heap and to define a fat pointer type that includes a bit to say whether each instance needs freeing. A clever albeit non-portable implementation might use a low order bit of the pointer itself because for many architectures the bottom 2 bits or more of all pointers are always zero. Garbage collectors have used this trick to distinguish pointers from unboxed discrete types (fixnums in the biz) almost forever.

If you allow more than one pointer to the same object (think graph data structure), then things get more complex or interesting depending on your point of view. For this, you'll probably need a garbage collection scheme: obstacks, reference counting, mark and sweep, arena copying, etc. Other languages tend to give you one of these as a built-in or (as in C++) language features deliberately meant to support implementing one or more yourself. With C, not so much...

Gene
  • 46,253
  • 4
  • 58
  • 96