Headers, translation units and linking
لم يُترجم بعد؛ يُعرض باللغة الإنجليزية
A C++ program is built from many files, and the compiler never sees them all at once. Follow a three-file program, main.cpp, square.h and square.cpp, as each .cpp file and the headers it includes become one translation unit, compile on their own into object files with symbol tables, and meet again in the linker. Then break it on purpose: a missing definition, a doubled one, and see why separate compilation keeps big builds fast.
5 أسئلة سريعة
اختر إجابة لتعرف السبب. تُحفَظ نتيجتك مع تقدّمك.
01What is a translation unit?
The preprocessor pastes every #include into the .cpp file. The result is one translation unit, and the compiler turns each one into an object file on its own.
02main.cpp calls square(7) but never sees square.cpp. Why does it still compile?
A declaration is a promise. The compiler checks the call against it and leaves square's address blank in main.o, noting that main.o needs square. The linker fills it in later.
03Every file compiles, but g++ main.o -o app fails with "undefined reference to square(int)". What went wrong?
main.o's symbol table says it needs square. Only square.o defines it, so linking without it leaves the need unmet. This is a link error, not a compile error.
04You put the full body of square in square.h, and two .cpp files include it. What happens at link time?
The one-definition rule allows many declarations but only one definition of a (non-inline) function in the whole program. inline allows identical definitions in several units.
05You edit only square.cpp. What is the least work needed to get an up-to-date app?
Each translation unit compiles on its own, so main.o is still valid. Build tools such as make and Ninja use this to recompile only what changed. Editing a header, though, means recompiling every file that includes it.
المسرد
- Declaration
- A statement that introduces a name and its type, such as int square(int x); without a body. It may appear many times.
- Definition
- The declaration that also provides the entity itself, such as a function's body. A non-inline function has exactly one in a program.
- Header
- A file (often .h or .hpp) of declarations that other files #include, so they can use what it declares.
- Translation unit
- One source file plus everything it #includes, after preprocessing: what the compiler turns into one object file.
- Object file
- The compiler's output for one translation unit (main.o): machine code plus a symbol table, not yet a whole program.
- Symbol table
- The list inside an object file of the names it defines and the names it still needs from elsewhere.
- Linker
- The tool that joins object files and libraries, matching every needed name with its definition, and writes the executable.
- One-definition rule (ODR)
- The C++ rule that a program may contain only one definition of each non-inline function or variable, however many declarations it has.
الملاحظات والمصادر
- The C++ standard describes nine phases of translation and calls a source file together with everything it includes, after preprocessing, a translation unit. The figure shows the preprocessor and compiler as one step, as g++ -c runs them.
- #pragma once is not in the C++ standard, but GCC, Clang and MSVC all support it. The standard way is an include guard: #ifndef SQUARE_H / #define SQUARE_H / ... / #endif.
- During compiling the cards show each #include line replaced by what it brings in. <iostream> really brings in many thousands of lines; the card shows a comment in their place.
- nm output is shortened: real lines start with an address, and on macOS C++ symbols carry an extra leading underscore (__Z6squarei). -C (demangle) turns _Z6squarei back into square(int).
- Linker messages differ: GNU ld says "undefined reference to `square(int)'" and "multiple definition of `square(int)'"; Apple's linker says "Undefined symbols" and "duplicate symbol". The C++ standard calls a missing or doubled definition ill-formed; many such cases need no diagnostic, so do not rely on the linker catching every one.
- The link step also pulls in the C++ standard library (libstdc++ with GCC, libc++ with Clang), which holds the code behind std::cout.
- The three files on the cards are real C++20 and build with g++ -std=c++20 -Wall -Wextra with no warnings. The program prints 49 with no newline.
- cppreference.com: Phases of translation
- cppreference.com: Source file inclusion (#include)
- cppreference.com: Declarations
- cppreference.com: Definitions and ODR
- cppreference.com: inline specifier
- cppreference.com: Implementation defined behavior control (#pragma once)
- Standard C++ Foundation (isocpp.org): Inline functions FAQ
- Standard C++ Foundation (isocpp.org): Coding standards FAQ
- GCC manual: Options Controlling the Kind of Output (-c, -E, -o)
- GNU Binutils: nm
- Itanium C++ ABI: name mangling
اختبار يتاح بعد الخطوة الأخيرة.