Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

What are some flaws that drive you nuts in C APIs (including standard libraries, third party libraries, and headers inside of a project) ? The goal is to identify API design pitfalls in C, so people writing new C libraries can learn from mistakes of the past.

Explain why the flaw is bad (preferably with an example), and try to suggest an improvement. Although your solution might not be practical in real life (it's too late to fix strncpy), it should give a heads up for future library-writers.

Although the focus of this question is C APIs, problems that affect your ability to use them in other languages are welcome.

Please give one flaw per answer, so democracy can sort the answers.

share|improve this question
Joey, this question is verging on being not constructive by asking to build up a list of things people hate. There's potential here for the question to be useful if the answers explain why the practices they're pointing out are bad and provide detailed information on how to improve them. To that end, please move your example from the question into an answer of its own and explain why it's a problem/how a malloc'd string would fix it. I think setting a good example with the first answer could really help this question thrive. Thanks! –  Anna Lear Aug 13 '11 at 5:27
@Anna Lear: Thanks for telling me why my question was problematic. I was trying to keep it constructive by asking for an example and suggested alternative. I guess I really needed some examples to indicate what I had in mind. –  Joey Adams Aug 13 '11 at 6:12
@Joey Adams Look at it this way. You are asking a question that is supposed to "automatically" solve C API issues in a general way. Where sites like StackOverflow were designed to work such that the more common issues with programming are easily found AND answered. StackOverflow will naturally result in a list of answers for your question but in a more structured easily searchable way. –  Andrew Finnell Aug 13 '11 at 20:24
I voted to close my own question. My goal was to have a collection of answers that could serve as a checklist against new C libraries. The three answers so far all use words like "inconsistent", "illogical", or "confusing". One can't objectively determine whether or not an API violates any of these answers. –  Joey Adams Aug 17 '11 at 1:41

3 Answers 3

Functions with inconsistent and often cumbersome string returning conventions.

For example, getcwd asks for a user-supplied buffer and its size. This means an application either has to set an arbitrary limit on the current directory length, or do something like this (from CCAN):

 /* *This* is why people hate C. */
len = 32;
cwd = talloc_array(ctx, char, len);
while (!getcwd(cwd, len)) {
    if (errno != ERANGE) {
        return NULL;
    cwd = talloc_realloc(ctx, cwd, char, len *= 2);

My solution: return a malloced string. It's simple, robust, and no less efficient. Excepting embedded platforms and older systems, malloc is actually quite fast.

share|improve this answer
I would not call this bad practice, I would call this good practice. 1) It is so utterly common that no programmer should be surprised by it. 2) It leaves the allocation to the caller, which excludes numerous possibilities of memory leak bugs. 3) It is compatible with statically allocated buffers. 4) It makes the function implementation cleaner, a function calculating some mathematical formula shouldn't be concerned with something entirely unrelated such as dynamic memory allocation. You think main gets cleaner but the function gets messier. 5) malloc isn't even allowed on many systems. –  user29079 Aug 17 '11 at 13:30
@Lundin: The problem is, it leads to programmers creating unnecessary hard-coded limits, and they have to try really hard not to (see the example above). It's fine for things like snprintf(buf, 32, "%d", n), where the output length is predictable (certainly not more than 30, unless int is really huge on your system). Indeed, malloc isn't available on many systems, but for desktop and server environments, it is, and it works really well. –  Joey Adams Aug 17 '11 at 14:45
But the problem is that the function in your example sets no hard-coded limits. Code like this is not common practice. Here, main knows things about the buffer length that the function should have known. It all suggests poor program design. Main doesn't seem to know what the getcwd function even does, so it is using some "brute force" allocation to find out. Somewhere the interface between the module in which getcwd resides and the caller is muddled. That doesn't mean that this way of calling functions is bad, on the contrary experience shows it is good for the reasons I already listed. –  user29079 Aug 18 '11 at 6:40

Functions with inconsistent or illogical return values. Two good examples:

1) Some windows functions that return a HANDLE use NULL/0 for an error (CreateThread), some use INVALID_HANDLE_VALUE/-1 for an error (CreateFile).

2) The POSIX 'time' function returns '(time_t)-1' on error, which is really illogical since 'time_t' can be either a signed or unsigned type.

share|improve this answer
Actually, time_t is (usually) signed. However, calling December 31, 1969 "invalid" is rather illogical. I guess the 60s were rough :-) In seriousness, a solution would be to return an error code, and pass the result through a pointer, as in: int time(time_t *out); and BOOL CreateFile(LPCTSTR lpFileName, ..., HANDLE *out); . –  Joey Adams Aug 13 '11 at 6:48
Exactly. It's weird if time_t is unsigned, and if time_t is signed, it makes one time invalid in the middle of an ocean of valid ones. –  David Schwartz Aug 13 '11 at 6:58

Functions or parameters with non-descriptive or affirmatively confusing names. For example:

1) CreateFile, in the Windows API, doesn't actually create a file, it creates a file handle. It can create a file, just like 'open' can, if asked to through a parameter. This parameter has values called 'CREATE_ALWAYS' and 'CREATE_NEW' whose names don't even hint at their semantics. (Does 'CREATE_ALWAYS' mean it fails if the file exists? Or does it create a new file on top of it? Does 'CREATE_NEW' means it creates a new file always and fails if the file already exists? Or does it create a new file on top of it?)

2) pthread_cond_wait in the POSIX pthreads API, which despite its name, is an unconditional wait.

share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.