C Part I · Module 1 · Understanding C and How Programs Work
Which C Are We Learning: Standards, Compilers and Extensions
Chapter 1.1 said the language is the specification and your compiler is one implementation of it. That sentence has a consequence worth settling before you write any code: the specification has been revised several times, so there is more than one version of C, and your compiler almost certainly offers things the specification never described. This chapter fixes what this curriculum means by C — C17, with C23 differences named where they matter — and then gives you the four categories that explain why two compilers can disagree about the same source file and both still be correct. It is short on history and deliberately precise about vocabulary, because every later chapter will lean on the distinctions made here.
Foundation9 min readCPart IFundamentalsStandards
Part I · Module 1 · Chapter 1.3 · Which C Are We Learning: Standards, Compilers and Extensions
1. The Question Your Compiler Cannot Settle
Here is a situation that happens to every C programmer eventually. You write something, your compiler accepts it, the program runs, and you move on. A colleague takes the same file, compiles it on a different machine, and it does not work — or worse, it works differently.
Someone now has to answer a question, and the compiler cannot answer it: was that source file correct C?
Your compiler accepting something is evidence, but it is not proof. It might be accepting something the specification never described. The specification might permit both behaviours you saw. Or your source might be broken in a way that neither compiler is obliged to point out.
Those are three genuinely different situations with three different fixes, and a programmer who cannot tell them apart spends a long time guessing. This chapter gives you the vocabulary to tell them apart.
2. C Has Versions
The specification is not one fixed document. It has been revised, and each revision has a short name that you will see in compiler options and in other people's code:
- C89 (also called C90) — the first standard version of the language.
- C99 — a substantial revision that added a good deal.
- C11 — the next substantial revision.
- C17 — a correction of C11. It adds no new language features; it fixes defects in the wording.
- C23 — the most recent revision, with genuinely new features.
That is all the history you need. Two things about the list are worth noticing, though.
The first is that the revisions are overwhelmingly additive. The C described in C89 is still, in large part, the C described in C23. Very little has been taken away. This is why C code written decades ago often still compiles, and it is also why "which version" turns out to matter less for beginners than the list above suggests.
The second is that C17 adding no new features is exactly what makes it useful as a baseline. It is C11 with the wording repaired, which means almost every compiler in active use supports it completely.
3. The Baseline This Curriculum Teaches
Stated plainly, so that you can hold this curriculum to it:
This curriculum teaches C17, and names C23 explicitly wherever the difference matters.
C17 is the choice because support for it is effectively universal — including in the semiconductor tools Part II eventually deals with, which tend to be conservative about compiler versions. C23 is real and worth knowing about, but its features are not yet uniformly available everywhere you might need to compile, and a curriculum that quietly assumed them would teach you code that some of your future colleagues cannot build.
You will see the baseline expressed concretely as a compiler option:
cc -std=c17 program.c -o programThat -std=c17 is you telling the compiler which set of rules to apply. Chapter 1.5 is where you will actually run a command like this and see what the other parts of it do — for now, only the -std=c17 part matters, and what matters about it is that the version of C you are compiling against is a decision someone makes, not something that is simply true of your machine.
4. Where the Standard Deliberately Leaves Room
Now the part that pays for itself repeatedly.
It is tempting to assume a specification either defines something or is silent about it. C is more careful than that, and the difference between its categories is the difference between code that is portable and code that merely happens to work. Here are the four you will meet, each with the module that teaches it properly:
| Category | What the standard does | A plain example | Taught in |
|---|---|---|---|
| Defined by the standard | Defines the behaviour completely. Every correct implementation must produce it. | Adding 2 and 3 gives 5. | throughout |
| Implementation-defined | Requires a choice to be made, leaves the choice to the implementation, and requires it to be documented. | How many bits a whole number of type int has. Your implementation picks, and must tell you. | Module 2.5 |
| Unspecified | Permits several possibilities and requires nothing further — no documentation, no consistency. | When one line asks for two separate calculations, which of them is carried out first. C permits either, and nothing requires your compiler to tell you or to choose the same way next time. | Module 3.11 |
| Undefined | Imposes no requirements at all. | Dividing a number by zero. | Modules 13.2–13.3 |
The two middle rows are where portability actually lives. Something implementation-defined is safe to rely on once you have named your compiler and platform and checked what they chose — and unsafe to rely on the moment your code moves. Something unspecified is not safe to rely on even on one machine, because nothing obliges your compiler to be consistent.
The last row is different in kind, and it is worth being careful about the wording. When behaviour is undefined, the standard does not say "anything may happen" as a threat — it says nothing at all, which means there is no guaranteed behaviour left for you to reason about. Not for the line in question, and not necessarily for the rest of the program either. Chapter 1.1 put it without the terminology: once the language's obligation only covers programs that followed the rules, a program that breaks them has nothing to fall back on. The term for that is what you now have. Module 13 is built around it.
5. Extensions: What Your Compiler Adds On Top
There is one more reason two compilers can disagree, and it has nothing to do with the categories above.
Compilers offer features the standard never described. These are called extensions, and they exist for good reasons — a compiler writer wants to give you something useful, and sometimes a useful thing becomes standard later precisely because an extension proved its worth.
An extension is not wrong. It is simply not portable C. Code that uses one compiles with the compiler that offers it and fails with one that does not, and no amount of studying the C standard will explain why, because the feature is not in it.
The practical consequence is that your compiler will usually let you write non-portable code without comment. If you want it to tell you when you have left standard C behind, you have to ask — which is one of the things the option set in Chapter 1.5 is for.
6. Why "It Works On My Compiler" Is Not a Claim About C
Putting §4 and §5 together gives you the answer to the situation in §1. When your code works for you and not for a colleague, there are four distinct possibilities, and they are not equally good news:
- You relied on something implementation-defined, and the two implementations chose differently. Your code was reasonable; it just was not portable.
- You relied on something unspecified. Even one compiler need not behave the same way twice.
- Your code has undefined behaviour. It was never correct; one machine was hiding it.
- You used an extension. The other compiler has never heard of it.
None of those is discovered by arguing about compilers. All of them are settled by asking which category the behaviour falls into — which is why the vocabulary came before the code.
This is also the honest version of the portability claim Chapter 1.2 made. C lets you separate most of a program's logic from any one processor, but how portable a given program is depends on how much of it rests on the first row of that table rather than the others. Portability is not something C hands you. It is something you can achieve, on purpose, and the categories above are how you tell whether you have.
7. What You Should Be Able to Say Now
- C has been revised several times; the revisions are mostly additions, and old C largely remains valid C.
- This curriculum teaches C17, and says so explicitly when C23 differs.
- Which version you compile against is a decision, expressed to the compiler — not a fact about your machine.
- The standard has four different attitudes to behaviour: defined, implementation-defined, unspecified, and undefined — and they are genuinely different, not degrees of the same thing.
- Your compiler also offers extensions the standard never described; using one is not an error, but it is not portable C.
- "It works on my compiler" is a statement about your compiler, and never by itself a statement about C.
8. Exercise
Exercise 1 — classify. For each situation, say which of the four categories from §4 is involved, and give a one-line reason: (a) A program prints a different number of digits for the same value on two machines, and both compilers' manuals document their choice. (b) A program divides by zero. (c) A program produces one answer when compiled normally and a different answer when the compiler is asked to optimise harder, with no documentation of either. (d) A program adds 10 and 15 and gets 25 everywhere.
Exercise 2 — separate the two reasons. A colleague's file compiles with their compiler and is rejected outright by yours, with a message about an unrecognised keyword. Which of §5's situations is this most likely to be, and why is it not one of the four behaviour categories?
Exercise 3 — explain the asymmetry. §4 says implementation-defined behaviour can be safe to rely on while unspecified behaviour cannot, even on one machine. Explain the difference in two sentences.
Exercise 4 — apply the contract. You find a C example online that uses a feature introduced in C23. You are working against this curriculum's baseline. Describe what you should check before using it, and what you would expect to happen if you did not check.
9. Summary
- C has versions. They are overwhelmingly additive, so old C is mostly still valid C, and the version question matters less at the start than it appears to.
- This curriculum teaches C17, and names C23 where the difference matters. C17 is C11 with the wording corrected, which is why support for it is effectively universal.
- The version you compile against is a decision, expressed with an option such as
-std=c17, not a property of your computer. - Four categories, not two. Defined, implementation-defined, unspecified and undefined are genuinely different. The middle two are where portability lives; the last means there is no guaranteed behaviour left to reason about.
- Extensions are a separate matter. Useful, not wrong, and not portable C.
- "It works on my compiler" never settles whether something is correct C. The category does.
You now know which C you are learning and what kind of claims the language actually makes. The next chapter stops talking about C in general and puts a complete program in front of you — the smallest one that can exist, with every part of it accounted for.
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.
