Libraries and DLLs


Richard Heathfield, 24 July 2002



What is a library? What are they for?

What is a DLL?

How do I create a library?

How do I use a library?

How should I organise code within the library?

Where should I keep my libraries and header files?
 
 

What is a library? What are they for?


In computer programming jargon, a library is a repository for object code, organised in a way that permits re-use of that object code (whether or not you have the original source code). Once the object code is compiled by a particular implementation, it can be stored in the library, which can then be linked into as many programs as you like. When you link the library to the program, any object code required by the program is copied from the library into the program. The benefits of using libraries are:

You don't have to keep copying source code all over the place
You don't have to keep re-compiling the same code over and over
You can provide functionality to other programmers without releasing source

This last-named benefit may seem to Open Source developers to be more of a disadvantage, of course, but that's a socio-economic issue, not a technical issue!
 

What is a DLL?


A DLL (or Dynamic Link Library, or Dynamically-Linked Library) allows you to link object code to the program at runtime, rather than at link time. This has advantages and disadvantages. The principal advantage is that you save space; if more than one program uses a particular function, that function needn't exist in two or more places. It can simply reside in the DLL, and be called by any program that needs it. The principal disadvantage of DLLs is that of code management. When the function you need is nailed to your program at link-time, you know you've got the function you need. When you call it at runtime, there's a risk that the DLL has been "upgraded", and your function may have been changed by someone else. (This is less likely for DLLs you write yourself, for your own use, as you are the only one likely to upgrade them - but even then, it is a possible danger if you are a forgetful sort of person.)

A sample DLL is available here.
 

How do I create a library?


This varies depending on your library-building tool. Here's some source code, which we'll build into a library using several different implementations.

/* foo.c */
unsigned int halve(unsigned int n)
{
   return n / 2;
}
/* end of foo.c */

Before we add this to any libraries, though, let's get our file organisation right. Firstly, we need to ensure that our calling programs (the programs that want to use this library code) have access to the function prototypes of the functions within the library. We can do this by writing a header.

/* foo.h */
#ifndef FOO_H_
#define FOO_H_ 1

/* Function: halve()
 * Purpose: returns half the value of the input
 */
unsigned int halve(unsigned int n);

#endif

Okay, that's all the caller needs to know. Now let's compile our source, and stick it into a library. I'll demonstrate using command line tools for various implementations. If you would rather use a GUI, and can't find out how to drive your particular GUI, ask on Usenet.

First, compile foo.c as follows:

Linux

gcc -W -Wall -ansi -pedantic -O2 -c -o foo.o foo.c

Note the -c switch. This means "just compile, don't link". That's rather important (especially as foo.c doesn't have a main() function!).

Borland

bcc32 -A -c foo.c

-A just means "turn on those ANSI rules".

Microsoft

cl -W4 -Za -c foo.c

-Za means "disable Microsoft extensions".


Next, let's create the library.

Linux

ar cr libmycode.a foo.o

The "c" means "create the library"; if the library already exists, omit this flag. The "r" means "insert the file into the archive, replacing an existing file if necessary". Note that I've named the library "libmycode.a". There's a reason it starts with "lib", which we'll come to in due course.

Borland

tlib myborlandcode.lib /C +foo.obj

/C means "make the library case-sensitive". Since C is a case-sensitive language, this is a jolly good idea!

The + means "add", so +foo.obj means "add foo.obj to the library".

Microsoft

lib /out:mymscode.lib foo.obj



 

How do I use a library?

To use the library, we'll need a program. Here's one:

#include <stdio.h>
#include "foo.h"

int main(void)
{
  printf("%u\n", halve(6U));
  return 0;
}

Save this as testfoo.c.  I will assume that, for the time being, you're using the same directory as you used to build the library. That's not a constraint on library usage; it's just that I can only explain one thing at a time. Bear with me.

Let's build the program:

Linux

gcc -W -Wall -ansi -pedantic -O2 -L"." -o testfoo testfoo.c -lmycode

The quotes around the full-stop (period) are optional. The -L switch means "look for libraries along these paths, as well as along wherever it is you usually look", and of course "." means "this directory right here". The -lmycode switch means "link in a library called libmycode.a". Yes, the compiler expands the library name all by itself, so make sure that the compiler does this expansion correctly, by paying careful attention to the real name of the library - start it lib and finish it .a for best results.

Clearly, you can keep libmycode.a anywhere on your filesystem, as long as you specify the -L switch correctly. If you have write access to /usr/local/lib, you may want to stick your library code in there.

Borland

bcc32 -A testfoo.c myborlandcode.lib

Microsoft

cl -W4 -Za testfoo.c mymscode.lib
 

In each case, the program is built correctly, and testfoo can call the halve() function, despite the compiler not having access to the source code of halve() at the time that testfoo is built.

But we're not finished quite yet. The library only has one function in it. If I went to my local lending library and found that they had only one book in stock, I'd probably be a bit disappointed (unless it was a really good book!). Let's find out how to add new object files to existing libraries.

Here's another function:

/* bar.c */
unsigned int quarter(unsigned int n)
{
   return n / 4;
}
/* end of bar.c */

You may wish to knock up a bar.h file, or (since quartering is clearly related to halving) simply add the prototype to foo.h. Then, compile this to either bar.o (Linux) or bar.obj (Borland, Microsoft). Here's how to add it to the existing library:

Linux

ar r libmycode.a bar.o

Borland

tlib myborlandcode.lib +bar.obj

To replace an existing object file in the library, by the way, use  -+filename.obj   instead. If you don't do this, Borland plays safe and refuses to update the file.

Microsoft

Apparently (and I haven't tested this yet) you can add an object file to an existing Microsoft library by letting the library be one of the inputs to the lib command, with the new object(s) being the other input(s):

lib /out:mymscode.lib mymscode.lib bar.obj



 
 

How should I organise code within the library?


Well, it's up to you. Be aware, however, that some linkers (very possibly including the one you use) aren't able to extract functions from libraries. They can only extract object files from libraries. Thus, if your program needs just one function from a bunch of five all in the same object file, you could well end up with a bigger binary than necessary.

"Well, yes, all right," you say, "but why is that a problem? After all, I could just write one function in each object file." Absolutely right. You can do that, and that's fine. So, there's one way you might organise your code to make it linker-friendly; just put one function in each object file.

Mind you, there are times when functions are pretty well bound to be used together in a well-written program. Examples that spring to mind include FindFirstFoo() and FindNextFoo(), or CreateWibble() and DestroyWibble(). Or maybe your externally-exposed function needs several static helper functions. These are good reasons to include more than one function in a particular object file.

It's entirely up to you, though.


Where should I keep my libraries and header files?


Again, this is up to you. Here's a suggestion which you are entirely free to adopt, adapt, or ignore entirely:

Whilst the library is under development, treat it as you treat any other project.

Once it's settled down sufficiently that modifications of its existing functions are likely to be rare, copy it to a well-known location. On Linux, the obvious place is /usr/local/lib. On Windows, it's less clear-cut. I use d:\alldata\rjh\dev\lib for mine. At the same time, copy the relevant header(s) to an appropriate include directory (/usr/local/include or d:\alldata\rjh\dev\include, in my case). Adjust your development tools, via switches or GUI settings or whatever, to check these places when compiling and linking code.
 
 

That's it. Suggestions for improvements to this page are most welcome. Kudos to EwIck for sorting out how to add objs to existing libs in Microsoft C.


You are visitor number  - call again soon!