In order to avoid linking code that isn't used, the linker works from left to right, keeping track of functions that are undefined. If a library defines some function that hasn't been used in preceding code, then the function is presumed to be unneeded, so the definition is ignored and not linked into the code.
Thus, in your example,
clang -ggdb -Wall -Werror hello.c -lcs50 -o hello
Let's suppose that get_string
is the only CS50 Library function used in hello.c
. The linker first sees a compiled version of hello.c
with a reference to a function named get_string
that hasn't yet been defined. The linker therefore remembers to look for a definition of get_string
in subsequent libraries. Then, the linker gets to the -lcs50
part. Here it finds the get_string
definition that it was looking for. However, the CS50 Library also contains many other functions that aren't used by hello.c
, such as get_int
. If get_int
isn't going to be used, then there's no point in linking it into the final compiled hello
executable. That would just waste space. So, the linker simply forgets about get_int
and the other unused functions.
If, on the other hand, we write
clang -ggdb -Wall -Werror -lcs50 hello.c -o hello
…then the first thing the linker sees is the CS50 Library and its various function definitions. But it doesn't know that any of these functions will be used, so it forgets all of them! Then the linker moves on to hello.c
. At this point, it sees that get_string
is needed. Alas, the previous definition of get_string
has already been forgotten. The linker will at this point make a note that it needs a definition for get_string
, but since this is the last file that we're linking, we won't find a definition, and the linking process fails.