I\'m far from fully understanding how the C++ linker works and I have a specific question about it.
Say I have the following:
Utils.h
Think of each function as a node in a graph.
Each node is associated with a piece of binary code - the compiled binary of the node's function.
There is a link (directed edge) between 2 nodes if one node (function) depends on (calls) another.
A static library is primarily a list of such nodes (+ an index).
The program starting-node is the main()
function.
The linker traverses the graph from main()
and links into the executable all the nodes that are reachable from main()
. That's why it is called a linker (the linking maps the function call addresses within the executable).
Unused functions, do not have links from nodes in the graph emanating from main()
.
Thus, such disconnected nodes are not reachable and are not included in the final executable.
The executable (as opposed to the static library) is primarily a list of all nodes reachable from main()
(+ an index and startup code among other things).
This depends a lot on what tools and switches you use in order to link and compile.
Firstly, if link some_huge_lib
as a shared library, all the code and dependencies will need to be resolved on linking the shared library. So yes, it'll get pulled in somewhere.
If you link some_huge_lib
as an archive, then - it depends. It is good practice for the sanity of the reader to put func1 and func2 in separate source code files, in which case in general the linker will be able to disregard the unused object files and their dependencies.
If however you have both functions in the same file, you will, on some compilers, need to tell them to produce individual sections for each function. Some compilers do this automatically, some don't do it at all. If you don't have this option, pulling in func1 will pull in all the code for func2, and all the dependencies will need to be resolved.
Including or linking against large libraries usually won't make a difference unless you use that stuff. Linkers should perform dead code elimination and thus ensure that at build time you won't be getting large binaries with a lot of unused code (read your compiler/linker manual to find out more, this isn't enforced by the C++ standard).
Including lots of headers won't increase your binary size either (but it might substantially increase your compilation time, cfr. precompiled headers). Some exceptions stand for global objects and dynamic libraries (those can't be stripped). I also recommend to read this passage (gcc only) regarding separating code into multiple sections.
One last notice about performances: if you use a lot of position dependent code (i.e. code that can't just map to any address with relative offsets but needs some 'hotpatching' via a relocation or similar table) then there will be a startup cost.