Launch School has occasional optional group seminars, where each student is assigned to a team and one team per week teaches about a topic. The last seminar was based on C, using
as our guide. My team was tasked with explaining the difference between Static and Dynamic libraries. I decided to specifically present on the part of the chapter that explains static libraries. While I can’t share the final recording, I can share my presentation with a transcript of roughly what I said using each slide. Head First C
I made this presentation using Canva. The text is extra large to make it easier for participants to read on Zoom while screen sharing.
It’s recall time! Take a few minutes to answer the questions, and share your answers in the chat. You can answer as many as you want, as in depth as you want. Take some time to look over other people’s answers. So we’re going to collectively fill in any gaps we might have in our understanding.
Let’s go over the answers. There are several different ways to answer these questions, but I want to touch on some of the important concepts that we’ll need to remember to fully understand the concepts that we’re going to see today. First of all, why do we use headers? Headers are function declarations that are useful for letting the compiler know what to expect in a program. Why do we use header files? Header files are headers extracted into a separate file that has the .h suffix. And one of the main answers related to using header files is scalability. We can collect our header files into one central location and that allows us to access them and access the functions themselves in all of our programs. So that is also related to object files. So why do we create object files? Normally, this is something the compiler would do on its own, but the idea behind breaking down this process and doing it more manually is that it allows us to have more control over which object files we are linking, which leads to a faster compilation process.
I’m going to be talking about a static library and what kind of files comprise that library, what kind of decisions we want to make as we’re storing those files, and also how to access them syntactically.
The main difference between static libraries and dynamic libraries is when the library is accessed by the compiler. In a static library, the code is accessed at compile time. This means that the compiler will search for all of the pieces that it needs from that library, pull them into the program, and compile it, leaving an independent file that can work on its own, even if it doesn’t have access to the library in the future. If you wanted to get rid of the library, or send that program to another person, they wouldn’t need to have access to the library because everything they need is there in the program already. One of the effects of that is that the program ends up being a lot larger, but it’s more independent.
The static library is like its own ecosystem. I was reading an article that compared it to the idea of a pacemaker, in that it needs to be able to do what it’s going to do independently – once you put it in there, it’s not coming out. It needs to work well on its own, and it needs to be pretty bug free if you’re going to be sharing it with other people.
When we’re thinking about scalability, what kinds of files do we need to have access to when we’re writing our programs in order to provide that scalability? One of those files is header files, and the other is object files. We’re going to talk about header files first, what kind of storage options we need to consider when we’re thinking about where to put them on our system. Then we’ll talk about the different options we have for object file storage, as well.
For header files, we have two different storage options: we can either store them in a standard directory, or we can store them somewhere else, a local directory, maybe another directory called my_header_files. We’re going to talk about what kind of syntax we need to use to tell the compiler where to find these header files, depending on where we want to store them and also how much pathname writing we want to do.
If you remember, standard headers and local headers have a difference in how to write them when we are referencing them in our include statement. When we’re going to store our headers in a standard directory, we are going to need to use those angle brackets in our include statements to refer to the header to the header file. There are several different places in the standard directories that we can store them and that depends on your system, your administrative permissions, and you can see more info about that on p. 355.
When we have our header stored in a local directory, we have several options for telling the compiler how to find them: in the .c files themselves, we can specify the pathname of the header in the include statements, or when compiling we can tell the compiler what directory to look in to find the headers.
Here I have a bday cake c file that uses a function stored in the bake c file. The bake c file uses a header file called bake.h, and this header file is stored in a directory called my_header_files. So our first way to let the compiler know where to find the header file is by specifying the pathname in the include statements. When we go to compile, the compiler will be able to find the header.
For the second option, we are letting the compiler know where to look for the header files when we compile. In our c files, the include statement only has the name of the header file without the path written out. During the compilation process, we use the uppercase -I command to tell the compiler which directory to look in to find the header files needed in the program.
So now that we’ve talked about header files and storage options for those, let’s talk about object files. For object files, we have a couple of options for storage: we can store them in a shared directory, and the compiler will access each individual file separately from that directory. Let’s say your family is pretty picky when it comes to cakes and some people like chocolatey cakes, some people like strawberry cakes. So when we go to the store, we decide to just buy individual slices of cakes. Then we have to carry all those packets of slices and they can get a little unwieldy, a lot of packaging. The other option is to create an archive. An archive would be like buying a whole cake: it is what it is. We can’t go back and decide that we want to change its flavor. But it’s more presentable if we’re having our birthday party, it’s easier to work with.
So what does this look like in code? When we’re compiling using files out of a shared directory, we have to compile using the full pathnames of all of the object files. When we compile using an archive, we just reference the name of the archive and that’s it. In this case, our archive name is cake.
How do we create an archive? We use the ar command, the flags rcs, and then the prefix lib and the suffix .a. Including the prefix lib let’s the compiler know that this is a library. Then you list the names of the object files. Or if you want to convert the entire shared directory into an archive, you can use the splat operator.
So then how do we compile using archives? Here you can see that I’m making a bday cake program, so I list my source code first, then I tell the compiler to look for the library using the -l switch. So you write it with a lowercase l and with the name of the archive. If my archive is located in another directory, perhaps a directory called my_lib, I can tell the compiler to look in that directory using the uppercase L flag.
A couple of other useful commands: we can use the nm command to see what’s inside of an archive, and we can also extract a single file from the archive.
Review questions: True or false: a program compiled using a static library needs to have access to the library at runtime. If you think this statement is true, give a thumbs up, and if you think it’s false, give a clap.
If you chose false, you are correct! Static libraries are used in the compilation process, so at runtime we don’t need the library, which is actually one of the benefits of using a static library.
Question 2: My compiler says it can’t find the header files. What option do I need to use to tell the compiler where to look? Uppercase -I or uppercase -L
If you chose uppercase -I, you are correct! Remember that uppercase L is used to indicate to the compiler where it needs to look for an archive.
3. What needs to go in the blank space below? (Hint: what process is happening here?)
If you chose lib, you are correct! The ar command is used to create the archive using the lib prefix and the .a suffix. The -l flag is used during the compilation process.
That’s it for static libraries! I’m excited to pass it on to my teammates, who will tell you more about dynamic libraries.