Skip to content
VLSI Mentor

C Part I · Module 12 · Standard Library, Files and Program Interfaces

How to Read the C Standard Library

You have been using this library since Chapter 1.4 and have met four of its functions. It has several hundred, and no useful chapter could list them. What a chapter can do — and what makes the rest of this module possible — is give you a way to read one you have never seen: four questions that pull a function's contract out of any reference page in about a minute, and that keep working for functions this curriculum will never mention. The questions matter more than any particular answer, because the library's failure conventions are not consistent. Four different ones are in use, they are not interchangeable, and the most dangerous of them returns a value that looks entirely legitimate while something has gone wrong.

Intermediate18 min readCPart IFundamentalsStandard Library

Part I · Module 12 · Chapter 12.1 · How to Read the C Standard Library

1. The Library Is Part of the Language

The C standard specifies two things: the language and the library. They are in one document, written to the same level of precision, and a conforming implementation must provide both. printf is not an add-on that happens to ship with your compiler; it is as much a part of C as while.

That has a practical consequence worth stating before anything else: the library's guarantees are of the same kind as the language's. When Module 9 said that reading freed memory is undefined, and Module 10 that padding bytes are unspecified, those were statements from the same document that says what fopen returns on failure. You already know how to read that kind of statement — which is most of this chapter.

C17 specifies 29 headers. On the machine used for these measurements, 27 of them could be included:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
includable: 27 of 29
not available: threads.h uchar.h

§6 is about why, and about the difference between the two missing ones.

2. The Four Questions

For any library function, ask:

1. What does it assume? The preconditions you must satisfy. Violate one and the behaviour is undefined — the function will not check.

2. What does it return? The type, and what the value means when everything worked.

3. How does it report failure? Which of §3's four conventions, and the exact value.

4. What does it not promise? The parts deliberately left unspecified or implementation-defined.

Question 4 is the one people skip and the one that produces portable-looking code that is not. Chapter 9.6 §6 is a worked example: malloc(0) is implementation-defined, so a program that relies on either answer is relying on something the contract does not give.

Applied to a function you already know:

Questionmalloc
assumesnothing about the argument; size may be any size_t
returnsa void * to a block of at least size bytes, suitably aligned, with indeterminate contents
fails byreturning NULL — convention A
does not promisethat the contents are zero; what malloc(0) returns; that the block is contiguous with anything

Four lines, and every bug in Module 9 is a violation of one of them.

3. The Four Failure Conventions

This is where a method beats memorisation, because the conventions are not consistent across the library and using the wrong one produces code that looks correct.

Four ways a standard library function reports failure. First, a sentinel return: a reserved value is given back, such as fopen returning a null pointer. Second, a short count: fewer items than requested, such as fread returning 3 when 64 were asked for, and the count alone does not say whether the cause was end of file or an error. Third, a value plus errno: the returned value looks legitimate, such as strtol returning the maximum long on overflow, so errno must be cleared before the call and tested after. Fourth, cannot fail: no failure value exists, such as strlen, and the only risk is a precondition you failed to meet.ConventionHow failure appearsMeasured exampleSentinel returna reserved valueunmistakablefopen -> NULLShort countfewer than requestedcause not statedfread 3 of 64feof or ferror says whichValue plus errnothe value looks validclear errno firststrtol -> LONG_MAXerrno == ERANGECannot failno failure valueexiststhe risk is yourpreconditionstrlen12
Figure 1 — the four ways a standard library function tells you something went wrong, with the measured example of each. Read down the middle column: the conventions get harder to notice as you go. A sentinel return is unmistakable. A short count tells you something happened but not what, so it needs a second question. A value-plus-errno failure returns something that looks entirely legitimate, so there is no visual cue at all and the check has to be deliberate. The fourth row is not a failure convention but its absence, which shifts the entire burden onto the precondition you were supposed to meet.
Azvya Education Pvt. Ltd.VLSI Mentor
all four, in one program
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>

