Declarations: variables

Rule: each variable is to be declared in a separate statement.

Rec.: the dereference operator '*' and address-of operator '&' are to be attached to the associated type name. (This is not dangerous thanks to the preceding rule.)

Rec.: prefer initialization over assignment.

Rec.: minimize the use of global variables, or if possible do not use them at all.



Most languages allows the declaration of multiple variables of the same type in a short form:

    var
        a, b, c: integer; (* Yes, it's Pascal... *)
        s, t, u: ^char;   (* Declares s, t and u as pointers to char *)

In C and derived syntaxes, however, it is possible to use this short form to declare variables of different type:

    char * s, t, u; // Warning!!! Only s is a pointer to char, t and u are char

This has the potential of introducing some subtle bugs that can be particularly difficult to find, because the discrepancy is usually difficult to notice in a cursory inspection. The traditional answer to this problem is to attach the type modifier (the pointer-to character '*' or the address-of character '&') close to the variable name.

In practice, this is hardly an improvement. The issue of being able to declare (and possibly initialize) variables of different type in a single statement is not addressed, so there is still room for mistakes.

Also, many programmers find it more logical to think of the type "pointer to character" as the single expression "char *", rather than of the base type char with the operator '*' applied to each identifier that must be a pointer. (Though experience do help with this kind of issues.)

Rule: each variable is to be declared in a separate statement.

Rec.: the "pointer-to" operator '*' and "address-of" operator '&' are to be attached to the associated type name. (This is not dangerous thanks to the preceding rule.)

For example:

    // Ok: one declaration per statement
    char * s;
    char * t;
    char * u;

A variable should be declared close to the statement where it is first used, and possibly go out of scope when it is not needed anymore. However, it is better not to declare a variable in the middle of a block when it gets used up to the end of the block, because that makes it difficult to find the variable declaration. In such cases, the variable should be declared at the beginning of the block.

A common exception to the above rule is when the parameters needed to initialize a variable, say the constructor arguments for instance, are not available at the beginning of the function. It may be then necessary to declare the variable in the middle of a block, when the required data is available. A blank line put before and after the declaration can help separate it from the rest of the code, making it easier to find.

It sometimes happens that a variable is needed only for a few instructions. For these variables, which most often are used to hold temporary values, it might be sometimes preferrable to create a block (and thus a scope) so that they can be declared and stay alive just for the short time they are needed. This makes code more readable and in a few cases more efficient, as unnecessary allocations are avoided. For example:

    int main( void )
    {
        // ...some code here...
    
        int temp; // Warning: temp stays alive until end of function

        temp = a;
        a = b;
        b = temp;

        // ...some long code here...

        // Sorry! The common name temp cannot be redeclared here, and the
        // declaration cannot be found at beginning of function either
        String temp;
    
        temp = s;
        s = t;
        t = temp;

        // ...some code here...

        return 0;
    }

This is at times worked around by declaring a different temporary variable for each type, such as tempInt, tempStr and so on. Here is an alternative approach that is not necessarily better in all circumstances, but is worth considering:

    int main( void )
    {
        // ...some code here...

        // Variable temp goes out of scope as soon as it's not needed, and
        // block is so short that reader will always get the whole picture
        {
            int temp;

            temp = a;
            a = b;
            b = temp;
        }

        // ...some code here...

        return 0;
    }

In C++ there is difference between variable initialization and assignment:

    class String {
    public:
        String(); // Default constructor
        String( const String & ); // Copy constructor
        String & operator = ( const String & ); // Assignment operator

        //...
    };

 
    String okMessage;
    ...
    String messageOne( okMessage );
    String messageTwo;
    msg2 = okMessage;

The compiler invokes only the copy constructor to initialize the variable messageOne, whereas messageTwo is first constructed with the default constructor and then assigned the specified value by calling the assignment operator. For anything but the simplest objects this extra step is inefficient code that can have an adverse impact on the program performances. When possible, use initialization instead of assignment.

Rec.: prefer initialization over assignment.

It is a well known fact that global variables make programs a lot harder to understand and maintain. Potentially, a global variable can affect the behavior of each and every line of code in a program. This makes it very hard to describe and follow the state of the program at a specified point, because this state also depends directly or indirectly on the value of all of the global variables in the program. But even if we are positive about the state of a program in a known situation, a single change in the value of a global variable can have an effect on parts of code that we are not able to see on the spot. These code portions can be placed in another module, or simply too far in the current module for us to notice. It then becomes relatively easy to lose tracks of the changes and the code is no longer maintenable.

The obvious recommendation here is to avoid using global variables altogether, but in practice that may not be possible for a number of reasons. It is then useful to at least try to prevent future damages by using them in a limited manner. If possible, use global variables as constants. That is, initialize them at startup or very early in the program code, and then never change their value until the program exits. In any case each global variable should be modified in a single place, and possibly by a single function. When code is added that may change the value of a global variable, it is good practice to do a quick search in the code for other uses of that variable to make sure our changes are compatible with the current usage. This helps finding problems that may be very hard to track down in a debugger.

Rec.: minimize the use of global variables, or if possible do not use them at all.

Top: Table of content  |  Previous: Identifiers  |  Next: Declarations: classes and class members (C++)

Copyright (c) 2003 Alessandro Scotti. All rights reserved.