What are the three steps that must be taken when a file is used by a program?

For a C program of any reasonable size, it is convenient to separate the functions that comprise the program into different text files. There are standard ways to organize source code in order to allow the functions in separate files to cooperate, and yet allow the compiler to build a single executable.

The process of compiling C programs is different than with Java, and has important implications for how source code must be organized within files. In particular, C compilers make a single (top-to-bottom) pass over source code files. This process is very much unlike the Java compiler, which may make multiple passes over the same file, and which may automatically compile multiple files in order to resolve code dependencies (e.g., if a class is used in one file but defined in another, the compiler will compile both files). In C, it is entirely up to the programmer to decide which files need to be compiled and linked to produce an executable program.

There are three basic steps involved in compiling a C program: preprocessing, compilation of C source code to machine code (or assembly) (also called object code), and linking of multiple object files into a single binary executable program. Each of these steps are described below.

What are the three steps that must be taken when a file is used by a program?

The three basic steps when compiling C programs are preprocessing, compilation, and linking.

9.1.1. The preprocessing step¶

The preprocessing step happens just prior to the compilation phase. The C preprocessor looks for any preprocessor directives in the source code, which are any lines starting with

#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
0. The preprocessor then performs some actions specified by the directive. The text resulting from the preprocessor's action is then fed directly (and automatically) to the compilation phase.

Since a C compiler makes a single pass over a

#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
1 file, it must be made aware of all the types and signatures in order to correctly and successfully complete the compilation process. That is, if an unknown data type is encountered in the single top-to-bottom pass, the compiler will report an error. For example, here is some source code that will not compile correctly:

#include <stdlib.h>

int main() {
    struct function f1 = { 1,2};
    return EXIT_SUCCESS;
}

struct function {
    int numerator;
    int denominator;
};

Why does it fail? Simply because the definition of

#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
2 comes after its first use. To make the code correctly compile, the
#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
3 definition must precede
#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
4.

9.1.1.1. Header (
#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
5) and source (
#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
1) files¶

Because of the single-pass top-to-bottom operation of C compilers, each source file (each

#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
1 file) must identify all data types and function signatures that are used in that file in order to make the code successfully compile. The standard practice in C is to define any types and declare any functions in header files (
#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
5 files) in order to facilitate the compilation process. In one sense, you can think of the
#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
5 files as containing the "external interfaces" (i.e., the API) and data types used for a set of functions, and the corresponding
#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
1 file as containing the actual function definitions.

For example, say that we want to define the

#include "fraction.h"  // include struct fraction definition and
                       // fraction utility function prototypes,
                       // as well as other headers like stdlib.h

int main() {
    struct fraction f = {2,3};
    invert_fraction(&f);
    print_fraction(&f);
    return EXIT_SUCCESS;
}
1 type and a couple utility functions that can be used in other
#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
1 files. We might define a
#include "fraction.h"  // include struct fraction definition and
                       // fraction utility function prototypes,
                       // as well as other headers like stdlib.h

int main() {
    struct fraction f = {2,3};
    invert_fraction(&f);
    print_fraction(&f);
    return EXIT_SUCCESS;
}
3 file that contains the following:

struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);

Notice that this header file contains the

