Ch20.1: What Freestanding and Hosted Implementations Are

中文(简体)

1. The Definition

The standard defines two kinds of implementation. The reference text is cppreference: freestanding implementation, and it fits on one screen, which is itself part of the problem this chapter is about. Here is what it says, in the order it says it:

That is the entire contract. Notice what is not in it: nothing about which language features are available. As far as the standard's own wording goes, a freestanding implementation still has exceptions, RTTI, new, thread_local and floating point — it just has fewer headers. That gap between "fewer headers" and "the target cannot do this at all" is where C++ freestanding lived for three decades, and it is the subject of 20.3.

2. Asking the Compiler Which World You Are In

The macro is not a hint; the compiler defines it itself, from how it was configured. One program, two command lines:

#include <cstdio>

int main() { ::std::printf("%d\n", __STDC_HOSTED__); }
$ g++ -std=c++26 -O2 hosted.cc -o h1 && ./h1
1
$ g++ -std=c++26 -ffreestanding -O2 hosted.cc -o h2 && ./h2
0

So -ffreestanding does flip the macro, and from the compiler's point of view you are now in a freestanding translation unit. Keep that output in mind for section 4, because the next thing that happens is that the program keeps working perfectly on your desktop — and that is the trap.

3. What -ffreestanding Actually Changes

The flag changes exactly three things, and none of them is the library:

  1. __STDC_HOSTED__ becomes 0. Code that checks it now takes its freestanding branch.
  2. Builtins that require a hosted C library are turned off. printf is no longer a builtin to be expanded into puts, malloc is no longer assumed to be __builtin_malloc, and main is no longer special.
  3. Nothing else. The include paths, the library, the start files and the linker script are the same ones as before. You are still pointed at your desktop /usr/include and your desktop libc.

This is the single most misunderstood flag in C++. -ffreestanding is a promise from you to the compiler that you will not depend on the hosted runtime. It is not a promise from the toolchain that a freestanding runtime exists. The toolchain you have was built for your desktop, so it ships your desktop's headers, and the result is a hybrid that is neither: a translation unit that believes it has no operating system, compiled against a library that assumes one.

4. The Hybrid Breaks Immediately, and the Error Is Informative

Here is the same desktop toolchain with -ffreestanding, compiling a file that includes <cmath>. libstdc++ guards that header:

$ g++ -std=c++26 -ffreestanding -c t.cc
In file included from .../include/c++/17.0.0/cmath:46,
                 from .../include/c++/17.0.0/math.h:36,
                 from t.cc:1:
.../include/c++/17.0.0/bits/requires_hosted.h:34:4: error: #error "This header is not available in freestanding mode."
   34 | #  error "This header is not available in freestanding mode."
      |    ^~~~~

bits/requires_hosted.h is libstdc++'s gate: it inspects __STDC_HOSTED__ and refuses the header when it is 0. So the desktop's standard library is not merely unsuitable for freestanding work, it knows it is, and says so. The header list it accepts and refuses is measured in 20.6, and it is not the list the standard requires.

5. What "Freestanding" Is a Property Of

The conclusion is the one that reorganises how you think about toolchains: freestanding is a property of an installation, not of a command line. To have one you need, at minimum:

PieceWho supplies itWhat goes wrong without it
A compiler that targets your CPU without assuming an OS A cross-build of the compiler (20.5) Wrong ABI, wrong floating-point conventions, host headers found by accident
A C library, or none newlib, picolibc, musl, or your own kernel's memcpy undefined reference to `memcpy' and friends
A C++ library subset The standard's required list, or a vendor's extension, or nothing fatal error: 'cstddef' file not found
Start-up code and a linker script You, or your board support package Execution begins at a random address with an uninitialised stack
The compiler runtime: division helpers, `memcpy`, soft float libgcc, compiler-rt Link errors on arithmetic that the CPU has no instruction for

Every row is a thing that a hosted toolchain gives you for free and a freestanding one makes you responsible for. That is why this chapter is not a list of compiler switches: the switches are the easy part.