syque.com

The Psychology of Quality and More

| Menu | Books | Share | Search | Settings |

C Style: Standards and Guidelines (contents)

CHAPTER 7 : File Layout

PART 3 : LAYOUT

CHAPTER 7 : File Layout
7.1 Layout of directories
7.2 Division of files
7.3 Considerations for File Layout
7.4 Header files
7.5 Layout of Data files
7.6 Layout of Code files
7.7 Summary

<--Prev page | Next page -->

 

7.4  Header files

Header files simplify code and data files by removing a possibly large number of declarations, etc. from the start of the file. In doing this, however, they make the removed items effectively invisible to the reader of the file. The programmer should counteract this by organizing the contents and layout of the header files so that the reader can easily find the missing items.

7.4.1  Division of header files

A header file should 'serve' a coherent group of code files. A small program might have just one header file. A larger program would have more header files, divided to reflect the division of these code files.

There are several possible uses for header files: a system interface file provides the external world with all it needs to access this system; a system common file contains common things used within this system; a system specific file contains items used only within a specific portion of this system.

System interface files may contain function prototypes, data interface to the system, including data structure declarations, error codes, etc. They act as the 'ambassador' for the subsystem, and this may be the only files that you write that other users ever read. Thus special care should be taken to ensure that these files are particularly complete, well structured and clearly commented.

There can be several levels of interface header files, depending on their effective scope for example to an external platform (eg. operating system, windowing system) or an internal subsystem (eg. keyboard interface).

7.4.2  Memory reservation

Header files can contain any C item, but commonly do not contain any data definitions (ie. those that reserve memory), as this could result in multiple instances of the same symbol being declared in different files, which would cause linker resolution problems. It also may be unclear who 'owns' the item. Similarly, executable code in header files is to be avoided (other than in macros). By insisting that header files are completely passive, causing no memory to be reserved for data or for code, readers and users can be confident that no definitions are 'invisible' to them.

7.4.3  Header file interdependency

It can make for confusing reading if header files interact or have some dependency on one another. Consider the case where one header file must be #include'd before another:

 

/* sysio.h */
#define MAX_BUF_SIZE   128

/* kbdio.h */
struct KBD_BUF
{
    char    KbdBuffer[MAX_BUF_SIZE];
    int     KbdStatus;
};

/* kbdctrl.c */
#include "kbdio.h"    /* WRONG: this file should be included after sysio.h */
#include "sysio.h"

-----------------------------------------------------------------------------

This makes it difficult for the reader of 'kbdio.h' to find where MAX_BUF_SIZE is declared. It is also an additional awkward piece of information to remember for anyone extending the system. Confusion caused by this can be avoided by nesting header files:

 

/* kbdio.h */
#include "sysio.h"
...
struct KBD_BUF
{
    char    KbdBuffer[MAX_BUF_SIZE];
    int     KbdStatus;
};

 

This, however, can cause problems of duplicate symbols (which are not allowed) if 'sysio.h' is included twice. This can be avoided by bracketing the contents of header files with an '#ifndef':

 

/* sysio.h */
#ifndef sysio_h
#define sysio_h
...
all statements in file
...
#endif

 

It is not always possible to eliminate interaction between header files without causing worse problems. Nevertheless, it will help to improve readability if interaction is minimized.

7.4.4  Header files and declarations

Where a data item is used by multiple functions, the best place for its description is in a single header file. This is then #include'd in the code file that defines and the file that uses that data. It also allows constants associated with the data to be defined within the same context.

 

/* kbddata.h */
typedef struct                        /* describe item in header file */
{
    char        KB_Buffer[KB_BUF_LEN];
    unsigned    KB_Status;
} KB_BUFFER;
/*
 *  values for KB_Status
 */
#define KB_FULL     0
#define KB_EMPTY    1

/* kbdread.c */
#include "kbdextn.h"
...
static KB_BUFFER KbdBuffer;           /* define it in 'owner' file      */
...
    ReadKbd( &KbdBuffer );

/* kbdio.c */
#include "kbddata.h"
...
int ReadKbd ( KB_BUFFER *KbdBuffer )  /* declare it in usage files     */

-------------------------------------------------------------------------------

7.4.5  Layout of header files

As with all files, the header file should start with a header comment, explaining its scope and contents, followed by a number of sections preceded by subheader comments. The layout of rest of the file will depend very much on its contents.

The layout could simply be by statement type, putting declarations earlier if they more likely be used elsewhere, thus: #include's, then #define's, enum's, struct's, typedef's and function declarations.

A better approach is to divide it by functional area so that all items pertaining to a single usage or group of usages are placed together (thus minimizing visual scope). A good test of this is that if the file becomes too big, the points at which it may be split are naturally available. Care must be taken when doing this that dependent items are in the correct order. This can be helped by sorting within these groupings by type, as above. Thus, a typical format might be:

 

   File heading comment

#ifndef filename_h

#define filename_h

   Include files

   Common context items:

       #define's, enum's

   Functional area 1:

       Heading comment

       #define's, enum's for context

       Data description 1:

                   Heading comment

                   enum's for context

                   typedef or struct to declare data item

                   #define's for constant values

       Data description 2:

          ...

       External function declarations

   Functional area 2:

...

#endif /* filename_h */

 

Note that having several functional areas in one header file is a part of the complexity balancing act between scope and file size. It may quite reasonable to have a header file containing only one functional area or even one data description, such as a message identification header file, which contains only #define's for external message numbers.

 

<--Prev page | Next page -->

 

Site Menu

| Home | Top | Settings |

Quality: | Quality Toolbook | Tools of the Trade | Improvement Encyclopedia | Quality Articles | Being Creative | Being Persuasive |

And: | C Style (Book) | Stories | Articles | Bookstore | My Photos | About | Contact |

Settings: | Computer layout | Mobile layout | Small font | Medium font | Large font | Translate |

 

You can buy books here

More Kindle books:

And the big
paperback book


Look inside

 

Please help and share:

 

| Home | Top | Menu |

© Changing Works 2002-
Massive Content -- Maximum Speed