Organization of C files

末鹿安然 提交于 2019-11-28 17:52:15
cschol

You should regard .h files as interface files of your .c file. Every .c file represents a module with a certain amount of functionality. If functions in a .c file are used by other modules (i.e. other .c files) put the function prototype in the .h interface file. By including the interface file in your original modules .c file and every other .c file you need the function in, you make this function available to other modules.

If you only need a function in a certain .c file (not in any other module), declare its scope static. This means it can only be called from within the c file it is defined in.

Same goes for variables that are used across multiple modules. They should go in the header file and there they have to marked with the keyword 'extern'. Note: For functions the keyword 'extern' is optional. Functions are always considered 'extern'.

The inclusion guards in header files help to not include the same header file multiple times.

For example:

Module1.c:

    #include "Module1.h"

    static void MyLocalFunction(void);
    static unsigned int MyLocalVariable;    
    unsigned int MyExternVariable;

    void MyExternFunction(void)
    {
        MyLocalVariable = 1u;       

        /* Do something */

        MyLocalFunction();
    }

    static void MyLocalFunction(void)
    {
      /* Do something */

      MyExternVariable = 2u;
    }

Module1.h:

    #ifndef __MODULE1.H
    #define __MODULE1.H

    extern unsigned int MyExternVariable;

    void MyExternFunction(void);      

    #endif

Module2.c

    #include "Module.1.h"

    static void MyLocalFunction(void);

    static void MyLocalFunction(void)
    {
      MyExternVariable = 1u;
      MyExternFunction();
    }

Try to make each .c focus on a particular area of functionality. Use the corresponding .h file to declare those functions.

Each .h file should have a 'header' guard around it's content. For example:

#ifndef ACCOUNTS_H
#define ACCOUNTS_H
....
#endif

That way you can include "accounts.h" as many times as you want, and the first time it's seen in a particular compilation unit will be the only one that actually pulls in its content.

Adam Davis

Compiler

You can see an example of a C 'module' at this topic - Note that there are two files - the header tea.h, and the code tea.c. You declare all the public defines, variables, and function prototypes that you want other programs to access in the header. In your main project you'll #include and that code can now access the functions and variables of the tea module that are mentioned in the header.

It gets a little more complex after that. If you're using Visual Studio and many other IDEs that manage your build for you, then ignore this part - they take care of compiling and linking objects.

Linker

When you compile two separate C files the compiler produces individual object files - so main.c becomes main.o, and tea.c becomes tea.o. The linker's job is to look at all the object files (your main.o and tea.o), and match up the references - so when you call a tea function in main, the linker modifies that call so it actually does call the right function in tea. The linker produces the executable file.

There is a great tutorial that goes into more depth on this subject, including scope and other issue you'll run into.

Good luck!

-Adam

A couple of simple rules to start:

  1. Put those declarations that you want to make "public" into the header file for the C implementation file you are creating.
  2. Only #include header files in the C file that are needed to implement the C file.
  3. include header files in a header file only if required for the declarations within that header file.

  4. Use the include guard method described by Andrew OR use #pragma once if the compiler supports it (which does the same thing -- sometimes more efficiently)
smh

To answer your additional question:

This question precipitated my inquiry. The tea.h file makes no reference to the tea.c file. Does the compiler "know" that every .h file has a corresponding .c file?

The compiler is not primarily concerned with header files. Each invocation of the compiler compiles a source (.c) file into an object (.o) file. Behind the scenes (i.e. in the make file or project file) a command line equivalent to this is being generated:

compiler --options tea.c

The source file #includes all the header files for the resources it references, which is how the compiler finds header files.

(I'm glossing over some details here. There is a lot to learn about building C projects.)

As well as the answers supplied above, one small advantage of splinting up your code into modules (separate files) is that if you have to have any global variables, you can limit their scope to a single module by the use of the key word 'static'. (You could also apply this to functions). Note that this use of 'static' is different from its use inside a function.

Your question makes it clear that you haven't really done much serious development. The usual case is that your code will generally be far too large to fit into one file. A good rule is that you should split the functionality into logical units (.c files) and each file should contain no more than what you can easily hold in your head at one time.

A given software product then generally includes the output from many different .c files. How this is normally done is that the compiler produces a number of object files (in unix systems ".o" files, VC generates .obj files). It is the purpose of the "linker" to compose these object files into the output (either a shared library or executable).

Generally your implementation (.c) files contain actual executable code, while the header files (.h) have the declarations of the public functions in those implementation files. You can quite easily have more header files than there are implementation files, and sometimes header files can contain inline code as well.

It is generally quite unusual for implementation files to include each other. A good practice is to ensure that each implementation file separates its concerns from the other files.

I would recommend you download and look at the source for the linux kernel. It is quite massive for a C program, but well organised into separate areas of functionality.

The .h files should be used to define the prototypes for your functions. This is necessary so you can include the prototypes that you need in your C-file without declaring every function that you need all in one file.

For instance, when you #include <stdio.h>, this provides the prototypes for printf and other IO functions. The symbols for these functions are normally loaded by the compiler by default. You can look at the system's .h files under /usr/include if you're interested in the normal idioms involved with these files.

If you're only writing trivial applications with not many functions, it's not really necessary to modularize everything out into logical groupings of procedures. However, if you have the need to develop a large system, then you'll need to pay some consideration as to where to define each of your functions.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!