oms-stand c programming rules

Upload: kleinfrans

Post on 10-Apr-2018

215 views

Category:

Documents


0 download

TRANSCRIPT

  • 8/8/2019 OMS-Stand C Programming Rules

    1/58

    O M S

    Standard

    C programming rules

    OMS report number 2001-02

  • 8/8/2019 OMS-Stand C Programming Rules

    2/58

  • 8/8/2019 OMS-Stand C Programming Rules

    3/58

    Version number: 1.2, November 20, 2002

    Administrator: OMS Help Desk

    Copyright: WL | Delft Hydraulics

    Ministry of Transport, Public Works and Water Management

    Standard

    C programming rules

  • 8/8/2019 OMS-Stand C Programming Rules

    4/58

    Programming Standard C

    4 of 58 1.2, November 20, 2002

  • 8/8/2019 OMS-Stand C Programming Rules

    5/58

    Preface

    1.2, November 20, 2002 5 of 58

    Preface

    In the Netherlands two advanced integrated modelling systems are operational for the prediction of

    flow and transport phenomena of water related problems. At the Dutch Rijkswaterstaat (RWS) the

    SIMONA system has been developed, whereas Delft3D is the integrated flow and transport modellingsystem of WL | Delft Hydraulics for the aquatic environment. Modules for hydrodynamics, water

    quality, ecology, waves and morphology are available to examine phenomena in coastal, river and

    estuarine areas. For example, these systems are currently being used in a system for determining the

    consequences of a calamitous discharge of pollutants, in a system that calculates the result of changes

    in the forelands of a river and in a system that operates the large storm surge barriers in the

    Netherlands.

    The functionality of both modelling systems is constantly improved and extended as a result of many

    consultancy and research projects and of the feedback from its users. Both SIMONA and Delft3D now

    exist for more than ten years. Both systems have become large and, more importantly, rather complex.

    This is due to the large variety of functionality within the modules and to the many cross-links

    between the modules in the two systems. It has been observed that both systems are lacking flexibilityto further improve the systems. Consequently, the costs of further development and maintenance of

    these modelling systems are rapidly increasing, and will soon grow beyond the capabilities of one

    organisation. This is the reason why Dutch Rijkswaterstaat and WL | Delft Hydraulics decided to co-

    operate in a public private partnership to develop a new modelling system, which is called the Open

    Modelling System (OMS).

    In August 2000 an agreement between the Dutch Rijkswaterstaat and WL | Delft Hydraulics was

    signed in order to migrate from their current modelling systems, being SIMONA and Delft3D,

    respectively, to one Dutch Open Modelling System (OMS). Top priorities in the migration phase are

    integrity and stability of intermediate releases and transparency of the migration to users of both

    systems. The aim of this integration is to be able to meet future demands of users with respect to

    functionality, flexibility, accessibility, modularity and performance.

    The new Open Modelling System should be open to new functionality and developments, which is not

    the case in the present systems SIMONA and Delft3D-FLOW. Moreover, the OMS system should

    allow connectivity to standard software packages, such as GIS, database software and software for pre-

    and post-processing and visualisation. The use of industrial standards for communication and data

    storage should guarantee a better access to commercially available software. Summarising, the OMS

    system will lead to a more flexible environment, both from developers' and from users' points of view.

    In February 2001 two projects have started to realise the OMS system, namely for:

    1. the first phase of the development of the OMS system

    2. defining the OMS maintenance and support organisation

    As part of the second project C programming rules are defined to guarantee the reliability and stability

    of the OMS modelling suit. The aim of these C Programming rules is to meet future demands of users

    with respect to functionality, flexibility, accessibility, modularity and performance.

  • 8/8/2019 OMS-Stand C Programming Rules

    6/58

  • 8/8/2019 OMS-Stand C Programming Rules

    7/58

    Contents

    1.2, November 20, 2002 7 of 58

    Contents

    1 DECLARATIONS ........................................................................................................... 11

    1.1 Variables .............................................................................................................................. 11

    1.2 Global (external) variables................................................................................................. 11

    1.3 Constants.............................................................................................................................. 12

    1.4 Type variables ..................................................................................................................... 12

    1.5 Macros .................................................................................................................................. 13

    1.6 Prototypes ............................................................................................................................ 13

    1.7 Pointer prefix....................................................................................................................... 14

    2 CONTROL STATEMENTS............................................................................................. 15

    2.1 General ................................................................................................................................. 15

    2.2 Loop constructs ................................................................................................................... 15

    2.2.1 For-loops ..................................................................................................................... 16

    2.2.2 While-loops................................................................................................................. 16

    2.2.3 Do-while loops............................................................................................................ 17

    2.2.4 Notes on the above ..................................................................................................... 17

    2.2.5 Examples..................................................................................................................... 17

    2.3 Selections via switch/case ................................................................................................... 18

    2.4 Conditional statements ....................................................................................................... 18

    2.5 Subroutines and functions.................................................................................................. 19

    2.6 Error handling..................................................................................................................... 20

    2.7 Using the C preprocessor ................................................................................................... 21

    3 USING I/O ...................................................................................................................... 23

    3.1 Opening and closing files.................................................................................................... 23

    3.2 Standard input and output................................................................................................. 23

    3.3 Reading and writing ........................................................................................................... 23

    4 EXPRESSIONS AND ASSIGNMENTS.......................................................................... 27

    4.1 Varargs................................................................................................................................. 27

    4.2 True and false ...................................................................................................................... 27

  • 8/8/2019 OMS-Stand C Programming Rules

    8/58

    Programming Standard C

    8 of 58 1.2, November 20, 2002

    4.3 Parentheses .......................................................................................................................... 27

    4.4 Implicit assignments ........................................................................................................... 27

    4.5 Conditional expressions...................................................................................................... 28

    4.6 Loop conditions ................................................................................................................... 28

    4.7 Token pasting....................................................................................................................... 29

    4.8 Debug statements ................................................................................................................ 29

    4.9 General ................................................................................................................................. 29

    4.10 Errors and functions........................................................................................................... 29

    4.11 Pointers and functions ........................................................................................................ 29

    4.12 Type casting ......................................................................................................................... 30

    4.13 NULL pointers..................................................................................................................... 30

    5 FILE ORGANISATION ................................................................................................... 31

    5.1 Source files ........................................................................................................................... 31

    5.2 Include files .......................................................................................................................... 32

    6 LAYOUT, INTERNAL DOCUMENTATION .................................................................... 35

    6.1 Naming conventions............................................................................................................ 35

    6.2 Language.............................................................................................................................. 35

    6.3 Order of program text parts .............................................................................................. 35

    6.4 Comment .............................................................................................................................. 36

    6.5 Alignment of declarations .................................................................................................. 37

    6.6 Statements............................................................................................................................ 37

    6.7 Assignments and expressions............................................................................................. 37

    6.8 Letters................................................................................................................................... 37

    6.9 Line length ........................................................................................................................... 38

    6.10 File length............................................................................................................................. 38

    6.11 Function length.................................................................................................................... 38

    6.12 Spaces ................................................................................................................................... 38

  • 8/8/2019 OMS-Stand C Programming Rules

    9/58

    Contents

    1.2, November 20, 2002 9 of 58

    6.13 Braces ................................................................................................................................... 38

    6.14 Indentation........................................................................................................................... 39

    6.15 Empty lines .......................................................................................................................... 39

    6.16 Fileheader............................................................................................................................. 39

    6.17 Function header................................................................................................................... 39

    6.18 Functions and parameters.................................................................................................. 40

    6.19 Variable/constant ................................................................................................................ 40

    6.20 Version information............................................................................................................ 41

    7 RECOMMENDATIONS .................................................................................................. 42

    7.1 Source and include files ...................................................................................................... 42

    7.2 Names of routines and functions ....................................................................................... 43

    8 PROHIBITED STATEMENTS AND CONSTRUCTIONS ............................................... 45

    8.1 Forbidden data types .......................................................................................................... 45

    8.2 Null statement...................................................................................................................... 45

    8.3 Forbidden standard functions ........................................................................................... 45

    8.4 Illegal use of pre-processor constructs.............................................................................. 45

    8.5 Illegal use of functions ........................................................................................................ 46

    9 REFERENCES............................................................................................................... 49

    APPENDIX A LOG SHEET................................................................................................51

    APPENDIX B SET/GET FUNCTIONS ............................................................................... 53

    APPENDIX C EXAMPLES ................................................................................................. 55C.1 Example C module (.c)........................................................................................................ 55

    C.2 Example C header file (.h).................................................................................................. 58

  • 8/8/2019 OMS-Stand C Programming Rules

    10/58

  • 8/8/2019 OMS-Stand C Programming Rules

    11/58

    Declarations

    1.2, November 20, 2002 11 of 58

    1 Declarations

    1.1 Variables

    Each variable must be declared on a separate line.

    Each variable must be explained by means of a comment string. This comment string will be placed

    directly after the declaration or definition and may be continued on one new line at the most.

    Local variables must be declared at the beginning of a function and also at the beginning of a block.

    All basic variables (int, float, char, long, short and double) are initialised at declaration. Dont assume

    the compiler will initialise variables at zero or blanks for you. Exceptions to this rule are static and

    external variables, according to ANSI C they will always be initialised at zero. Mind the usage of

    already defined functions.

    Examples:

    Compliant Not compliantlong lower = 0; /* Lower bound */long upper = 300; /* Upper bound */long step = 20; /* Step size */float eps = 1.0e-5; /* Smallest difference */char c = \\; /* Escape char */

    int lower, upper, step;float eps;char c;

    1.2 Global (external) variables

    Global variables are inevitable from time to time:

    It may be necessary to keep the state of the program in a globally available structure, because in

    most user-interfaces the callback functions that are typically used to handle window events do not

    allow an arbitrary argument list.

    It may be necessary to pass information on to higher-level routines, for instance error conditions

    and there is no way (because of third-party libraries) to handle this information flow via an

    argument that is passed by reference.

    When using such tools as Yacc and Lex, information is mainly transferred via global variables, as

    there is no other way.

    Therefore we need an etiquette for the use of global variables. This etiquette is given below:

    The use of global variables is forbidden (with or without the static attribute), unless they are contained

    in a structure. The definition of this structure is contained in an include file as a separate user-defined

    type (typedef).

    Any routine seeking to use this globally available structure, should obtain a pointer to this structure

    either via its argument list or via a function call:

  • 8/8/2019 OMS-Stand C Programming Rules

    12/58

    Programming Standard C

    12 of 58 1.2, November 20, 2002

    In the include file:/* Definition of the program state */typedef struct _ProgramState {

    } ProgramState, *ProgramStatePtr;

    /* Auxiliary function - obtain the program state */ProgramStatePtr *GetProgramState(void);

    In some routine:long routine( , ProgramStatePtr statep, ){

    /* Update the program state */

    }

    Or alternatively:long routine( ){

    ProgramStatePtr statep;

    statep = GetProgramState();

    /* Update the program state */

    }

    The global variables within this structure can best be updated via get/set routines, rather than direct

    assignments to the individual fields, as this makes the application less susceptible to changes in the

    structure.

    One advantage of using structures and pointers to these structures is that, should it become necessary

    to distinguish several such items, one can simply allocate a new structure of this type and fill it.

    1.3 ConstantsThe best way to define constants is to declare them with the const qualifier. This way you specifythat its value will not be changed. Using the const qualifier for an array it says that the elements will

    not be altered of when used for array arguments it indicates that the function does not change that

    array. An advantage is the compiler checking of the variables that have been declared with the constqualifier.

    Examples:const long number = 42; /* Crucial number */const double e = 2.71828182845905; /* Maths exp(1) */

    const char msg [ ] = warning : ; /* Prefix for messages */

    long KeyValue( const char [ ] );

    1.4 Type variables

    Types play an important role in C programming. C provides a facility called typedef for creatingnew data type names, to be used as more explanatory synonyms. Especially use typedef for multi-dimensional arrays, pointers to functions or pointers to user-defined structures.

    Caution:

    The scope of some basic C-types is platform dependent or even compiler dependent. For that reason

    porting an information system from one platform to another may cause adjustments to that information

  • 8/8/2019 OMS-Stand C Programming Rules

    13/58

    Declarations

    1.2, November 20, 2002 13 of 58

    system. This is hardly avoidable in those cases where software layers are very close to the ANSI

    library. Therefore the usage oftypedefs is depreciated for the lower software layers.

    Some examples type definitions:typedef int Length; /* type declaration */Length len; /* length of a word */Length maxlen; /* maximum length */

    typedef char * pString; /* type declaration */pString p; /* local string */pString lineptr[MAXLINES] /* string pointer array */

    1.5 Macros

    Use macros (#define) to define constants and only leave trivial constants (such as 0 and 1)unchanged in the code. Preferably use a module prefix for a macro from a dedicated module.

    Always use capitals when naming the macro and take care not to redefine a predefined macro like

    BUFSIZ (defined in stdio.h) or M_PI (often defined in math.h)

    Always use brackets in expressions in a macro. This will avoid an incorrect evaluation of the

    expression.

    Using a set of macro definitions where an enumerated type definition can be used is not allowed:

    Compliant Not compliantenum months {

    JAN = 1,FEB,DEC };

    #define JAN 1#define FEB 2#define DEC 12

    Note: Beware of side effects when using a macro, for instance when increments might be used in the

    parameters to the macro.

    The description of a #define constant or macro has to placed before the definition, for example

    (otherwise the comment might become part of the macros value):

    /* Code for closing an experiment-object */#define CLS_EXP_OBJ 100

    1.6 Prototypes

    When using an (external) function the existence of a declaration of the so-called function-prototype is

    required. It is an error if the definition of a function or any uses of it do not agree with its prototype.

    An important reason for function prototyping is the improving software maintenance and type

    checking by a standard-compliant compiler.

    Function prototypes have to be declared in a header (*.h) file. The usage of header files with function

    prototypes needed may only be achieved with an #include statement. To be sure the function

    prototypes and function definitions are the same the header file with function prototypes must also be

    included in the .c file with the function definitions.

  • 8/8/2019 OMS-Stand C Programming Rules

    14/58

    Programming Standard C

    14 of 58 1.2, November 20, 2002

    Use the const keyword for function parameters that will not change and the keyword void for afunction that returns no value. This will explain something about the parameter as well as the function

    and, moreover, the compiler can check for certain errors that violate these constraints.

    If a function has no parameters, the keyword void (instead of empty parentheses) has to be used.Leaving out

    voidmeans the list of parameters can be anything checking by the compiler is then

    impossible.

    Note: The rules mentioned above concern all functions.

    Example:void GiveMessage (

    long state,long message_type,char * text );

    1.7 Pointer prefix

    For naming of variables which will be used as pointers a type prefix p is required. The prefix is the

    first character must be placed before the variable name.

    Examples:double * pWlevel; /* waterlevel */long * pMmax; /* size M-direction */char ** pstrings; /* Array of strings */

  • 8/8/2019 OMS-Stand C Programming Rules

    15/58

    Control statements

    1.2, November 20, 2002 15 of 58

    2 Control statements

    2.1 General

    Each language has its so-called idiom (cf. Kernighan and Pike, 1999). For C (and C-like languages,

    such as C++ and Java) this includes:

    The way for-loops are normally set up

    The use of certain functions, especially the functions from the standard library

    The use of return values, such as 0 and 1

    Knowing the idiom and using it is crucial: if you do not follow that style of programming, the readers

    of your programs can very easily get confused and make mistakes that could have been avoided.

    For instance, a function that is able to do its job will normally return zero to indicate this. So, using a

    function that returns 1 instead to indicate everything was fine, goes against the grain. Similarly, it is

    customary in C to use counters and indices that start at zero, rather than at 1. Use this and similarconventions at all times. This chapter helps identifying the most common idiom and specifies several

    additional constructions and keywords. All are meant to help creating robust and maintainable

    programs.

    A corollary to the above statements is: Use the standard functions whenever possible. This applies in

    particular to string manipulation functions (such as strcmp()) and character type functions (such as

    isalpha()). Besides the ones defined in the ISO C standard, we also define the following as part of the

    OMS portability library:

    mkfilename() to construct the full path (directory and name) for a file from its parts

    strdup() to duplicate a string

    Several general restrictions apply:

    Logical expressions must be kept simple

    If the expression requires the evaluation of more than two sub-expressions (so at least two logical

    operators), it must be split into parts (for instance, by assigning partial results to variables with a

    meaningful name).

    None of the expressions may have side effects

    The use of side effects, especially in compound logical expressions, can lead to difficult subtleties,

    because C programs will evaluate such expressions in a lazy manner. Put simply: never use side

    effects (increment operator etc.), unless explicitly allowed in special constructs by this standard.

    Keep the nesting depth within bounds

    If you find that you need more than, say, four levels of nesting, you are probably better off putting

    the inner constructions in a separate function or subroutine.

    2.2 Loop constructs

    C has three different loop constructs:

    for-loops to loop over a predefinedset of values or items

    while-loops to loop until a certain condition is no longer valid

    do-while loops to loop at least once

  • 8/8/2019 OMS-Stand C Programming Rules

    16/58

    Programming Standard C

    16 of 58 1.2, November 20, 2002

    2.2.1 For-loops

    The for-loops have two common forms:

    Looping over a set of integers:for ( i = 0; i < n; i ++ ){

    }

    Crucial aspects are:

    The index starts at zero

    The index is incremented by 1 in the third part of the for-statement, using an increment operator

    The condition is that the index must be lower than a constant value

    Neither the index nor the upper bound are changed within the body of the loop

    You may get a variation to this type of loop by iterating backwards:for ( i = n-1; i >= 0; i -- ){

    }

    In all cases: observe the standard expressions

    Looping over a set of items (a linked list for instance):for ( elem = get_first( list );

    elem != NULL;elem = get_next( list, elem ) )

    {

    }

    Crucial aspects are:

    The initialisation part picks up the firstelement and the iteration part picks up the next one.

    The loop is continued as long as there are elements in the set that have not been visited yet. The set is notchanged during the iteration, because this could lead to very awkward behaviour

    (e.g. if you append a copy of the current element, the iteration would never end!)

    2.2.2 While-loops

    The while-loop has three acceptable forms, whereas the do-while loop has only one:while ( some_condition ){

    }

    with two special cases:

    Reading a file and the equivalent to the infinite for-loop):while ( fgets( string, sizeof(string), infile ) != NULL ){

    }

    The reason to allow this construction is that it is accepted idiom (cf. Kernighan and Pike) and that

    alternatives would require more logic or a slightly awkward sequencing (reading the line at the end of

    the loop for instance).

    The equivalent to the infinite for-loop:

    while ( 1 ){

    }

  • 8/8/2019 OMS-Stand C Programming Rules

    17/58

  • 8/8/2019 OMS-Stand C Programming Rules

    18/58

    Programming Standard C

    18 of 58 1.2, November 20, 2002

    2.3 Selections via switch/case

    Switch statements should be used instead of if-else-if constructions if the condition involves the

    values of a single integer or character. They are more efficient and, above all, much more readable

    than long if-blocks.

    We make the following notes:

    Each case must be terminated by a breakstatement or by a comment that a fall-through is

    required, so that the lack of the breakstatement is clear.

    Do not try to be clever, a fall-through works best if the first case has only a few statements or,

    preferably, none at all that are specific to the first case. The second case should have no code that

    is exclusive for that case.

    All switch statements must have a defaultcase. If this is not supposed to appear in practice, it

    must contain a body with proper error handling to indicate that fact.

    Note the indentation: the switch, case, and defaultkeywords should be vertically aligned.

    Use symbolic names as provided by the enum construct, rather than hardwired numbers.

    Example:

    Compliant Not compliantenum {

    CASE0,CASE1,CASE2,CASE3

    };

    switch ( var ){case CASE0: /* Fall through */case CASE1:

    printf( Typical cases\n );break;

    case CASE2:pstr = Case 2;printf( %s\n, pstr );break;

    case CASE3:pstr = Case 3;printf( %s\n, pstr );break;

    default :fprintf( stderr,

    Impossible case! );break;

    }

    switch ( var ){case 0:case 1:

    printf( Typical cases\n );break;

    case 2:pstr = Case 2;

    case 3:

    if ( var == 3 )pstr = Case 3;

    printf( %s\n, pstr );break;

    }

    2.4 Conditional statements

    General conditions can be applied in if-statements. There are a small number of restrictions, though:

    Do not make the if-blocks too long

    Long if-blocks with several else-if statements will quickly become unmanageable. Limit them to at

    most 30 lines (so half a page on paper).

    If-blocks with at least one else-if should always have an explicit else branch

    Because the if-block is compound, one must catch the possibility that none of the conditions

    holds. If there is no reason to assume this ever to be the case, then treat it just as the impossibledefault case in a switch-statement.

  • 8/8/2019 OMS-Stand C Programming Rules

    19/58

    Control statements

    1.2, November 20, 2002 19 of 58

    For example:if ( strcmp( string1, string2 ) == 0 ){

    /* First case */}else if ( strcmp( string1, string3 ) >= 0 ){

    /* Second case */

    }else{

    /* Default processing required! */}

    2.5 Subroutines and functions

    All rules concerning the use of subroutines and functions could be summarised as:

    Be as explicit as possible in defining the interface

    More specifically: Use the keyword const to indicate that arguments will not change. This enables the compiler to

    check illegal or unexpected changes.

    Do not locally change arguments that have been passed by value, even though the changes will

    have no effect in the calling routine. It is bad programming.

    Consider the difference between *arg and arg[]. The first can be used as a synonym for passing

    by reference or for passing an array. The second explicitly states that an array is expected.

    All subroutines and functions must have a proper prototype.

    Use the ISO style for defining the arguments, not the older K&R style:

    Compliant Not compliant

    longsample(

    long * a, /* Parameter a */long b, /* Parameter b */long c ) /* Parameter c */

    {

    }

    int sample( a, b, c )int *a;int b, c;{

    }

    Use an explicit return statement if the function is supposed to return a value. Do not use an

    explicit return if it does not:

  • 8/8/2019 OMS-Stand C Programming Rules

    20/58

    Programming Standard C

    20 of 58 1.2, November 20, 2002

    Compliant Not compliantlongsample(

    long a[], /* Array a */const long b, /* Param. B */const long c ) /* Param. C */

    { long retval = 0; /* Return value:0: OK;

  • 8/8/2019 OMS-Stand C Programming Rules

    21/58

    Control statements

    1.2, November 20, 2002 21 of 58

    Use the special macros __FILE__ and __LINE__ to communicate where impossible conditions

    occurred

    Via the referred macros it is possible to automatically include the position in the source code

    where an impossible condition occurred. This does not carry the same information as a full stack

    trace, but it saves searching for the right place:default : /* Should never occur */

    fprintf( stderr, Line %s in %s: Impossible case!, __LINE__, __FILE__ );

    Even better (the special macros are caught in a standard macro):#define INVALID_STATE \

    fprintf( stderr, Line %s in %s: Impossible case!, __LINE__, __FILE__ );

    default : /* Should never occur */INVALID_STATE;break;

    2.7 Using the C preprocessor

    The C preprocessor allows, in principle, many constructs that add power to the C programming

    language. However, caution is required, as the conditional statements #if, #ifdef etc. essentially create

    multiple versions of the source code in one single file. This means that the use of pre-processor

    constructs should be restricted:

    Macros to define constant values are allowed

    Statements like:#define BUFFER_LENGTH 255

    are allowed, but consider the following alternatives:

    Using the enum construction to define numerical constants by name (strictly speaking: enumerated

    values are not integers)

    Using constvariables

    The advantage of these alternatives is that the compiler can produce better diagnostics if they are

    used wrongly.

    Macros to define functions are not allowed

    Macro functions like:#define veclength(a,b) sqrt((a)*(a)+(b)*(b))

    are not allowed, because there is virtually no advantage for these functions to ordinary functions,

    and the compiler can not always check that the construction is correct. Debugging them is also

    much more difficult.

    Creative use of the preprocessor, for example to avoid repetitive code, is allowed, provided this

    type of use is well documented.

    Practical examples of such use are:

    Interface definitions that have to take of platform-dependencies (the interfacing between Fortran

    and C for instance)

    Set and get functions for the individual fields of large structures (see Appendix A):#define set( name, value)

    rather than a large number of functions like:1

    void name_set( value ) { }

    1 This is to some extent a matter of taste. In OO environments the emphasis is very much on such small

    individual set/get routines, rather than a single pair that selects a field.

  • 8/8/2019 OMS-Stand C Programming Rules

    22/58

    Programming Standard C

    22 of 58 1.2, November 20, 2002

    Conditional preprocessor statements should not be used, unless to capture platform dependencies

    Such statements may certainly not be used to:

    Hide currently unused (old?) code fragments/* Old code - should be removed one of these days */#if 0x = y*y;#endif

    Select or deselect debugging statements/* Debugging code - needs some reworking though */#if DEBUGONfprintf( stderr, x = %f\n, x );#endif

    The reason is that such code would not get compiled and would not evolve with the rest. Hence

    after a cycle or two of maintenance the code is outdated and it probably will not compile correctly.

    A better solution in the context of OMS is to use an if-statement with a more or less trivial

    condition:2

    if ( debug_on ){ /* Debugging */}

    2 An alternative that does use a macro, is presented in Chapter 7.

  • 8/8/2019 OMS-Stand C Programming Rules

    23/58

    Using I/O

    1.2, November 20, 2002 23 of 58

    3 Using I/O

    The use of external files in C has very similar problems as in Fortran or Java: the files may or may not

    exist, reading may or may not cause run-time errors and so on. Yet the details of these problems differs

    from the other programming languages and this chapter means to help avoid them by imposing a

    number of rules.

    3.1 Opening and closing files

    The following rules apply:

    Check that fopen() does not return NULL

    If it does, the file could not be opened. Take proper action.

    Close the file in the same routine

    Files opened in one routine should also be closed in that routine. If not, the file pointer must be

    handed to the calling routine. Otherwise the file pointer will be lost and the file can never be

    closed properly. Never use fixed file names

    Put the names of the files in variables (not macros). These can be changed easily as they will be

    found in one standard section of the source files.

    Compose directories and file names via a special routine

    Rely on the OMS standard routine mkfilename() to take care of the platform-dependent issues

    regarding the separator character and such.

    Use string variables of length PATH_MAX to store the filenames

    The macro PATH_MAX is commonly defined to the maximum allowable size of a file name

    including the directory etc. Using this length guarantees that the name will always fit.

    3.2 Standard input and output

    The following rules apply:

    Reopen stdout, stderr if necessary

    Under MS Windows, graphical user-interfaces have no access to a useful console. Hence, use the

    freopen() function to redirect output to some file.

    Use stdin only in non-GUI programs

    If you need user interaction, consider using a graphical user-interface. Never combine stdin and a

    graphical user-interface - this is very problematic under MS Windows.

    Use fgets() in stead of gets()

    If you read from stdin, always use fgets(), because with this function you have control over the

    length of the string to be read.

    3.3 Reading and writing

    The following rules apply:

    Read a file line by line using fgets()

    If you use fscanf(), you face an almost insoluble problem with line ends: the file position will stay

    on the previous line (if that is long enough), without the program being able to detect this. With

    fgets(), you have the benefit of being able to check that the end-of-line has been reached or not.

    Then use sscanf() to split it in the desired pieces.

    Check end-of-file with the feof() macroNever use the EOF return value, because that is not reliable.

  • 8/8/2019 OMS-Stand C Programming Rules

    24/58

    Programming Standard C

    24 of 58 1.2, November 20, 2002

    Make sure the buffers used in sprintf() are large enough

    As C does not provide any run-time checks on strings, you are yourself responsible for keeping

    within the bounds of a buffer. This can be facilitated by avoiding open-ended formats like %f

    and %s (see below).

    Use input format like %d and %f

    To give the user the freedom he or she wants, use as unspecific a format as possible. The sscanf()routine will make sure that it gets parsed correctly.

    Never use an output format like %f. Instead, %#13.6g and %#22.15g (or %12.6g, %21.15g if you

    do not care for exact length) are preferred for float and double reals.

    The problem with formats like %f is that large numbers are written with a lot of digits (see the

    example). The alternative format uses the hash sign (#) to make sure that the decimal point is

    always present and that trailing zeroes are maintained.

    Keep formats simple

    The printf() family of routines defines a huge set of formats but not every programmer is familiar

    with the possibilities. Hence stick to the mainstream, because otherwise others get confused (this

    is especially true with formats like %*s). If you need to use the more sophisticated formats,

    document their use via appropriate comments.

    Consider using automated tools like Yacc and Lex for reading files

    Tools like Yacc and Lex can produce robust reading routines. Their use is especially

    recommended if the input consists of more than simple numbers.

    The example below shows what happens if you go against some of these rules:

    The input file is:A line of text1.0 # One real value1 # One integer value

    The nave implementation is:#include

    #include

    int main( int argc, char * argv[] ){

    FILE * infile ;long ivalue ;float rvalue ;char string[100];

    infile = fopen( "example.inp", "r" );

    if ( infile != NULL ){

    fscanf( infile, "%s", string ); /* %s reads a single word */fscanf( infile, "%f", &rvalue ); /* %f reads the next word! */fscanf( infile, "%d", &ivalue ); /* %d reads the third word */

    printf( "String: %s\n", string );printf( "Real: %f\n", rvalue );printf( "Integer: %d\n", ivalue );

    rvalue = 1.0e20;printf( "Big real: %f\n", rvalue ); /* %f uses as many positions as necessary*/fclose(infile);

    }

    return 0;}

    which, on a SUN solaris machine results in the following output:String: A

    Real: 0.000000Integer: -268440104Big real: 100000002004087734272.000000

  • 8/8/2019 OMS-Stand C Programming Rules

    25/58

    Using I/O

    1.2, November 20, 2002 25 of 58

    The explanation for this is simple:

    The format %s reads a string until a white space character is encountered, hence only A is

    read. As the file position is not moved to the next line but left at the word line. This is then read

    as a real value, which fails. The value for the integer variable may be the result of an implicit

    conversion from characters to integers, as these two data types are closely related in C (for

    historical reasons mostly, never use this fact implicitly!) The large real is written in decimal form, giving a very long string. This can not be controlled via

    %12.3f or comparable formats.

    The correct implementation, with the above rules, is:#include #include

    int main( int argc, char *argv[] ){

    FILE * infile ;long ivalue ;float rvalue ;char buffer[100];char string[100];

    infile = fopen( "example.inp", "r" );

    if ( infile != NULL ){

    fgets( buffer, sizeof(buffer), infile );sscanf( buffer, "%s", string );

    fgets( buffer, sizeof(buffer), infile );sscanf( buffer, "%f", &rvalue );

    fgets( buffer, sizeof(buffer), infile );sscanf( buffer, "%d", &ivalue );

    printf( "String: %s\n", string );printf( "Real: %12.6g\n", rvalue );printf( "Integer: %d\n", ivalue );rvalue = 1.e20;

    printf( "Big real: %12.6g\n", rvalue );fclose(infile);}return 0;

    }

    which, again on a SUN workstation, results in the more expected output:String: AReal: 1Integer: 1Big real: 1e+20

  • 8/8/2019 OMS-Stand C Programming Rules

    26/58

  • 8/8/2019 OMS-Stand C Programming Rules

    27/58

    Expressions and assignments

    1.2, November 20, 2002 27 of 58

    4 Expressions and assignments

    In this chapter some requirements concerning C programming can be found in relation to expressions

    and assignments. By eliminating complex constructions and constructions which are error prone the

    chances to make errors will be reduced. Also some constructions in which the behaviour of C

    programs are undefined are excluded. Uniform C code is more understandable and easier to maintain.

    As a consequence porting the programs to other platforms becomes easier as well.

    4.1 Varargs

    Modules with a varying number of parameters must be avoided as much as possible. Only if there is

    no other solution, are they allowed and then the use ofvarargs is required.

    4.2 True and false

    Normally the macros FALSE and TRUE are defined in a some sort of standard header file. If they are

    not yet defined in some header, file define them like this (no other similar definitions may be used):#if ! defined(FALSE)#define FALSE (0)#endif#if ! defined(TRUE)#define TRUE (1)#endif

    The constants TRUE and FALSE may only be used in assignments or in return value. The usage as

    relational operand is not allowed.

    Compliant Not compliantreturn FALSE; return 0; /* 0 means logical FALSE */

    if ( !islower(c) ){

    }

    if ( islower(c) == FALSE ){

    }

    Note: if possible, avoid negations, especially in compound logical expressions.

    4.3 Parentheses

    For readability reasons usage of parentheses in expressions is strongly recommended. On the other

    hand using too many brackets can undo the readability. So handling with due care is needed.

    4.4 Implicit assignments

    Logical expressions with and and or may not contain implicit assignments. Due to short-circuiting

    the evaluation, it may not be clear that the assignment will indeed take place.

  • 8/8/2019 OMS-Stand C Programming Rules

    28/58

    Programming Standard C

    28 of 58 1.2, November 20, 2002

    Compliant Not compliantif ( c[i] == &&

    c[i+1] != ){

    i ++;

    }

    if ( c[i] == &&c[i++] != )

    {

    }

    In this example the index i may not be increased if c[i] turns out not to be a space, as the second part

    of the logical expression will not be evaluated!

    4.5 Conditional expressions

    Using conditional expressions is strongly depreciated. It is far better to use normal if-then-else

    constructions.

    Compliant Not compliantif ( a > b ){

    z = a}else{

    z = b}

    z = ( a > b ) ? a : b;

    The conditional expression may seem very compact, but as soon as the expressions become more

    complicated, it is very difficult to see what is going on.

    The following are examples of valid uses:#define MAX(a,b) ( (a) > (b) )? (a) : (b) )fprintf( stderr, Value is %s\n, ((debug_on != 0)? true : false) );

    The reason that these examples are allowed is that they make it possible to abbreviate constructs

    without obscuring the actual intent. In the first example the use of a macro allows one to hide the

    complicated structure of the conditional expression. In the second example there is no need for an

    extra if-statement and a variable to hold the strings true and false.3

    4.6 Loop conditions

    Do not misuse the expressions in a loop condition to make redundant the body of the loop. This canvery easily result in unreadable code.

    Compliant Not compliantfor ( p = string;

    *p != \0;p++ )

    {*p = toupper( *p);

    }

    for ( p = string;(*p) != \0;(*p++) = toupper(*p) );

    3 Actually, even in these cases other solutions may be preferable. See Chapter 7.

  • 8/8/2019 OMS-Stand C Programming Rules

    29/58

    Expressions and assignments

    1.2, November 20, 2002 29 of 58

    4.7 Token pasting

    Applying the so-called token pasting technique (##) when using macros is not allowed.

    Compliant Not compliant

    (none)

    #define cat(x,y) x # # y

    4.8 Debug statements

    Pieces of code that only become active when debugging are not allowed in production programs. An

    example is code that gives extra debugging information when it is activated by setting specific macros

    during the compilation. If it is necessary to be able to provide more detailed information about the

    operation of the program, then use standardised tools (output logging and levels of reporting). This

    type of output is more useful for the user, whereas debug output is solely useful for the programmer.

    4.9 General

    To avoid common errors and uncertainties, some general requirements about statements and

    expressions are formulated:

    Do not use the increment (++) and decrement (--) operators in one single expression;

    Usage of the comma-operator is forbidden;

    On one line only one single assignment is allowed. Both operators ++ and - - also are

    assignments;

    An actual argument may not contain an assignment. Actual arguments are arguments of the

    statements if, while, switch, return, do and for. Only one exception is allowed:the while expression may contain one single assignment.

    For example:while ( (c = fgetc(infile)) != \n )

    Using identical names for different variables in different blocks in the same function is not

    allowed.

    4.10 Errors and functions

    Possible errors occurring in a function must be returned by means of a function parameter or a returnvalue. The possible values of this function parameter or return value indicate the error that occurred.

    4.11 Pointers and functions

    Functions are never allowed to return a pointer to a local non-static variable, it is a severe

    programming error.

  • 8/8/2019 OMS-Stand C Programming Rules

    30/58

    Programming Standard C

    30 of 58 1.2, November 20, 2002

    Compliant Not compliantchar * IO_GetNewName (

    char * nameBuffer,long sizeBuffer ) /

    {return nameBuffer;

    }

    Or, alternatively:char * IO_GetNewName ( void ){

    static char nameBuffer[80];return nameBuffer;

    }

    char * IO_GetNewName ( void ){char nameBuffer[80];

    ...return nameBuffer;

    }

    Pointers to local non-static variables are valid only as long as the routine in which the variables are

    defined is active. Upon return the memory is reclaimed and such pointers become invalid.

    4.12 Type casting

    When data types do not correspond, explicit type casting is required. This creates clarity and therefore

    increases the maintainability of programs.

    Compliant Not compliantlong level;double dbl ;

    dbl = (double) (level * 4);dbl = (double) level * 4.0

    int level;double dbl ;

    dbl = level * 4;dbl = level * 4.0

    Note that real constants in C get the type double, unless they are suffixed with f. Integer constants

    have the type int, unless they are suffixed with L:

    Constant Type

    44L4.04.0f

    intlongdoublefloat

    4.13 NULL pointersUse the special symbol NULL to check if pointers are null pointers. Do not use 0, because NULL

    immediately indicates that a pointer is meant. Also do not use pointers as if they were logical

    variables. Comparison to NULL must be explicit.

    Pointers are often compared with NULL to determine the end of a data structure or test for validity. In

    this case the comparison has to be explicit!

    Compliant Not compliantwhile ( ptr != (PntrType) NULL ){

    ...}

    while ( ptr ){

    ...}

  • 8/8/2019 OMS-Stand C Programming Rules

    31/58

    File organisation

    1.2, November 20, 2002 31 of 58

    5 File organisation

    Source files and include files must be organised in a standard manner, to increase readability and

    maintainability of the source and include (header) files.

    5.1 Source files

    The organisation for source files that has been chosen for OMS has proven its usefulness over the

    years and is quite common:

    The first lines contain a block of comments that describe the contents of the file and the context of

    its routines. For instance: these routines manipulate the dictionary of keywords as found in the

    OMS input files.

    Then come the include files:

    Standard include files, such as stdio.h, get triangular brackets.4

    They are listed first.

    Program-specific include files get quotation marks.

    Macros are defined next, as these are often needed in the specification of data types and functionprototypes. This is restricted to the macros that are private to the source file. More general

    macros and data types must be put in an appropriate header file. Do not put comment after the

    macro, it might be regarded as part of the macros value.

    The next section is the definition of specific data types. This is of course restricted to the types that

    are private to the source file.

    Then come the global (both static and non-static) variables (sorted in alphabetic-lexicographical

    order).

    Then come the prototypes for static functions and routines in this file. Prototypes are always

    required for such functions and routines, even if due to their position in the file it is not strictly

    necessary.

    The last section contains the functions and routines themselves. Routines and functions that arenot intended for use outside the source file, must be declared static. All others must have visible

    prototypes in appropriate header files.

    Example (illustrating the division in sections):/* dictionary.c - Dictionary routines

    (description of the content)

    */

    #include #include

    #include dictionary.h

    /* Macros:INITIAL_SIZE - initial size for a dictionaryINCREMENT - amount by which to increment the array

    */#define INITIAL_SIZE 20#define INCREMENT 10

    /* Data types:Cursor, CursorPtr - used to keep track of search

    */typedef struct _Cursor{

    ...} Cursor, * CursorPtr;

    4 There is a subtle difference between these two types of include statements. The triangular brackets are

    reserved for files in the standard include directories.

  • 8/8/2019 OMS-Stand C Programming Rules

    32/58

    Programming Standard C

    32 of 58 1.2, November 20, 2002

    /* Static functions*/static int_DictIndex(

    DictionaryPtr dict,char * keyword );

    /* Global variables:cursor - Cursor into the given dictionary

    */static Cursor cursor;

    /* Start of actual routines and functions */

    static long_DictIndex(

    DictionaryPtr dict,char * keyword )

    {... /* Body of routine */

    }

    char *DictGetValue(

    DictionaryPtr dict,char * keyword )

    { ... /* Body of routine */}

    5.2 Include files

    For include files the set-up is almost the same:

    The first lines contain a block of comments that describe the contents of the file and the context of

    its routines. For instance: these routines manipulate the dictionary of keywords as found in the

    OMS input files.

    Then come the include files.

    Macros are defined next. The definitions of specific data types follow.

    Then come the global (both static and non-static) variables.

    Finally the prototypes for public functions are given.

    The last section contains the routines themselves.

    To prevent problems with multiple inclusions the whole content is enclosed by an #ifndef

    construction:#ifndef FILENAME_H_INCLUDED#define FILENAME_H_INCLUDED

    /* Body of include file */

    #endif /* FILENAME_H_INCLUDED */

    Note:

    The macro used to indicate this inclusion always has the above form (replace FILENAME by the

    actual name in capitals).

    Here is an example:/* dictionary.h - Header file for dictionary routines

    (description of the content)

    */

    #ifndef DICTIONARY_H_INCLUDED#define DICTIONARY_H_INCLUDED

  • 8/8/2019 OMS-Stand C Programming Rules

    33/58

    File organisation

    1.2, November 20, 2002 33 of 58

    #include #include

    /* Macros:NOT_FOUND - return value to indicate the keyword was not found

    */#define NOT_FOUND (char *)NULL

    /* Data types:Dictionary, DictionaryPtr - the structure to hold a dictionary

    */typedef struct _Dictionary{

    ...} Dictionary, * DictionaryPtr;

    /* Global variables:None

    */

    /* Public functions*/char *DictGetValue(

    DictionaryPtr dict,char * keyword );

    #endif /* DICTIONARY_H_INCLUDED */

    The following additional rules must be adhered to:

    All routines and functions must have an explicit type. Thus the following declaration is forbidden:/* Type int is implicit */static _DictIndex( DictionaryPtr dict, char *keyword );

    The include files must be arranged such that all routines and functions have an explicit prototype

    before they are first used or defined.

    If a routine or function is supposed to be available outside the source file, then its prototype must

    appear in an include file.

    If a routine or function is notsupposed to be available outside the source file, then it must be

    declared static and its prototype must appear in the source file.

    All prototypes for routines and functions in a particular source file must appear in the same

    include file.

    There can be more than one routine or function in a source file, but one should keep the total

    number of lines within limits and the routines and functions must be related, for instance, all

    routines and function that manipulate a particular data type.

    See the recommendations for further remarks about names for routines and functions and the names of

    source and include files.

  • 8/8/2019 OMS-Stand C Programming Rules

    34/58

  • 8/8/2019 OMS-Stand C Programming Rules

    35/58

    Layout, internal documentation

    1.2, November 20, 2002 35 of 58

    6 Layout, internal documentation

    In this standard documentation means the complete information to be obtained from the user

    documentation, source files and the system documentation. The user documentation contains all

    information important to the user. The source files and the system documentation contain all

    additional information important to the maintenance programmer. The emphasis is put on the

    documentation (comments) in the source files - the system documentation will provide additional

    information only. Good documentation of the source code is important for program maintenance. A

    quick and correct understanding of the program text leads to quick and correct improvements and

    additions.

    This chapter is limited to the layout of and the documentation within the program text.

    6.1 Naming conventions

    A good understanding of a program text is important. Therefore clear naming conventions for theidentifiers are required:

    constants

    variables

    types

    macros

    functions

    The following general conventions apply:

    The names have to be consistent, in other words: for the same meaning the same name has to be

    used. (For the use of acronyms see chapter 7 Recommendations.)

    The names of identifiers can be up to 31 significant characters. So use descriptive names!

    6.2 Language

    All comments and other documentation have to be written in English without any exceptions.

    6.3 Order of program text parts

    For some parts of the program text the use of ordering rules is obligatory. Conforming strictly to thisordering prevents many possible errors and makes it easier to find definitions.

    The strict order of program text parts of a header file (.h) is:

    file header comment block with:

    file name

    programmer name

    version number, date and description

    copyright

    file description

    includes

    standard files project files

  • 8/8/2019 OMS-Stand C Programming Rules

    36/58

    Programming Standard C

    36 of 58 1.2, November 20, 2002

    defines

    constants

    macro's

    enums and typedefs

    enums

    typedefs external variable declarations

    external function prototypes

    The required order of program text parts of a program file (.c) is:

    file comment header block with

    file name

    programmer name

    version number, date and description.

    copyright

    file description

    keywords includes

    standard files

    project files

    defines

    constants

    macros

    enums and typedefs

    enums

    typedefs

    external variable definitions

    (are declared in header files)

    internal variable definitions

    ( static variables)

    static function prototypes

    (external function prototypes are placed in header files)

    function definitions (global and local)

    for every function:

    function comment header block with:

    function name

    function description

    summary

    function heading (type, name, parameterlist)

    function body

    6.4 Comment

    To explain the functioning of a program, the program text has to be clear enough. This can be

    achieved by adding comment text to the program text. The language of the comments is always

    English.

    Comment headers (the first line of a comment block) start with a slash (/) at the first position

    followed by between 40 and 80 asterisks (*). Every continuing line (except the last line) starts with

  • 8/8/2019 OMS-Stand C Programming Rules

    37/58

    Layout, internal documentation

    1.2, November 20, 2002 37 of 58

    an asterisk on the first position. The last line starts with between 40 and 80 asterisks, followed with a

    slash.

    Comments consisting of only one single line must be indented according to the surrounding program

    text. Comments of more than one line may start at the left margin in order to make use of the full line.

    Global comment preferably is placed before the function itself and detailed comment within the

    function. Detailed comment blocks of more than 10 lines are preferably placed before the function or

    else directly at the beginning of the function. The reason for this is to prevent large pieces of comment

    statements that decrease the readability of the actual program text.

    Short comment (comment behind program statements) may only be used in case the readability not

    decreases.

    6.5 Alignment of declarations

    Consecutive declarations in the program text have to be aligned with each other. In case of pointerdeclarations the type-specifier is separated from the pointer (one of more *) by one or more spaces,

    whereas the pointer and identifier may be separated by one of more spaces.

    Example:long cntr;char * string;char opt;char ** pString;

    6.6 Statements

    Only one statement per line is allowed. Statements should be consistently indented according to theextended style:

    if ( a > b ){

    z = a;y = b;

    }

    6.7 Assignments and expressions

    Around an assignment-sign (=) spaces have to be placed. In expressions also spaces must be used.

    However it is recommended not to use spaces with sub-expressions.

    In order to clarify the sequence of evaluation of sub-expressions brackets must be used consistently.

    Examples:q2r = d / 2.0;area = M_PI * (r * r); /* M_PI is used from math.h */c = sqrt( a*a + b*b );V0 = j0(M_E/t1) * (t1 t0); /* M_E is used from math.h */

    6.8 Letters

    Small letters as well as capitals may be used in program text and comment lines.

  • 8/8/2019 OMS-Stand C Programming Rules

    38/58

    Programming Standard C

    38 of 58 1.2, November 20, 2002

    6.9 Line length

    Not more than 80 characters per line may be used. There are no exceptions. The reason is that printers

    normally allow only 80 characters. Longer lines would either be wrapped or truncated.

    6.10 File length

    The length of a C source file may not exceed 3000 lines (all kind of lines). This is an absolute

    maximum. Strongly recommended is a maximum of 1500 lines. Exceeding the limit of 1500 lines is

    only permitted after consulting and getting permission from the maintenance team.

    6.11 Function length

    The length of a function may not exceed 300 lines (all kinds of lines). This is an absolute maximum.

    Strongly recommended is a maximum of 150 lines. Exceeding the limit of 150 lines is only permitted

    after consulting and getting permission of the maintenance team.

    Note:

    Besides the above mentioned limits the requirements of readability, easy reference, being structured,

    modularity and cohesion of functions stay into effect.

    6.12 Spaces

    Spaces should be used to increase readability.

    Compliant Not compliantind = 0;for (i = 0; i < 4; i++ ){

    sum += c1[i+ind] + 4*i;ind += 40;

    }

    ind=0;for (i=0;i

  • 8/8/2019 OMS-Stand C Programming Rules

    39/58

    Layout, internal documentation

    1.2, November 20, 2002 39 of 58

    Example:if (a > b){

    a = b;}

    6.14 Indentation

    From a readability point of view it is important to have a clear structure of the program text. This

    structure must be achieved by indentations in case of nesting. Nesting one single level must be

    accompanied by an indentation of 3 spaces. Indentations may not be achieved by using tab characters.

    Also use indentation in case of a function call which consists of more than one line.

    6.15 Empty lines

    To introduce logical separations in the program code extra empty lines can be used. For example twonew lines before a function definition or one new line in block structures.

    6.16 Fileheader

    All C program files (.c, .h files) need to have a so-called file header block. For some simple examples

    also see the paragraph Examples.

    Every C-file must contain at least the following descriptions:

    The version information of the file. If possible this version information can be filled in by some

    version control mechanism. See also paragraph Version information in this chapter;

    The copyright information. This must be placed in the COPYRIGHT comment block; A description of the connection of the functions in the file.

    This means:

    which common functionality do these functions have;

    how these functions communicate with each other (allocate/free memory, open, read, close);

    which data structure is common for these functions;

    which part or layer of a system these functions are part of.

    This description must be made in the FILE DESCRIPTION comment block.

    These descriptions need to be in the top of the C-file in the same order.

    6.17 Function header

    All functions must have a so-called function header block. For some simple examples also see the

    paragraph Examples.

    All functions must contain the following items:

    The function target.

    This means:

    what is the use of the function;

    in which cases the function can be used.

    This part has to be put in the FUNCTION DESCRIPTION comment block. The function description is

    this block consists of a maximum of two lines preceded and followed by one empty line.

  • 8/8/2019 OMS-Stand C Programming Rules

    40/58

    Programming Standard C

    40 of 58 1.2, November 20, 2002

    How to use the function.

    This means:

    which part of the software is responsible for the proper input of the function (pre condition). See

    also the paragraph Parameter in this chapter;

    which software part is responsible for allocated memory;

    which function has to be called before this function of which function has to be called after thisfunction;

    what is the return value is this function.

    This description must be placed in the FUNCTION DESCRIPTION comment block.

    Which files will be approached in this function. Only the files given in full names are meant. This

    description has to be put in then FILE I/O comment block. This comment block is optional.

    These descriptions need to be in the top of the h-file in the same order.

    6.18 Functions and parameters

    In this paragraph some documentation aspects of functions and parameters are described.

    Functions

    When defining the function a description of the return value has to be given after the function heading

    (type and name).

    Examplelongifac ( /* return value: faculty of integer number */

    long inum) /* I integer number */...

    }

    parameters

    Of each function parameter the following aspects must be described:

    Whether it is an input and/or and output parameter

    Output parameters optionally must have a description whether memory will be allocated and if the

    calling function is responsible for the freeing of this memory.

    The meaning of the parameter.

    This part can be skipped in case acronyms are used. For a description of the use of acronyms, see

    chapter 7 Recommendations.

    The possible range of the parameter.

    For example, this can be a list of possible values, a minimum or a maximum value.

    All above-mentioned descriptions must be on the same line at the right of the definition.

    6.19 Variable/constant

    Like parameters, after each definition of variable or constant the meaning has to be described.

    The description must be on the same line at right of the definition.

  • 8/8/2019 OMS-Stand C Programming Rules

    41/58

    Layout, internal documentation

    1.2, November 20, 2002 41 of 58

    6.20 Version information

    The version information lines, also mentioned in the paragraph Fileheader of this chapter consists of

    the following parts:

    version number - a number which increases with every change;

    date - the date the change was performed in format dd-mmm-yyyy; programmer - the initials of the person who performed the last update.

    Because of the fixed format of these version lines an automatic check can be done. The check consists

    among other things of correct increase of the version number or a check whether a (proper) problem or

    project identification is given.

  • 8/8/2019 OMS-Stand C Programming Rules

    42/58

    Programming Standard C

    42 of 58 1.2, November 20, 2002

    7 Recommendations

    Portability library:

    Define a function to compose directory and file name (mkfilename())

    Define a function strdup() if necessary

    Define wrappers for common routines like fopen() that are bound to require checking.

    Define a PATH_MAX macro, if not defined in the standard include file limits.h

    Define TRUE and FALSE as respectively 1 and 0. Check that these values are used if already

    defined

    Define UNDEFINED_NAME as --Undefined--

    Define a set of routines for logging error messages

    Define a macro INVALID_STATE that prints a mesage to stderr about the program being in an

    impossible or invalid state.

    Define a macro ON_DEBUG like this:#define ON_DEBUG( level, code ) \

    if ( _DebugLevel( level ) { code }

    (This macro uses a function _DebugLevel to decide if debug output is required (given the level),

    and if it is, then execute the given code. It is a flexible alternative to the code fragment presented

    at the end of section 2.7

    Define macros MAX() and MIN() (functions are not possible because we would need to

    distinguish between types)

    Define a function BooleanString() that returns true or false given the value of its (boolean)

    argument. This makes the ?: construct in section 4.5 superfluous.

    Data types:

    Restrict the use of unsigned integers

    Always use long integer (at least: do not try to be smart about short/int/long to save a few bytes) If you have to use >, always on unsigned variables (only allowed when constructing

    bitmasks?)

    Restrict the use of unions, hardly ever necessary.

    Bitmasks should only be used symbolically

    Know when to use size_tand sizeof()

    Logical expressions:

    Use 0 == x instead of x == 0 (if == is typed as =, the first gives a compiler error!)

    7.1 Source and include filesThere are no generally valid rules for the arrangement of source and include files. In this section we

    present two possible solutions:

    Have pairs of source and include files

    Have a limited number of include files that serve for several source files at once.

    The first solution would involve:

    Prototypes and such for a source file a.c are put in an include file a.h

    Any source file depending on routines and functions in file a.c will include the file a.h.

    There are no include files that include other include files (so no global include file).

    The advantages of this approach are that changes in an include file only affect the source files thatreally use the routines and functions it declares. The disadvantage is that the list of include files in the

  • 8/8/2019 OMS-Stand C Programming Rules

    43/58

    Recommendations

    1.2, November 20, 2002 43 of 58

    top part of each source file can become very long and unmanageable. Also the number of files to

    maintain is twice the number of source files. This is especially annoying if the routines are rearranged

    in other source files. What is more, the programmer will have to know which file defines which

    routine.

    The second solution would involve:

    Prototypes and such for several source files, a.c, b.c, c.c, d.c are put in an include file abcd.h

    Any source file depending on routines and functions in files a.c, b.c, c.c or d.c will include the file

    abcd.h.

    There can be an include file that include other include files.

    The advantages are that the list of include files will be short. The total number of files will be only

    slightly larger than the number of source files. The disadvantage is that changes in prototypes for

    routines found in a.c for instance, also affect files that include abcd.h but do not use these routines.

    Our recommendation is the latter (see also chapter 10), as this seems more manageable and allows

    header files for libraries rather than for individual source files. That is, define one header file per

    library.

    7.2 Names of routines and functions

    Our recommendation is to use a common prefix for the names of functions and routines that have a

    common task, e.g. the routines and functions in a particular library or the routines and functions that

    manipulate a certain data structure.

    The names of routines and functions must start with a capital if they are publicly useable, otherwise

    they should have a name starting with a single underscore and then the same prefix. (Further

    refinements are possible but not desirable).

    For example:

    The routines and function that manipulate a general hash table structure are found in two source files,

    hash1.c and hash2.c, because of the total size of the routines. Then the following prefixes are

    available:

    Hash Any publicly useful routine or function

    _Hash Private (both static and non-static) routines in hash1.c or hash2.c.

    These routines may also be used in the other source file but are not meant for use beyond the hash

    module. Via the underscore it is indicated that the non-static routines are not meant for use outside this

    collection.

    In all cases:The names of routines and functions must indicate their proper use. Therefore:

    Use verbs that indicate the action

    Use forms like Is or Has to indicate that something is available or of the mentioned type.

    These are examples of bad names:/* Bad: returns a string, suggests a boolean */char *DictIsAvailable( dict, keyword );

    /* Bad: what does activate mean? */void DictActivateKeyword( dict, keyword, value );

    Better names and declarations:

  • 8/8/2019 OMS-Stand C Programming Rules

    44/58

    Programming Standard C

    44 of 58 1.2, November 20, 2002

    /* Good: returns a boolean, as suggested by the name */typedef long bool;bool DictIsAvailable( dict, keyword );

    /* Good: store the key-value pair */void DictStoreKeyValue( dict, keyword, value );

  • 8/8/2019 OMS-Stand C Programming Rules

    45/58

    Prohibited statements and constructions

    1.2, November 20, 2002 45 of 58

    8 Prohibited statements and constructions

    8.1 Forbidden data types

    Forbidden data types are:

    Bit fields and register variables

    8.2 Null statement

    The null statement (;), mostly found with for and while loops may not be used.

    8.3 Forbidden standard functions

    The standard C library contains a number of functions whose proper use is problematic or that cause

    confusion. The table below lists these functions.

    Function Reason for forbidding their use

    I/O functions

    fgetc()

    fputc()

    fscanf()

    scanf()

    gets()

    Unnecessary, the more useful alternative is fgets()

    Unnecessary, the more useful alternative is fputs()

    It is near to impossible to get the end-of-line problem solved, use a

    combination of fgets() and sscanf()

    Ditto (Use fgets() and sscanf() instead)

    It is impossible to control the length of the string to be read, very long

    input lines will cause a memory overflow. Use fgets() instead.

    Miscellaneous

    signal()

    setjmp()/longjmp()

    abort()

    atexit()

    atabort()

    The semantics of signals is very difficult. Their use is in the majority of

    cases not necessary anyway.

    These routines cause a non-local jump and therefore lead to

    unmanageable program control flows.

    When aborting the program, files are not properly closed

    Registering call-backs is an unnecessary complication in most programs

    Ditto

    8.4 Illegal use of pre-processor constructs

    Sometimes programmers use the pre-processor to hide C syntax or to hide fragments of code that are

    only needed during testing or debugging. This is utterly forbidden. Other programmers are not familiar

    with such replacements and get confused. This happens especially if you use macros to hide the

    looping over certain variables (this is an actual example of how source code becomes almost

    unmanageable!)

    This horror has been illustrated below (note that the hidden variable listp becomes visible whenever

    you want to do something with the elements of the list):

  • 8/8/2019 OMS-Stand C Programming Rules

    46/58

    Programming Standard C

    46 of 58 1.2, November 20, 2002

    /* Define list variable here (used in macro below) */ListPtr listp;

    /* Define shorthand for iteration over list */#define ScanList( list ) \for ( listp = list; listp != NULL; \listp = listp->next )

    int main( int argc, char *argv[] ){

    /* Scan the list of titles, searching for the right value */ScanList( titles ){

    if ( strcmp( listp->value, which_title ) == 0 ){

    printf( Found it!\n );}

    }

    A trivial example that attempts to introduce extra keywords (Pascal, Fortran, ) into C is this:/* DO NOT USE THE PREPROCESSOR IN THIS WAY */

    #define BEGIN {#define END }

    if ( x > 0 ) BEGIN/* Do something */

    END

    There is no recommended alternative: always use the proper C syntax.

    A similarly forbidden construct is this function that may be used to define debugging statements:/* DO NOT USE THE PREPROCESSOR IN THIS WAY */#define ON_DEBUG( a ) if ( _debug_ ) { a }

    Instead simply use the if-construct directly:

    if ( _debug_ ){

    /* Debug code */}

    Yet another example:/* DO NOT USE THE PREPROCESSOR IN THIS WAY */#if defined( DEBUG )printf( Debug output: x = %d\n, x );#endif

    The alternative here is the same as above: use a proper if-construct. The code will always be

    compiled, yet there is little or no impact on the performance if it is not used, because the controlling

    parameter is false.

    Note:

    The only exception to the rule of not hiding code constructions in macros is the standard assert()

    macro. Using this macro is actually encouraged, to document the preconditions and post-conditions of

    a function.

    8.5 Illegal use of functions

    Certain standard functions are frequently used in manners that cause confusion to the reader. This

    section clarifies at least a number of such illegal uses.

    Functions that return integers or pointers are used in a logical contextOne example of this is strcmp() in the following fragment:

  • 8/8/2019 OMS-Stand C Programming Rules

    47/58

    Prohibited statements and constructions

    1.2, November 20, 2002 47 of 58

    Compliant Not compliantif ( 0 !=

    strcmp( string1, string2 ){

    fprintf( stderr,

    String does not match\n );}

    if ( strcmp( string1, string2 ) ){

    fprintf( stderr,String does not match\n );

    }

    The problem is that strcmp() return 0, -1 or 1, so not a logical value at all and that the check is

    supposed to be is string1 equal to string2? If not, return an error. Because strcmp() returns a zero

    (ordinarily a false value) when the two strings are equal, the form of the above fragment is as if the

    strings have to be equal for the error message to be printed. This is confusing.

    Another example:

    Compliant Not compliant

    infile = fopen( input.dat,r);if ( infile != NULL ){

    fgets( ... );}else{

    fprintf( stderr,Could not open file!\n );

    }

    infile = fopen( input.dat,r);if ( !infile ){

    fgets( ... );}else{

    fprintf( stderr,Could not open file!\n );

    }

  • 8/8/2019 OMS-Stand C Programming Rules

    48/58

  • 8/8/2019 OMS-Stand C Programming Rules

    49/58

    References

    1.2, November 20, 2002 49 of 58

    9 References

    Les Hatton (1995)

    Safer C, Developing Software for Hingh-intergrity and Safety-critical Systems

    McGraw-Hill, 1995

    Brian W. Kernighan, Dennis M. Ritchie (1988)

    The C Programming Language,

    Prentice-Hall, second edition, 1988

  • 8/8/2019 OMS-Stand C Programming Rules

    50/58

  • 8/8/2019 OMS-Stand C Programming Rules

    51/58

    Log sheet

    1.2, November 20, 2002 51 of 58

    Appendix A Log sheet

    Document

    version

    dat e Changes with respect t o the previous version

    0.8 April 17, 2002 Document name changed

    0.9 September 25, 2002 Changed due to review of Stef Hummel (some items)

    1.1 November 20, 2002 Changes w.r.t. long and int

  • 8/8/2019 OMS-Stand C Programming Rules

    52/58

  • 8/8/2019 OMS-Stand C Programming Rules

    53/58

    Set/get functions

    1.2, November 20, 2002 53 of 58

    Appendix B Set/get functions

    This appendix illustrates the use of specialised pre-processor macros to elegantly solve a maintenance

    problem. The problem was that a set of Fortran routines needed access to data stored in a C structure,

    both to set the fields and to get the current value.

    By defining a large number of individual get and set routines, this can be solved in a straightforward

    manner. However:

    There were at least 30 fields involved, so this solution would require 60 individual set/get functions.

    If the underlying structure changed, one would need to create new get and set functions or update the

    existing ones.

    Therefore a solution was sought and found in the following way (details have been left to clarify the

    solution):

    A generic set of macros was defined, one for each type of data items

    The fields to be accessed formed an argument to these macros

    The macros were embedded in a routine that could be called from Fortran where one of the arguments

    is the name of the field (so a string!).

    From the Fortran side, one can set a field noparams as:call setpar( noparams, noparams )

    On the C side the code that is executed looks like this (*value because Fortran passes everything by

    reference):void setpar( char *field_name, long *value )

    {if ( strcmp( field_name, noparams ) == 0 ){

    data->noparams = *value;}

    }

    The if-construction is repeated for all relevant fields, but actually this is done via the following macro:#define INT_VALUE( field ) \if ( strcmp( field_name, #field ) == 0 ) \{ \

    data->field = *value; \}So, the C routine setpar can actually look like this:void setpar( char *field_name, long *value ){

    INT_VALUE( noparams )INT_VALUE( intopt )INT_VALUE( nosubst )

    }

  • 8/8/2019 OMS-Stand C Programming Rules

    54/58

  • 8/8/2019 OMS-Stand C Programming Rules

    55/58

    Examples

    1.2, November 20, 2002 55 of 58

    Appendix C Examples

    C.1 Example C module (.c)

    /********************************************************************** Filename owndef.c* Programmer H.Hanzon* Version 1.00 27-11-1994 First Edition** COPYRIGHT** Copyright (c) 1994 "Rijkswaterstaat"* Permission to copy or distribute this software or documentation* in hard copy or soft copy granted only by written license* obtained from "Rijkswaterstaat".* All rights reserved. No part of this publication may be* reproduced, stored in a retrieval system (e.g., in memory, disk,* or core) or be transmitted by any means, electronic, mechanical,* photocopy, recording, or otherwise, without written permission* from the publisher.************************************************************************ FILE DESCRIPTION** This file (module) contains functions to manage the traffic at a* crossing.** Warning!* This example is for clarification purposes only.* Each definition and declaration stands for itself.* Together, they do not form a coherent program structure.**********************************************************************

    /* ----- includes ----- */

    #include #include

    #include "owndef.h"

    /* ----- definitions ----- */

    /* traffic lights */#define RED 1 /* red light */#define ORANGE 2 /* orange light */#define GREEN 3 /* green light */#define BLINKING_ORANGE 10 /* warning light */

    #define MACRO (bWhat,iThis,iThat) ((bWhat)?(iThis):(iThat))/* conditional expression */

    /* ----- enums and typedefs ----- */

    enum hallo{

    Here,There

    }; /* enumeration (example) */

    typedef struct complex{

    float fRe; /* real part of complex number */float fIm; /* imaginary part of complex number */

    } COMPLEX;

    typedef char *WHY;

    /* ----- external variable definitions ----- */ /* afgeraden */

    WHY sMe="?"; /* global variable question mark */

    /* ----- internal variable definitions -----*/

  • 8/8/2019 OMS-Stand C Programming Rules

    56/58

    Programming Standard C

    56 of 58 1.2, November 20, 2002

    static float fHome; /* internal variable (automatically initialised) */

    /* ----- static function prototypes ----- */

    static long ChangeState ( /* return value: error code */long * plState); /* o state of the light */

    /* ----- function definitions ----- */

    /********************************************************************** FUNCTION NAME GiveMessage** FUNCTION DESCRIPTION** This function produces a message, depending on the state of the traffic* light.** Warning: the description of the function may be long, but it should always* start with (at most) two lines with a summary of the full description.* These two lines are preceded and followed by at least one blank line.************************************************************************ PSEUDO CODE

    ** if the light is red then* print "stop"* elseif the light is orange then* print "go or halt"* elseif the light is green* print "go"* else* print "watch out"*********************************************************************/

    void GiveMessage ( /* no return value */long lState, /* i state: state of the light */long iClone, /* i clone of object */float fTarget) /* i thing to hit */

    {long * piPointer = &iClone; /* pointer to object */float * pfToTarget = NULL; /* pointer to target */char * sMessage =