int main(void)
{
    FILE *f = fopen("/definitely/not/here", "r");          /* A */
    printf("fopen on a missing path  -> %s\n", f == NULL ? "NULL" : "non-NULL");

    void *p = malloc(SIZE_MAX);                            /* A */
    printf("malloc(SIZE_MAX)         -> %s\n", p == NULL ? "NULL" : "non-NULL");
    free(p);

    errno = 0;                                             /* C: clear first */
    char *end;
    long v = strtol("99999999999999999999", &end, 10);
    printf("strtol overflow          -> %ld, errno==ERANGE ? %d\n", v, errno == ERANGE);

    printf("strlen(\"abc\")            -> %zu\n", strlen("abc"));   /* D */
    return 0;
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
fopen on a missing path  -> NULL
malloc(SIZE_MAX)         -> NULL
strtol overflow          -> 9223372036854775807, errno==ERANGE ? 1
strlen("abc")            -> 3

Convention A — a sentinel return. A reserved value that cannot be a success. fopen and malloc return NULL; fgets returns NULL; ftell returns -1L. These are the easy ones, and Chapter 9.6 §3's rule covers them: check at the boundary, once.

Convention B — a short count. The function returns how many items it handled, which may be fewer than you asked for.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
fread asked 64, got 3   feof=1 ferror=0

The count alone does not say why. Three of sixty-four might mean the file ended or might mean an error occurred, and fread's contract says you must ask feof or ferror to find out. A program that treats a short count as "end of input" silently swallows read errors.

Convention C — a value plus errno. The return value is in range and looks completely legitimate.

strtol on an overflowing input returned 9223372036854775807 — LONG_MAX. There is nothing wrong with that number. The only thing distinguishing it from a successful conversion of a very large value is errno, which is ERANGE. This convention is the dangerous one precisely because there is no visual cue.

Convention D — it cannot fail. strlen has no failure value; neither does memcpy or abs. That does not make them safe — it moves the entire burden to question 1, because the only way to get a wrong answer from strlen is to violate its precondition, and then the behaviour is undefined rather than wrong.

4. The One errno Fact You Need Now

Convention C is unusable without this, and it catches people out constantly:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    errno = 0;
    FILE *bad = fopen("/no/such/path", "r");
    printf("after a failed fopen : errno=%d (%s)\n", errno, strerror(errno));

    FILE *good = fopen("tiny.txt", "r");
    printf("after a SUCCESSFUL fopen, errno is still %d (%s)\n", errno, strerror(errno));
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
after a failed fopen : errno=2 (No such file or directory)
after a SUCCESSFUL fopen, errno is still 2 (No such file or directory)

A successful call does not clear errno. The library sets it on failure and is permitted to set it on success too; nothing ever resets it for you.

errno is only meaningful after a call that has already reported failure by its own convention. Testing errno to decide whether something failed reads a value that may be left over from any earlier call — possibly one inside the library that you never made.

So convention C always has two steps: set errno = 0 immediately before the call, then test the documented failure indication and errno afterwards. Chapter 12.8 is the full treatment; this is the part you need to read the other eight chapters.

5. What a Contract Does Not Say

Question 1 produces the preconditions, and a precondition is not a check the function performs.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    size_t strlen(const char *s);

Its precondition is that s points to a string — a sequence of characters with a terminating null, entirely within one object. If you hand it something else, strlen does not return an error. It walks memory until it finds a zero byte, which is Chapter 8.5's measured failure mode.

A library function assumes its preconditions and checks almost nothing. The contract tells you what you must guarantee; guaranteeing it is your side of the bargain, and the consequence of failing is undefined behaviour, not a diagnostic.

Four preconditions worth recognising because they recur across the whole library:

A pointer must be valid and non-null unless the contract explicitly permits null. free(NULL) is defined (Chapter 9.6 §5); strlen(NULL) is not.

A buffer must be large enough for everything the function may write. Chapter 8.5's entire subject.

Source and destination must not overlap, for many functions — memcpy says so explicitly, memmove is the one that permits it.

A string must be terminated. Chapter 8.1's rule, and the precondition behind most of <string.h>.

When a contract and your code disagree, the contract wins silently.

6. Where to Find the Answers

In decreasing order of authority:

The standard itself. The final word on what is guaranteed, implementation-defined, unspecified or undefined. Working drafts are freely available and are the place to settle an argument.

Your platform's manual pages — man 3 fopen. Fast, usually accurate, and they describe your implementation, which means they mix standard behaviour with platform extensions without always marking which is which. Excellent for looking something up, risky for deciding what is portable.

A good online reference. The best ones mark which standard version introduced each function and flag the implementation-defined parts.

The header on your system. It gives you the exact prototype and nothing about behaviour. Useful for settling a signature question, useless for a contract.

Use the manual for speed and the standard for portability questions. If you are asking "will this work on another compiler", only one of the four sources can answer.

7. Headers, and the Optional Parts

C17's 29 headers divide into groups that are worth knowing as groups rather than as a list:

GroupHeadersFor
Essentialstdio.h stdlib.h string.hI/O, allocation and conversion, strings
Types and limitsstddef.h stdint.h limits.h float.h stdbool.h inttypes.hsizes, widths, ranges
Diagnosticsassert.h errno.hchecking and error reporting
Numericmath.h complex.h tgmath.h fenv.harithmetic beyond the operators
Characters and textctype.h wchar.h wctype.h uchar.h locale.hclassification and wide characters
Control and timesignal.h setjmp.h time.hprogram control, clocks
Concurrencythreads.h stdatomic.hthreads and atomics
Variadic and alignmentstdarg.h stdalign.h stdnoreturn.h iso646.hfunction and declaration support

Some of it is optional, and the standard tells you how to ask — but only for some parts. Measured here:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
__STDC_NO_THREADS__  defined     -> <threads.h> is NOT provided
__STDC_NO_ATOMICS__  not defined -> atomics provided
__STDC_NO_COMPLEX__  not defined -> complex provided
__STDC_NO_VLA__      not defined -> VLAs provided
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
includable: 27 of 29
not available: threads.h uchar.h

8. Applying the Method

Three functions, read with the four questions. None of them is taught here; the point is that the method produces a usable answer without the chapter that covers them.

fgets

assumesbuf points to at least n writable bytes; n > 0; stream is open for reading
returnsbuf on success
fails byreturning NULL — convention A — for end-of-file or error, so feof/ferror decides which
does not promisethat a full line was read; that the result contains a newline

That last row is the one that produces bugs, and Chapter 12.3 is about it.

strtol

assumesthe string is null-terminated; base is 0 or 2–36
returnsthe converted value; *end set to the first unconverted character
fails byconvention C — LONG_MAX/LONG_MIN with errno == ERANGE; and "no digits" is reported by *end == input
does not promisethat it converted anything at all

Two failure modes, reported two different ways, neither of them by the return value alone.

memcpy

assumesboth pointers valid for n bytes; the regions do not overlap
returnsits destination argument
fails byconvention D — it cannot
does not promiseanything about overlapping regions, where behaviour is undefined

The entire risk is in row one, which is why Chapter 12.9 pairs it with memmove.

9. What You Should Be Able to Say Now

  1. The library is specified by the same standard as the language, to the same precision.
  2. C17 has 29 headers; measured here, 27 were includable.
  3. The four questions: what does it assume, what does it return, how does it report failure, what does it not promise.
  4. Question 4 is the one people skip and the one that produces non-portable code.
  5. Four failure conventions, not interchangeable: sentinel, short count, value plus errno, cannot fail.
  6. Measured: fopen → NULL; fread asked 64 and got 3 with feof=1; strtol overflow → LONG_MAX with errno == ERANGE; strlen("abc") → 3.
  7. A short count does not say why — ask feof or ferror.
  8. Convention C's failure value looks legitimate. There is no visual cue.
  9. Measured: a successful fopen left errno at 2. Set errno = 0 before, and test it only after a documented failure.
  10. A precondition is not a check. The library assumes and almost never verifies.
  11. The standard for portability, the manual for speed.
  12. Only threads, atomics, complex and VLAs have conditional-feature macros — uchar.h was simply absent, with nothing to announce it.

10. Exercises

Exercise 1 — answer the four questions. For each, using any reference you like, write the four-row table from §2.

  • fputs
  • calloc
  • atoi
  • memmove

Exercise 2 — name the convention. For each, say which of §3's four it uses and what the exact failure indication is.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
FILE *fopen(const char *, const char *);
size_t fwrite(const void *, size_t, size_t, FILE *);
int fclose(FILE *);
double strtod(const char *, char **);
void *memset(void *, int, size_t);
int printf(const char *, ...);

Exercise 3 — the legitimate-looking failure. Explain why strtol returning LONG_MAX is harder to handle than fopen returning NULL, and write the complete correct call including both failure modes from §8.

Exercise 4 — the short count. A loop reads with fread and stops when the count is less than requested, treating that as end of input. Describe the bug, name the two functions that distinguish the cases, and rewrite the stopping condition.

Exercise 5 — the stale errno. This code reports a failure that did not happen. Say why, and give the two-line fix.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
FILE *f = fopen(path, "r");
if (errno != 0) { report("could not open"); return -1; }

Exercise 6 — find the precondition. For each call, state the precondition being violated and what the contract says happens.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
strlen(NULL)
memcpy(buf, buf + 2, 10)
strcpy(dst, src)              /* dst is 8 bytes, src is "a longer string" */
fgets(buf, 0, stdin)

Exercise 7 — portability question. You must decide whether a function you have never used is safe to call on three different compilers. Say which of §6's four sources you would consult, in what order, and what each one can and cannot settle.

Exercise 8 — test for the optional parts. Write the preprocessor code that reports which of threads, atomics and complex arithmetic your implementation provides. Then say why the same technique cannot tell you whether <uchar.h> exists.

11. Summary

  • The library is part of the language, specified to the same precision. 29 headers; 27 includable here.
  • Four questions: assumes · returns · reports failure · does not promise.
  • Four failure conventions: sentinel · short count · value plus errno · cannot fail.
  • Measured: fopen → NULL · fread 3 of 64 with feof=1 · strtol → LONG_MAX, errno == ERANGE · strlen → 3.
  • A short count does not say why; convention C's failure looks legitimate.
  • Measured: a successful call left errno set. Clear it before; test it only after a reported failure.
  • A precondition is not a check — the library assumes and does not verify.
  • The standard settles portability; the manual is for speed.
  • Only four features have conditional macros — a missing header may be announced by nothing at all.

The next chapter takes the function you have used more than any other and reads it with exactly this method, which turns out to be more interesting than it sounds. printf is variadic, so the compiler cannot check its arguments against its format string by any ordinary rule — and the standard's position on a mismatch is not that you get odd-looking output. It is that the behaviour is undefined, in the full Chapter 9.12 sense, which means the question is not what gets printed but whether the program has any meaning at all. There is also a warning flag that recovers most of the checking, which is on by default in one of the two configurations this curriculum uses and which you should make sure is on in yours.

Standards & specifications

Governing standard
ISO/IEC 9899 (the C language standard)(opens ISO/IEC (JTC 1/SC 22/WG 14) in a new tab)

Defines the C language itself — the type system, expression semantics, object model, storage duration and the behaviour categories a conforming implementation must honour. It is a FAMILY of editions (C89/C90, C99, C11, C17, C23) and this curriculum names the edition wherever a claim depends on one. It does not define any foreign-function interface to a hardware description language, and it does not define platform behaviour: what a memory-mapped access does on real silicon is the implementation's and the device's contract, not this standard's. Working drafts published by WG14 are freely available and are what most engineers read day to day; a draft is not the published standard.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the C Programming curriculum.