#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
3 definition, and two function prototypes. A "prototype" for a function gives its name and arguments but not its body. The function parameters do not even have to have variable names (as they're shown above), but there's no problem if they do include the parameter names.

The corresponding

#include "fraction.h"  // include struct fraction definition and
                       // fraction utility function prototypes,
                       // as well as other headers like stdlib.h

int main() {
    struct fraction f = {2,3};
    invert_fraction(&f);
    print_fraction(&f);
    return EXIT_SUCCESS;
}
5 file might contain the following:

#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}

Notice that the first line of code in

#include "fraction.h"  // include struct fraction definition and
                       // fraction utility function prototypes,
                       // as well as other headers like stdlib.h

int main() {
    struct fraction f = {2,3};
    invert_fraction(&f);
    print_fraction(&f);
    return EXIT_SUCCESS;
}
5 is
#include "fraction.h"  // include struct fraction definition and
                       // fraction utility function prototypes,
                       // as well as other headers like stdlib.h

int main() {
    struct fraction f = {2,3};
    invert_fraction(&f);
    print_fraction(&f);
    return EXIT_SUCCESS;
}
7. Any line of code that begins
#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
0 is called a preprocessor directive. We have used
#include "fraction.h"  // include struct fraction definition and
                       // fraction utility function prototypes,
                       // as well as other headers like stdlib.h

int main() {
    struct fraction f = {2,3};
    invert_fraction(&f);
    print_fraction(&f);
    return EXIT_SUCCESS;
}
9 quite a bit so far. Its meaning is simply to directly replace the
#include "fraction.h"  // include struct fraction definition and
                       // fraction utility function prototypes,
                       // as well as other headers like stdlib.h

int main() {
    struct fraction f = {2,3};
    invert_fraction(&f);
    print_fraction(&f);
    return EXIT_SUCCESS;
}
9 directive with the text in the specified file name.

A file that uses the fraction utility functions in a file called

#define MAX 100
#define SEVEN_WORDS that_symbol_expands_to_all_these_words
1 might look like the following:

#include "fraction.h"  // include struct fraction definition and
                       // fraction utility function prototypes,
                       // as well as other headers like stdlib.h

int main() {
    struct fraction f = {2,3};
    invert_fraction(&f);
    print_fraction(&f);
    return EXIT_SUCCESS;
}

9.1.1.2. Preprocessor directives¶

There are several preprocessor directives that can be listed in C source code.

#include "fraction.h"  // include struct fraction definition and
                       // fraction utility function prototypes,
                       // as well as other headers like stdlib.h

int main() {
    struct fraction f = {2,3};
    invert_fraction(&f);
    print_fraction(&f);
    return EXIT_SUCCESS;
}
9 and
#define MAX 100
#define SEVEN_WORDS that_symbol_expands_to_all_these_words
3 are the two most common, but there are others.

9.1.1.3.
#include "fraction.h"  // include struct fraction definition and
                       // fraction utility function prototypes,
                       // as well as other headers like stdlib.h

int main() {
    struct fraction f = {2,3};
    invert_fraction(&f);
    print_fraction(&f);
    return EXIT_SUCCESS;
}

As we've already seen, the

#include "fraction.h"  // include struct fraction definition and
                       // fraction utility function prototypes,
                       // as well as other headers like stdlib.h

int main() {
    struct fraction f = {2,3};
    invert_fraction(&f);
    print_fraction(&f);
    return EXIT_SUCCESS;
}
9 directive reads in text from different files during the preprocessing step.
#include "fraction.h"  // include struct fraction definition and
                       // fraction utility function prototypes,
                       // as well as other headers like stdlib.h

int main() {
    struct fraction f = {2,3};
    invert_fraction(&f);
    print_fraction(&f);
    return EXIT_SUCCESS;
}
9 is a very unintelligent directive --- the action is simply to paste in the text from the given file. The file name given to
#include "fraction.h"  // include struct fraction definition and
                       // fraction utility function prototypes,
                       // as well as other headers like stdlib.h

int main() {
    struct fraction f = {2,3};
    invert_fraction(&f);
    print_fraction(&f);
    return EXIT_SUCCESS;
}
9 may be included in angle brackets or quotes. The difference is that system files should be enclosed in angle brackets and any user files should be enclosed in quotes.

9.1.1.4.
#define MAX 100
#define SEVEN_WORDS that_symbol_expands_to_all_these_words

The

#define MAX 100
#define SEVEN_WORDS that_symbol_expands_to_all_these_words
3 directive can be used to set up symbolic replacements in the source. As with all preprocessor operations,
#define MAX 100
#define SEVEN_WORDS that_symbol_expands_to_all_these_words
3 is extremely unintelligent --- it just does textual replacement without any code evaluation.
#define MAX 100
#define SEVEN_WORDS that_symbol_expands_to_all_these_words
3 statements are used as a crude way of establishing symbolic constants or macros. Generally speaking, you should prefer to use
#define MAX(a,b) (a > b ? a : b)
2 values over
#define MAX 100
#define SEVEN_WORDS that_symbol_expands_to_all_these_words
3 directives.

Here are examples of quasi-constant definitions:

#define MAX 100
#define SEVEN_WORDS that_symbol_expands_to_all_these_words

Later code can use the symbols

#define MAX(a,b) (a > b ? a : b)
4 or
#define MAX(a,b) (a > b ? a : b)
5 which will be replaced by the text to the right of each symbol in its
#define MAX 100
#define SEVEN_WORDS that_symbol_expands_to_all_these_words
3.

Simplistic macro functions can also be defined with

#define MAX 100
#define SEVEN_WORDS that_symbol_expands_to_all_these_words
3 directives. For example, a commonly used macro is
#define MAX(a,b) (a > b ? a : b)
4, which takes two parameters and can be used to determine the larger of two values:

#define MAX(a,b) (a > b ? a : b)

Again, the

#define MAX 100
#define SEVEN_WORDS that_symbol_expands_to_all_these_words
3 directive is incredibly unintelligent: it is simply smart enough to do textual replacement. For example, the following code:

int a = MAX(c, d);

would be replaced by the preprocessor with the following:

int a = (c > d ? c : d);

While

#define MAX(a,b) (a > b ? a : b)
4 is often referred to as a macro function (or simply macro), it does not operate as a function at all. The programmer can (somewhat) treat the macro as a function, but the effect is just an illusion created by the C preprocessor.

9.1.1.5. #if¶

At the preprocessing phase, the symbolic names (and values) defined by

#define MAX 100
#define SEVEN_WORDS that_symbol_expands_to_all_these_words
3 statements and predefined by the compiler can be tested and evaluated using
int a = MAX(c, d);
2 directives. The
int a = MAX(c, d);
2 test can be used at the preprocessing phase to determine whether code is included or excluded in what is passed on to the compilation phase. The following example depends on the value of the
int a = MAX(c, d);
4
#define MAX 100
#define SEVEN_WORDS that_symbol_expands_to_all_these_words
3 symbol. If it is true (i.e., non-zero), then the
int a = MAX(c, d);
6 lines (whatever they are) are compiled, and the
int a = MAX(c, d);
7 lines are ignored. If
int a = MAX(c, d);
4 is false (i.e., 0), then the reverse is true.

#define FOO 1

...

#if FOO
    aaa
    aaa
#else
    bbb
    bbb
#endif

Interestingly (and usefully), you can use

int a = MAX(c, d);
9 to effectively comment out areas of code you don't want to compile, but which you want to keep in the source file.

9.1.1.6. Multiple #includes¶

It is invalid in C to declare the same variable or

#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
3 twice. This can easily happen if a header file is
#include "fraction.h"  // include struct fraction definition and
                       // fraction utility function prototypes,
                       // as well as other headers like stdlib.h

int main() {
    struct fraction f = {2,3};
    invert_fraction(&f);
    print_fraction(&f);
    return EXIT_SUCCESS;
}
9d twice. For example, if a source code file includes header file A and B, and header file B also includes header file A, the contents of header file A will be included twice, which may cause problems.

A standard practice to avoid this problem is to use the

int a = (c > d ? c : d);
2 directive, which means "if the following symbol is not defined, do the following". The
#define MAX 100
#define SEVEN_WORDS that_symbol_expands_to_all_these_words
3 symbol is often based on the header file name (as in the following), and this practice

This largely solves multiple

#include "fraction.h"  // include struct fraction definition and
                       // fraction utility function prototypes,
                       // as well as other headers like stdlib.h

int main() {
    struct fraction f = {2,3};
    invert_fraction(&f);
    print_fraction(&f);
    return EXIT_SUCCESS;
}
9 problems.

#ifndef __FOO_H__
#define __FOO_H__  // we only get here if the symbol __FOO_H__ has not been previously defined

<rest of foo.h ...>

#endif // __FOO_H__

int a = (c > d ? c : d);
5 functions

There is yet another meaning to the keyword

int a = (c > d ? c : d);
5 in the context of global variables and functions. Specifically:

  1. A function may be declared

    int a = (c > d ? c : d);
    
    5, in which case it can only be used in the same file, below the point of its declaration. The meaning of
    int a = (c > d ? c : d);
    
    5 in this case is essentially that the function is "private" to the file. That is, it can only be used by other functions within the same file, but not from within another
    #include "fraction.h"
    
    void print_fraction(const struct fraction *f) {
        printf("Fraction: %d/%d\n", f->numerator, f->denominator);
    }
    
    void invert_fraction(struct fraction *f) {
        int tmp = f->numerator;
        f->numerator = f->denominator;
        f->denominator = tmp;
    }
    
    1 file.

  2. The

    int a = (c > d ? c : d);
    
    5 keyword can also be used with global variables in a
    #include "fraction.h"
    
    void print_fraction(const struct fraction *f) {
        printf("Fraction: %d/%d\n", f->numerator, f->denominator);
    }
    
    void invert_fraction(struct fraction *f) {
        int tmp = f->numerator;
        f->numerator = f->denominator;
        f->denominator = tmp;
    }
    
    1 file (i.e., variables defined outside any function). The meaning in this case is the same with
    int a = (c > d ? c : d);
    
    5 functions: the variable is "private" to the
    #include "fraction.h"
    
    void print_fraction(const struct fraction *f) {
        printf("Fraction: %d/%d\n", f->numerator, f->denominator);
    }
    
    void invert_fraction(struct fraction *f) {
        int tmp = f->numerator;
        f->numerator = f->denominator;
        f->denominator = tmp;
    }
    
    1 file and cannot be accessed or used from other
    #include "fraction.h"
    
    void print_fraction(const struct fraction *f) {
        printf("Fraction: %d/%d\n", f->numerator, f->denominator);
    }
    
    void invert_fraction(struct fraction *f) {
        int tmp = f->numerator;
        f->numerator = f->denominator;
        f->denominator = tmp;
    }
    
    1 files.

For example, here are definitions of a static (private) variable and static (private) function within a

#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
1 source file:

struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
0

9.1.1.7. Invoking the preprocessor¶

Normally, you do not need to do anything special to invoke the preprocessing phase when compiling a program. It is, however, possible to only invoke the preprocessing phase (i.e., no compilation or anything else), and also to define new preprocessor symbols on the command line.

To invoke just the preprocessor in clang, you can use the command clang -E sourcefile.c. clang has another command line option to just run the preprocessor and check code syntax: clang -fsyntax-only sourcefile.c.

To define new preprocessor symbols (i.e., just like

#define MAX 100
#define SEVEN_WORDS that_symbol_expands_to_all_these_words
3), the -D option can be used with clang, as in clang -DSYMBOL, or clang -DSYMBOL=VALUE.

9.1.2. The compilation step¶

The compilation step takes as input the result from the preprocessing stage. Thus, any

#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
0 directives have been processed and are removed in the source code seen by the compiler.

The compilation stage can produce either assembly code or an object file as output. Typically, the object code is all that is desired; it contains the binary machine code that is generated from compiling the C source. There are a few different relevant compiler options at this stage:

clang -S sourcefile.c

Produces assembly code in sourcefile.s

clang -c sourcefile.c

Produce object file (binary machine code) in sourcefile. This is the more common option to employ for the compilation stage. When all source files have been compiled to object code (

#define FOO 1

...

#if FOO
    aaa
    aaa
#else
    bbb
    bbb
#endif
8 files), all the
#define FOO 1

...

#if FOO
    aaa
    aaa
#else
    bbb
    bbb
#endif
8 files can be linked to produce a binary executable program.

Some additional compiler options that are useful at this stage:

option

meaning

-g

include information to facilitate debugging using a program like gdb.

-Wall

Warn about any potentially problematic constructs in the code.

9.1.3. The linking phase¶

The linking stage takes 1 or more object files and produces a binary executable program (i.e., a program that can be directly executed on the processor). It requires two things: that the implementations for any functions referenced in any part of the code have been defined, and that there is exactly one

#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
4 function defined.

9.1.3.1. Options for linking¶

In the simplest case, there is only one source file to preprocess, compile, and link. In that case, the same command line we've used with clang so far does the trick:

struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
1

or, if you've already compiled inputfile.c to inputfile.o, just:

struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
2

In a more "interesting" case, there is more than one file to compile and link together. For each source file, you must compile it to object code. Following that, you can link all the object files together to produce the executable:

struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
3

If you use functions from the standard C library, you don't need to do anything special to link in the code that implements the functions in that library. If, however, your program uses a function from an external library like the

#ifndef __FOO_H__
#define __FOO_H__  // we only get here if the symbol __FOO_H__ has not been previously defined

<rest of foo.h ...>

#endif // __FOO_H__
1 library (see man 3 math; it contains functions such as
#ifndef __FOO_H__
#define __FOO_H__  // we only get here if the symbol __FOO_H__ has not been previously defined

<rest of foo.h ...>

#endif // __FOO_H__
2,
#ifndef __FOO_H__
#define __FOO_H__  // we only get here if the symbol __FOO_H__ has not been previously defined

<rest of foo.h ...>

#endif // __FOO_H__
3,
#ifndef __FOO_H__
#define __FOO_H__  // we only get here if the symbol __FOO_H__ has not been previously defined

<rest of foo.h ...>

#endif // __FOO_H__
4,
#ifndef __FOO_H__
#define __FOO_H__  // we only get here if the symbol __FOO_H__ has not been previously defined

<rest of foo.h ...>

#endif // __FOO_H__
5, and
#ifndef __FOO_H__
#define __FOO_H__  // we only get here if the symbol __FOO_H__ has not been previously defined

<rest of foo.h ...>

#endif // __FOO_H__
6), the library to be linked with must be specified on the command line. The basic command is:

struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
4

The

#ifndef __FOO_H__
#define __FOO_H__  // we only get here if the symbol __FOO_H__ has not been previously defined

<rest of foo.h ...>

#endif // __FOO_H__
7 option indicates that some external library must be linked to the program, in this case the
#ifndef __FOO_H__
#define __FOO_H__  // we only get here if the symbol __FOO_H__ has not been previously defined

<rest of foo.h ...>

#endif // __FOO_H__
1 library.

9.1.3.2. The main function¶

The execution of a C program begins with the function named

#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
4. All of the files and libraries for the C program are compiled together to build a single program file. That file must contain exactly one
#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
4 function which the operating system uses as the starting point for the program.
#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
4 returns an int which, by convention, is 0 if the program completed successfully and non-zero if the program exited due to some error condition. This is just a convention which makes sense in shell oriented environments such as UNIX.

9.1.3.3. Command-line arguments to a program¶

For many C programs, it is useful to be able to pass various command-line arguments to the program through the shell. For example, if we had a program named

struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
02 and we wanted to give it the names of several text files for it to process, we might use the following command line:

struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
5

Each of the file names (file1-3.txt) is a command-line parameter to the program, and can be collected through two parameters to

#include "fraction.h"

void print_fraction(const struct fraction *f) {
    printf("Fraction: %d/%d\n", f->numerator, f->denominator);
}

void invert_fraction(struct fraction *f) {
    int tmp = f->numerator;
    f->numerator = f->denominator;
    f->denominator = tmp;
}
4 which are classically called
struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
04 and
struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
05 and are declared as follows:

struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
6

The meaning of these parameters is:

struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
04

The number of command-line arguments given to the program, including the program name

struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
05

An array of C strings which refer to each of the command-line parameters. Note that

struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
08 is always the name of the program itself. For example, in the above command line,
struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
08 would be
struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
10.

A simple program that traverses the array of command-line arguments and prints each one out could be written as follows:

struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
7

There is a C library function called

struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
11 that enables parsing of options in more flexible ways. See man 3 getopt for more information.

9.2. Invariant testing and struct fraction { int numerator; int denominator; }; void print_fraction(const struct fraction *); void invert_fraction(struct fraction *); 12¶

Array out of bounds references are an extremely common form of C run-time error. You can use the

struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
13 function to sprinkle your code with your own bounds checks. A few seconds putting in
struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
12 statements can save you hours of debugging.

Getting out all the bugs is the hardest and scariest part of writing a large piece of software. Adding

struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
12 statements are one of the easiest and most effective helpers for that difficult phase.

struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
8

Depending on the options specified at compile time, the

struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
13 expressions will be left in the code for testing, or may be ignored. For that reason, it is important to only put expressions in
struct fraction {
    int numerator;
    int denominator;
};

void print_fraction(const struct fraction *);
void invert_fraction(struct fraction *);
13 tests which do not need to be evaluated for the proper functioning of the program.

What are the 3 steps needed to process a file?

File Input/Ouput (IO) requires 3 steps:.
Open the file for read or write or both..
Read/Write data..
Close the file to free the resouces..

What are the three steps performed when working with any file in C?

File Input and Output in C 1) Create a variable to represent the file. 2) Open the file and store this "file" with the file variable. 3) Use the fprintf or fscanf functions to write/read from the file.

How are data files used in programs?

A data file is a computer file which stores data to be used by a computer application or system, including input and output data. A data file usually does not contain instructions or code to be executed (that is, a computer program). Most of the computer programs work with data files.

What is the process of retrieving data from a file?

Data extraction is the process of collecting or retrieving disparate types of data from a variety of sources, many of which may be poorly organized or completely unstructured.