Creating the executable.
OK, this should be short and sweet. Basically Libraries are collections of object code that you don’t have the source code for, but do have the include files for. That doesn’t make much sense, so lets try explain each part of that sentence. Object code is that which the compiler creates when it compiles your C Source code. It’s basically a one to one conversion (well, not exactly but close enough) of your source code into the assembly language upon which the code is intended to run. If I have 4 source files in my code I’ve written, I should end up with 4 object files.
But that’s only step one in the compilation process. What happens next is that the compiler will then run it’s linking step where it takes those 4 object files and puts them together to form the executable file. That’s a gross simplification, but in essence it’s what happens.
While we are here, lets mention optimizing compilers and what they do. Very often if you just convert source code directly line for line, instruction for instruction you end up with working code, but usually pretty slow. Optimizing compilers look for specific situations in code to try and analyze what you are doing in order to create faster tighter code. Usually inside of the optimizer it may take advantage of CPU specific instructions, like the twin instruction pipeline in the Pentium. Certain instruction pairs can be executed simultaneously by a Pentium chip, the optimizing compiler knows this and when it generates the assembly language instructions it tries to order them to take advantage of this ability.
One drawback of an optimizing compiler is that while it makes your code faster, it does make debugging it a pain. When you try and single step through the code often variables that you want to look at just aren’t there, or are pointing at something false. The code execution jumps around and it’s just very difficult to know what’s going on.
The two types of compilations are often referred to as Debug mode and Release Mode, for obvious reasons. You use Debug when you are tinkering and finding bugs, and Release mode if you are demonstrating the application to someone.
However, when you build your program, if you use some of the C functions (like math functions, or Printf) where does the code for these functions come from? Most people think the compiler builds it, but that’s not actually so (well in most cases). The code is actually already built for you by the people that make the compiler. They have built the C functions of Printf that conform to the C standards, so it will do what you expect it to. Rather than giving you the source code they built, they give you the compiled libraries and include files.
Libraries are created very like the process for creating an executable. The compiler will build object files for each of the compiled files, then push them all together to form a .lib file instead of a .exe. The difference is that you can’t run a library. It’s not a program, it’s just a collection of callable functions. The idea is that you can then use this library inside of your own program by including them when you build you own application. You just include the .h files that are supplied with the .lib file, tell the linker that you would like to link in the .lib file and you’ll have access to the functionality that’s inside the library file. Of course how you tell the linker to include a .lib file can get complicated… I’m not going to get into that since it all depends on what compiler and makefile combination you are using. However, you now know enough to look through the manufacturers documentation to find that out.
For example, Quake III uses functions inside of a pre-compiled library to be able to compress and decompress jpeg files. Id didn’t want to have to write all that code themselves, so they went to the web, did a search for jpeg libraries, found one in the public domain, downloaded it and included it in the Quake III source code. Much faster than trying to create all that nasty color math themselves.
The draw back of using libraries is that because you don’t have the original source code, you can’t modify it. If there is a bug in there, or some functionality you need to change, you are out of luck. It’s a method that works really well for very defined functionality requirements (like compressing or decompressing a jpeg) but not for fluid functionality, like handling fixed sized arrays. You’ll always run into a situation where the arrays aren’t big enough or something of that ilk.
Now all of this is done at compile time, so by the time you run the program it’s all transparent to you how it was built. You have your .exe file and you just run it. DLL (Dynamic Linked Libraries) is a way of doing this same functionality at run time, rather than at compile time. So what you’d do here is build you set of functions exactly as you would a library, but instead of linking it to the main application at compile/link time, you’d create a .DLL instead. Note – this is PC specific. This functionality is only offered by Microsoft, not Apple or most other development environments.
When you run the .exe of your main application produced by the build, the program would automatically look for the .dll (actually, there are more than one way of doing this – you can make the program automatically look for a .dll from a specific location defined by Windows, or you can write code in the main application to look for .dll’s in a location you specify. This is all getting way ahead of where you are probably at coding wise right now, so I wouldn’t worry about it).
So what does this ability mean in terms of how you write your code? The idea behind this is that you can use .dll’s supplied by other vendors (like MS for instance) to deal with some of your programming issues, then have them upgraded over time without having to rebuild the entire project. You can fix bugs by just releasing the DLL with the offending code in it. You can also release faster versions of DLL’s, or release newer versions with more functionality built into them (although you shouldn’t remove functionality – once the app using the DLL expects a function to be there, you can’t just yank it out later, the app will get very upset).
This is actually how lots of companies release bug fixes – even Microsoft does this. When you download patches, more often than not you are downloading replacement dll’s for Internet Explorer or Windows Networking or some such.
It’s actually a pretty nice little system – too bad it’s not portable across platforms. Using a dll that someone has created can save you a lot of time. However, there are some caveats.
If you are using someone else’s .dll they have created you need to be sure that it’s actually resident on someone’s system when running your application. It’s no good creating a program to calculate the meaning of life, releasing it, then finding that people can’t run it because they are missing some .dll that you have, but they don’t.
Now the solution might be obvious to you – simply include all the .dll’s you are using in the installer for your incredible new program. But it’s not as easy as that. You start getting into version issues. Imagine there are 7 versions of the greatcode.dll you are using. Each one offers more functionality than the next, as the original vendor upgrades the functionality in the .dll. You personally are using version 6 of the dll. You can’t just install version 6 over the top of version 7 because that will reduce out the functionality of the greatcode.dll which other applications might be depending on. Now that isn’t too hard to worry about, your installer has to then look at the version of the .dll it finds and not install your version if it finds a more recent version already there (you may have already seen warning about this when you upgrade your windows stuff?). Sounds simple right? Well a) not all installers do this checking as they should, and b) it starts getting really complicated when vendors start removing functionality from their dll’s which they don’t need anymore, but you do.
There are some memory issues you should be aware of when using .dll’s as well. Currently the way that .dll’s are implemented they all have their own memory pool. They do NOT share memory. What this means is that although a function in a .dll can ‘see’ memory that’s been allocated in the main .exe (or in another .dll) via pointer passing, it cannot free this memory itself. If you try to do so you’ll end up with a memory crash, and not understand why. Just an FYI because if you do start using .dll’s you will come across this at some point. Of course this memory issue doesn’t come up with libraries, because after the compilation they are all part of the main executable anyway.