荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: BlueBoy (蓝孩), 信区: Program
标  题: ESA Style Guide for 'C' coding
发信站: BBS 荔园晨风站 (Fri Mar 24 12:21:00 2000), 转信


From: Paul Singleton <paul@cs.keele.ac.uk>
Subject: Re: recommendation concerning c/c++ style
To: Christopher Lott <lott@informatik.uni-kl.de>
Date: Fri, 19 Nov 1993 14:53:56 +0000 (GMT)

I tweaked the format of 'cstyle-ESA-OZ.txt' because (with 8-character tabs)
it had lines longer than 80 chars, and the examples were misaligned.

Here's a version which

        * is free of tabs (uses spaces instead, so it is independent
          of terminal settings etc)

        * has no lines longer than 79 chars.

I haven't changed the semantics.

=== cstyle-ESA-OZ.v2.txt ======================================================

ESA Style Guide for 'C' coding
==============================

1.0 Introduction
================

1.1 Identification

    This document is Copyright 1991, Expert Solutions Australia Pty.
    Ltd. Permission is hereby granted for copying of this document (or
    parts thereof) provided it is not for commercial gain and provided
    ESA is acknowldedged as its source.

1.2 Introduction

    This document is a guide to what is considered good style for C
    code. Style covers general rules to be applied when designing and
    writing code, and actually can be applied to any programming
    language.

    The document is structured into a number of general headings, and
ions in this document cover how code should be designed
    and written, as opposed to how the syntactic elements should be
    laid out, which is the scope of the C Coding Standard.

    A company standard style has the advantage that the intent and
    workings of a piece of code are easier to grasp because the way
    things are done are familiar.  The recommendations in this
    document are a distillation of the experience of ESA staff,
    summarized to provide a suggested style with reasons why it is
    considered 'a good thing'.

1.5 Definition of Terms

    The definitions used in the C Coding Standard are used in this document.


2.0 Comments
============

2.1 General

    Sooner or later, and more likely sooner, someone is going to have
    to maintain your code. They will need to understand what it is
    trying to do, how it is doing it, and why it is doing it the way
    it is - so document these decisions in the code as you make them.

2.2 Inline Comments vs. Summary Comments

    Inline comments are small comments in front of the piece of code
    they are describing. They make that piece of code easier to
    understand. Summary comments are large comments at the start of
    the function they are describing. They make the function as a
    whole easier to understand. These should be descriptive - an
    explanation in English of what the function is trying to do and
    how it is doing it.

    Consistently use one or the other (or even use both, though this
    may increase the work maintaining the code, although descriptive
    summary comments should not need chaorrectly, e.g.

        ! ! ! ! NO ! ! ! !              + + + + YES + + + +

        int max = 0;                    int max;
            :                               :
            :                           max = 0;
        for ( i=0 ; i<n ; i++ )         for ( i=0 ; i<n ; i++ )
            {                               {
            if ( vec[i] > max )             if ( vec[i] > max )
                {                               {
                max = vec[i];                   max = vec[i];
                }                               }
            }                               }

    In the second example, it is more obvious to someone looking at
    the for loop (which may be a long way from the definition of max)
    that max is set to a minimum value.

3.2 Error Code / Return Value

    Avoid initializing a return value variable to an obscure default
    value for the reasons given above, and also because it may happen
    that it is not set explicitly when it should be - it can be har
}
                                      else        /* not found */
                                          {
                                          status = NO_ENT;
                                          }

    The second example more obviously covers both cases correctly.


4.0 Control Flow
================

4.1 Nested if statements

    Use nested if statements if there are alternative actions (i.e.
    there is something in the else clause), or if something done by a
    successful evaluation of the condition has to be undone. Don't use
    nested if statements if there are actions only in the if clause,
    e.g.

    ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! NO ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !

    status = delta_create( (Callback)NULL, &delta);
    if ( status == NDB_OK )
        {
        status = delta_record_condition( delta, ...);
        if ( status == NDB_OK )
            {
            status = delta_field_condition( delta, ...);
            if ( status == NDB_OK )
                {
                status = delta_field_condition( ...);
                if ( status == NDB_OK )
                    {
                    status = delta_commit( delta, ...);
                    }
                }
            }
        (VOID)ndb_destroy_delta( delta);
        }
    return( status);


    + + + + + + + + + + + + + + + + YES + + + + + + + + + + + + + + + +

    status = delta_create( (Callback)NULL, &delta);
    if ( status == NDB_OK )
        {
        if (  (status=delta_record_condition(...)) == NDB_OK
           && (status=delta_field_condition(...)) == NDB_OK
           && (status=delta_field_condition(...)) == NDB_OK )
            {
            status = delta_commit( delta, ...);
            }
        (void)ndb_destroy_delta( delta);
        }

