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.
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.
Copyright (c) 2003 Alessandro Scotti. All rights reserved.