Showing posts with label Linking. Show all posts
Showing posts with label Linking. Show all posts

2008/02/26

C: Declaration vs Definition

In computer science, there is a subtle but important distinction between a declaration and a definition.

A declaration is a statement that something exists and what its characteristics are (name, and type). A definition is saying what that thing is.

A function declaration in C looks like this: void foo();. Note the semicolon. If we were to create a variable like that (int i;) we would declare that the variable exists without assigning it a value. When we declare a method without giving it a body, was are saying that this method exists somewhere, but we are not saying what it is. [Note: the variable in the example would still be "defined" since it was allocated space.] In C, these are referred to as function prototypes. In Java, they are called method signatures. They are functionally and conceptually the same.

To define a function, we must give it a body: void foo() {...}. This is saying what the function is and is a shorthand for the concept of saying: void foo() = {...}.

You can think of all of the symbols in a program as entries in a dictionary. The declarations are listing all of the words, and the definitions are assigning them meanings. Because programs must be unambiguous (so that they can be machine executed), each word that we declare must have exactly one definition. It would be ambiguous to have words with no definition (no meaning) or multiple definitions (multiple meanings) -- the machine would not know what to do.

Declarations can be used by the compiler to verify that function calls and function definitions match the exposed prototypes. C can use the function declarations (separated from their definitions) to achieve seperate compilation.

Separate compilation allows the differing functional units of a program to be compiled independently. If the function "foo" calls the function "bar," the two of them can be compiled separately if they both know the prototype for the "bar" method. Bar does not have to know anything about foo, and foo only has to know the declaration for bar. The linker can link the definition of bar to the call to bar in the method foo.

Separate compilation allows for large projects to be compiled without having to recompile every function every time a change is made. In the previous example, a change to foo would not require bar to be recompiled, and a change to bar would not require foo to be recompiled. A change to the declaration of bar would require both foo and bar to be recompiled though.

See also: Variable Definition vs Declaration

Stages of Compilation

Compilation in C has two major stages. The first stage, "compiling" consists of preprocessing, lexical, syntactic, and semantic analysis, followed by optimization and code generation. All of the compile errors that you are familiar with in Java come from this first stage.

The input to the first stage is your .c and .h source files, and the output is a .o object file. The object file consists of two parts, the machine code equivalent of the source file, and a symbol table which lists all of the symbols (functions, variable, and a few other things) that this object files provides and requires.

The second stage of C compilation is "linking." A linker takes in a set of object files and "links" the symbols in the symbol tables together. If one symbol table requires a "bar" function, the linker connects it to the object file that provides it. The output from the linker is an executable file.

In Java, there is very little static linking -- most is done dynamically at run-time, so this will be a new source of errors for you. Fortunately, the errors generated by the linker are usually very straightforward.

There are two major sources of errors from the linker. The first occurs when a symbol is required by one object file, but not provided by any other. In this case, the linker cannot make a required link, so it will generate a "symbol not defined" error. [The closest equivalent in Java is a "NoClassDefFoundError," a "NoSuchFieldError," and a "NoSuchMethodError" at runtime.] The other kind of error comes when a symbol has been provided by more than one object file. In this case, the linker does not know which symbol to link to, so it will generate a "multiply defined symbol" error.

Linking errors for methods come about because of the distinction between method declaration and method definition in C, which is the subject of the next post.