4.2 Multiple Returns vs One Return

    Having a single return statement at the end of a function has the
    advantage that there is a single, known point which is passed
    through at the termination of execution of the function. It forces
    you to write structured code, using if-then-else statements
    'properly'. It does mean that the code can become deeply indented,
    but if this becomes a problem, you probably should break the
    function into a number of sub-functions.
    Avoid using multiple returns if the code can be just as clearly
    written with one return. If you think that the multiple return
    version would be superior, it may be tolerated, e.g.

    ! ! ! ! NO ! ! ! !               + + + + YES + + + +

{                            if ( vec[i] == key )
            return TRUE;                     {
            }                                found = TRUE;
        }                                    }
    return( FALSE);                      }
                                     return( found);

    The latter has the advantage that if we later on decide that there
    is more to do after the search, we just put it between the for
    loop and the return, whereas with the former we have to modify the
    for loop somehow.

4.3 Break statements

    For the same reasons as given above, avoid using break statements
    where boolean variables can be used, e.g. the second example above
    could be written to break out of the loop when a match is found,
    and the test i < max used instead of the variable found - this
    would be less clear and so harder to maintain.


5.0 Pointers
============

5.1 NULL

    NULL is the null, or invalid, pointer. You can assume it is
    #defined to the literal value 0, alng assigned to, or compared
        with, a pointer - the compiler will get it right, and you may not.

    o   Do cast NULL when it is being passed as a function argument -
        the non-ANSI compiler won't get it right, so you'd better.

    o   Do not use 0 as the literal null pointer - the compiler will get
        it right, but make it obvious to us mere mortals by using NULL.

    o   Do not use NULL as a literal integer - it may be defined to
        something other than 0, and this is very confusing anyway.

    o   Do not do an implicit pointer comparison with 0 - the compiler will
        get it right, but make it obvious by comparing it against NULL.

    o   Do not do an implicit integer comparison with 0 - the compiler
        will get it right, but make it obvious by comparing it against 0.

    *   Note that by "implicit comparison with 0" I mean "if (i) ..."
        and "if (!i) ...". If i is not intended to be a boolean, it
        can be difficult working out what the test is really trying to
        do (it is not glaringly obvious that "if (!i) ..." is true
        when i is zero), and it indicates that you are not handling
        all the cases properly (do you mean `i > 0' or `i < 0' or `i
        != 0').


6.0 Data Structures
===================

6.1 Using struct

    A data structure defines the information about some thing (aka an
    object). All information about an object should be grouped into a
  GLOBAL struct
        GLOBAL Int  height;                 {
                                            Int    width;
                                            Int    height;
                                            }
                                        rectangle;

    This can often be omitted when there is just one instance of the
    thing - for automatic variables, it is often best to not do it,
    but variables with global or file scope should be turned into
    structs. The utility of this approach is clearer when there is an
    array of such objects, e.g.

        ! ! ! ! NO ! ! ! !              + + + + YES + + + +

        GLOBAL Int  width[100];         GLOBAL struct
        GLOBAL Int  height[100];            {
                                            Int    width;
                                            Int    height;
                                            }
                                        rectangle[100];

    With the former, we don't know for sure (without inlobal Data

    Program global data should be avoided at all costs since it cannot
    be protected against change. Every data item in a program should
    belong to some `thing', and it is that thing's responsibility to
    maintain it. Rather than making   change, and if necessary, it can be
abstracted such that the
    function provides an 'ideal' view of the data while internally it
    is different, e.g.

    ! ! ! ! NO ! ! ! !

    EXTERN Colour   colour_table [];
        :
    rectangle_draw( x, y, width, height, colour_table[c]);
        :

    + + + + YES + + + +

    GLOBAL Colour   colour_table(int colour);
        :
    rectangle_draw( x, y, width, height, colour_table(c));
        :

    With the former, a later change which involves calling some X
    function to determine the Colour for an int means that any code
    that uses colour_table has to be modified, whereas with the
    former, only the function colour_table has to be modified.

    This principle should also be applied to file global data - it is
    invariably better to provide a functional interface (possibly
    using macros) to file global data as it abstracts them better,
    making it easier to later change their representation.


7.0 Functions
=============

7.1 Function vs. Prld do something to (i.e. affect the state of,
        or change) one or more of its arguments (usually its first
        argument, which it is considered a method of). Successive
        calls to a procedure will generally have different effects. A
       results (i.e. it should be idempotent). A
        function should return whatever is required to indicate the
        state being interrogated.

    The rationale for this is to focus procedures and functions on
    doing one thing - this makes them more reusable, and easier to
    write, understand, and maintain.  Complex functions which do
    multiple things are required (e.g. generating a report involves a
    lot of different activities), but it is better to have these as a
    third class of [usually] non-reusable functions, e.g.

    ! ! ! ! NO ! ! ! !

    el1 = (Element *)list_delete_current( list);
                        :
    el2 = (Element *)list_advance_to_next( list);

    + + + + YES + + + +

    el1 = (Element *)list_current_element( list);
    (VOID)list_delete_current( list);  /* assume will not fail */

    (VOID)list_advance_to_next( list); /* assume will not fail */
    el2 = (Element *)list_current_element( list);

7.2 Size of functions

    Functions should not be too lether they are input, input+output, or output,

    o   what it is intended to do,
        o   in English and without any of the details of how it does it,

    o   under what pre-conditions it promises to do this,
        o   including temporal conditions (e.g. must be called
            immediately after object_do_something()), and state
            conditions (e.g. both list_before_first() and
            list_after_last() must be false),

    o   what post-conditions will apply once the function has fing ASSERT()
    which will do the checking in development code but should do
    nothing in production code. It is a 'good thing' if the
    post-conditions are also checked with ASSERT(). Neither the
    pre-conditions nor post-conditions must be checked with if
    statements - this is the caller's responsibility, not the
    function's.

7.4 Reentrant Functions

    Using structs for all related data, passing a pointer to a struct
    as a function argument, and using no static data makes the code
    reentrant. This should be done where possible because of the event
    driven nature of our software.


8.0 Miscellaneous
=================

8.1 Setting Memory

    There are a number of ways of setting memory in C:

    o   Direct assignment, including structure assignment, which
        should be used whenever possible as the compiler will work out
        how much to copy.

    o   The str... functions, which should be used to copy strings
        (i.e. arrays of characters). Note that str_ncpy is preferredan initial
value (which is not always for the best) and puts a
    magic number (i.e. 0) in the design. Use explicit initialization
    of fields instead - defensive programming says to initialize
    everything, so initialize data with no obvious initial value to an
    invalid value (e.g. NULL for pointers).

8.2 Allocating and Freeing Memory

    If a module allocates memory for some object, it should also be
    the one to free it. Similarly, if a module simply fills in a given
    block of memory, it should not be the one to free the block.

    A corollary to this is that chunks of allocated memory should not
    be handed over by one module to another (except for modules
    specifically built to do this - e.g. a heap allocation module).
    This is poor practice since:

    o   it means that two different modules share the data, making
        them interdependent and introducing potential for confusion
        and bugs;

    o   it means that both modules need to understand the structure of
        the block of memory.

    A better solution is for some unique identifier to be generated
    for a block of memory and that identifier be used by all modules
    (other than the owning module) to reference a particular object
    instance.

    Note that thi           utility_function( ...
                          addr,                           id,
                          ...);                           ...);
        free( addr);                    object_destroy( id);

    addr may be inadvertently reused now, probably causing some awful
    memory allocation bug that is a nightmare to track down. id can
    also be reused, but the object routines will complain about it no
    longer being valid.

-------------------------------------------------------------------------------
Matt Atterbury [matt@bacchus.esa.oz.au]   Expert Solutions Australia, Melbourne

--
※ 来源:·BBS 荔园晨风站 bbs.szu.edu.cn·[FROM: 192.168.0.90]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店