Code Complete --- (Part3 Variables)
=========================================================
1. General issues in using variables
A pompus[华而不实的] fraud [骗子] --- 绣花枕头
Following are guidelines for avoiding initialization problems,
---- Initialize each variable as it's declared
---- Initialize each variable close to where it's first used
---- Use const when possible
---- Pay speical attention to counters and accumulators, i, j, k, total
---- Initialize a class's member data in its constructor
---- Initialize working memory at the beginning of your program ,
0xCC is a good value to use because it's the machine code for a breakpoint interrupt(int 03h).
Scope
---- Keep variable "Live" for as short a time as possible
Persistence
Binding Time (In general, the later you make the binding time, the more flexibility you build into your code)
---- The earliest possible time at Code-Writing time
Title.Color = 0xFF;
---- A slightly later time at Compile time
const int TITLE_COLOR = 0xFF;
Title.Color = TITLE_COLOR;
---- The later time at Run time
Title.Color = ReadTitleColor();
Coding time (use of magic numbers)
Compile time (use of named constant)
Load time (reading a value from an external source such as Windows registry file)
Object instantiation time (reading value each time a window is created)
Just in time (reading value each time the window is drawn)
Using each variable for exactly one purpose
=========================================================
2. The power of variable names
Considering in choosing good names
---- Problem orientation, speak to the problem rather than the solution, express the 'what' more than 'how', refers to the problem domain rather than the computing universe.
---- Optium name length
---- the effect of scope on variable names, use qualifiers on names that are in the global namespace
---- computed-value qualifiers in variable names
Many programs have variables that contain computed values: totals, averages, maximums, and so on. If you modify a name with a qualifier like Total, Sum, Average, Max, Min, Record, String, or Pointer, put the modifier at the end of the name.
---- Common Opposites in variable names
Naming special types of data
---- Naming Loop Indexes, i,j,k, or more informative name.
---- Naming Status Variables, think of a better name than 'flag' for status variable, 'printerReady'.... or enum type
---- Naming temporary variables
---- Naming Boolean variables
Keep typical boolean names in mind
---- 'done', 'error','found', 'success','ok','ready'
IsFound, if (IsFound) --- if (found) would be better
---- Naming Enumerated types
C++ Color_Green, Color_Red....
C# Green, Red (Color.Green, Color.Red)...
---- Naming Constants , using named variable
The power of naming conventions
There are no hard-and-fast rules[金科玉律] for when you should establish a naming convention, but here are a few cases in which conventions are worthwhile:
---- When multiple programmers are working on a project
---- When you plan to turn a program over to another programmer for modifications and maintenance (which is nearly always)
---- When your programs are reviewed by other programmers in your organization
---- When your program is so large that you can't hold the whole thing in your brain at once and must think about it in pieces
---- When the program will be long-lived enough that you might put it aside for a few weeks or months before working on it again
---- When you have a lot of unusual terms that are common on a project and want to have standard terms or abbreviations to use in coding
Guidelines for a language-independent convention
---- Differentiate between variable names and routine names, begin with lower case/upper case: myName, MyName();
---- Differentiate between classes and objects
---- Identify global variables, g_RunningTotal, names with g_ prefix
---- Identify member variables, m_
---- Identify type definition, t_, t_char
---- Identify named consts, use all upper case letters, MAX
---- Identify elements of enumerated types, Color_
---- Identify input-only parameters in languages that don't enforce them, const &, *
---- Format names to enhance readability, Point_Total
....
C++ Constants, typedef, and preprocessor macros are in ALL_CAPS
=========================================================
3. Fundamental data types
Number in General
---- Avoid "magic numbers"
use hard-code 0 and 1 if you need to
---- Anticipate divide-by-zero errors
---- Make type conversions obvious
---- Avoid mixed-type comparsions
---- Heed your compiler's warnings
Integers[5intidVE]
---- Check for integer division 7/10 != 0.7, so 10 *(7/10) != 7 *******
---- check for integer overflow
---- Check for overflow in intermediate results
Float-point numbers
---- Avoid additions and subtractions on numbers that have greatly different magnitudes
1,000,000.00 + 0.1 probably produces an answer of 1,000,000.00
---- Avoid equality comparisons
const double ACCEPT_DELTA = 0.00001;
bool Equals(double termSource, double termTarget){
if (Math.abs(termSource - termTarget) < ACCEPT_DELTA )
return true;
else
return false;
}
---- Anticipate rounding errors (you multiply dollars by 100 ....)
Characters and Strings
---- Avoid magic charactes and strings
---- Strings in C
---- Be aware of the difference between string pointers and character arrays
StringPtr = "Some Text String";
In this case, "Some Text String" is a pointer to a literal text string and the assignment merely sets the pointer StringPtr to point to the text string. The assignment does not copy the contents to StringPtr.
---- Declare C-Style strings to have length CONSTANT + 1
char name[ NAME_LENGTH + 1 ] = { 0 };
for ( i = 0; i < NAME_LENGTH; i++ )
name[ i ] = 'A';
---- Intialize strings to null to avoid endless strings.
You can avoid endless strings in two ways. First, initialize arrays of characters to 0 when you declare them ---- char EventName[ MAX_NAME_LENGTH + 1 ] = { 0 };
Second, when you allocate strings dynamically, initialize them to 0 by using calloc() instead of malloc(). calloc() allocates memory and initializes it to 0. malloc() allocates memory without initializing it, so you take your chances when you use memory allocated by malloc().
---- Use arrays of characters instead of pointers in C
---- Use strncpy() instead of strcpy() to avoid endless strings.
strcpy() and strcmp() keep going until they run into a null terminator. Their safer companions, strncpy() and strncmp(), take a parameter for maximum length so that even if the strings go on forever, your function calls won't.
Boolean variables
---- Use boolean variables to simplify complicated tests to document your program
if ( ( elementIndex < 0 ) || ( MAX_ELEMENTS < elementIndex ) ||
( elementIndex == lastElementIndex )
) {
...
}
finished = ( ( elementIndex < 0 ) || ( MAX_ELEMENTS < elementIndex ) );
repeatedEntry = (elementIndex == lastElementIndex );
if ( finished || repeatedEntry ) {
...
}
Named Constants
---- Use named constants in data declarations
---- Avoid literals, even 'safe' ones
For i = 1 To 12
For month = Month_January To Month_December
Arrays
---- In C, use the ARRAY_LENGTH(x) macro to work with arrays
#define ARRAY_LENGTH(x) (sizeof(x)/sizeof(x[0]))
Creating your own types (type aliasing)
typedef float Coordinate;
---- Avoid predefined types
---- Don't redefine a predefined type
---- Define substitue types for portability
define a type INT32 and use it instead of int, or a type LONG64 instead of long
---- Consider creating a class rather than using a typedef
=========================================================
4. Unusual data types
Structures
---- use structures to clarify data relationships
---- use structures to simplify operations on blocks of data
---- use structures to simplify parameter lists
---- use structures to reduce maintenance
Pointers
The knowledge of how to interpret the contents of a location in memory is provided by the base type of the pointer. If a pointer points to an integer, what that really means is that the compiler interprets the memory location given by the pointer as an integer.
General tips on pointers
---- lsolate pointer operations in routines or classes
---- Declare and define pointers at the same time
---- Delete pointers at the same scoping level as they were allocated
---- Check pointers before using them
---- Check the variable reference by the pointer before using it
---- Usd dog-tag fields to check for corrupted memory
---- Use extra pointer variable for clarity to simplify complicated pointer expressions
startNode->next->previous
Node *followingNode = startNode->next
---- Shred your garbage/Set pointers to null after deleting or freeing them/Check for bad pointers before deleting a variable
ASSERT(pointer != NULL, "Attempting to delete null pointer.");
memset(pointer, GARBAGE_DATA, MemoryBlockSize(pointer));
delete pointer;
pointer = NULL;
---- Keep track of pointer allocations
IsPointerInList(pointer)
---- Write cover routines to centralize your strategy to avoiding pointer problems
#define
SAFE_DELETE(pointer) {
ASSERT(pointer
!=
NULL,
"
Attempting to delete null pointer.
"
);
if
(IsPointerInList(pointer))
{
memset(pointer, GARBAGE_DATA, MemoryBlockSize(pointer));
delete pointer;
pointer = NULL;
}
else
{
ASSERT(FALSE,"Attempting to delete unallocated pointer.");
}
}
C++ pointers
---- Understand the difference between pointers and references
The most significant differences are that a reference must always refer to an object, whereas a pointer can point to null, and what a reference refers to can't be changed after the reference is initialized.
---- Use pointers for "pass by reference" parameters and use const references for "pass by value" parameters
Global Data
Using Access Routines Instead of Global Data
---- You get centralized control over the data.
---- You can ensure that all references to the variable are barricaded.
---- You get the general benefits of information hiding automatically.
---- Access routines are easy to convert to an abstract data type.
How to Use Access Routines
Hide data in a class. Declare that data by using the static keyword or its equivalent to ensure only a single instance of the data exists. Write routines that let you look at the data and change it. Require code outside the class to use the access routines rather than working directly with the data.
if you have a global status variable g_globalStatus that describes your program's overall status, you can create two access routines: globalStatus.Get() and globalStatus.Set()
---- Require all code to go through the access routines for the data
---- Don't just throw all your global data into the same barrel
---- Build a level of abstraction into your access routines
---- Keep all accesses to the data at the same level of abstraction
node = node.next account = NextAccount( account )
employee = NextEmployee( employee )
event = EventQueue[ queueFront ] event = HighestPriorityEvent()
event = EventQueue[ queueBack ] event = LowestPriorityEvent()
eventCount = eventCount - 1 RemoveEvent( event )
=========================================================
1. General issues in using variables
A pompus[华而不实的] fraud [骗子] --- 绣花枕头
Following are guidelines for avoiding initialization problems,
---- Initialize each variable as it's declared
---- Initialize each variable close to where it's first used
---- Use const when possible
---- Pay speical attention to counters and accumulators, i, j, k, total
---- Initialize a class's member data in its constructor
---- Initialize working memory at the beginning of your program ,
0xCC is a good value to use because it's the machine code for a breakpoint interrupt(int 03h).
Scope
---- Keep variable "Live" for as short a time as possible
Persistence
Binding Time (In general, the later you make the binding time, the more flexibility you build into your code)
---- The earliest possible time at Code-Writing time
Title.Color = 0xFF;
---- A slightly later time at Compile time
const int TITLE_COLOR = 0xFF;
Title.Color = TITLE_COLOR;
---- The later time at Run time
Title.Color = ReadTitleColor();
Coding time (use of magic numbers)
Compile time (use of named constant)
Load time (reading a value from an external source such as Windows registry file)
Object instantiation time (reading value each time a window is created)
Just in time (reading value each time the window is drawn)
Using each variable for exactly one purpose
=========================================================
2. The power of variable names
Considering in choosing good names
---- Problem orientation, speak to the problem rather than the solution, express the 'what' more than 'how', refers to the problem domain rather than the computing universe.
---- Optium name length
---- the effect of scope on variable names, use qualifiers on names that are in the global namespace
---- computed-value qualifiers in variable names
Many programs have variables that contain computed values: totals, averages, maximums, and so on. If you modify a name with a qualifier like Total, Sum, Average, Max, Min, Record, String, or Pointer, put the modifier at the end of the name.
---- Common Opposites in variable names
Naming special types of data
---- Naming Loop Indexes, i,j,k, or more informative name.
---- Naming Status Variables, think of a better name than 'flag' for status variable, 'printerReady'.... or enum type
---- Naming temporary variables
---- Naming Boolean variables
Keep typical boolean names in mind
---- 'done', 'error','found', 'success','ok','ready'
IsFound, if (IsFound) --- if (found) would be better
---- Naming Enumerated types
C++ Color_Green, Color_Red....
C# Green, Red (Color.Green, Color.Red)...
---- Naming Constants , using named variable
The power of naming conventions
There are no hard-and-fast rules[金科玉律] for when you should establish a naming convention, but here are a few cases in which conventions are worthwhile:
---- When multiple programmers are working on a project
---- When you plan to turn a program over to another programmer for modifications and maintenance (which is nearly always)
---- When your programs are reviewed by other programmers in your organization
---- When your program is so large that you can't hold the whole thing in your brain at once and must think about it in pieces
---- When the program will be long-lived enough that you might put it aside for a few weeks or months before working on it again
---- When you have a lot of unusual terms that are common on a project and want to have standard terms or abbreviations to use in coding
Guidelines for a language-independent convention
---- Differentiate between variable names and routine names, begin with lower case/upper case: myName, MyName();
---- Differentiate between classes and objects
---- Identify global variables, g_RunningTotal, names with g_ prefix
---- Identify member variables, m_
---- Identify type definition, t_, t_char
---- Identify named consts, use all upper case letters, MAX
---- Identify elements of enumerated types, Color_
---- Identify input-only parameters in languages that don't enforce them, const &, *
---- Format names to enhance readability, Point_Total
....
C++ Constants, typedef, and preprocessor macros are in ALL_CAPS
=========================================================
3. Fundamental data types
Number in General
---- Avoid "magic numbers"
use hard-code 0 and 1 if you need to
---- Anticipate divide-by-zero errors
---- Make type conversions obvious
---- Avoid mixed-type comparsions
---- Heed your compiler's warnings
Integers[5intidVE]
---- Check for integer division 7/10 != 0.7, so 10 *(7/10) != 7 *******
---- check for integer overflow
---- Check for overflow in intermediate results
Float-point numbers
---- Avoid additions and subtractions on numbers that have greatly different magnitudes
1,000,000.00 + 0.1 probably produces an answer of 1,000,000.00
---- Avoid equality comparisons
const double ACCEPT_DELTA = 0.00001;
bool Equals(double termSource, double termTarget){
if (Math.abs(termSource - termTarget) < ACCEPT_DELTA )
return true;
else
return false;
}
---- Anticipate rounding errors (you multiply dollars by 100 ....)
Characters and Strings
---- Avoid magic charactes and strings
---- Strings in C
---- Be aware of the difference between string pointers and character arrays
StringPtr = "Some Text String";
In this case, "Some Text String" is a pointer to a literal text string and the assignment merely sets the pointer StringPtr to point to the text string. The assignment does not copy the contents to StringPtr.
---- Declare C-Style strings to have length CONSTANT + 1
char name[ NAME_LENGTH + 1 ] = { 0 };
for ( i = 0; i < NAME_LENGTH; i++ )
name[ i ] = 'A';
---- Intialize strings to null to avoid endless strings.
You can avoid endless strings in two ways. First, initialize arrays of characters to 0 when you declare them ---- char EventName[ MAX_NAME_LENGTH + 1 ] = { 0 };
Second, when you allocate strings dynamically, initialize them to 0 by using calloc() instead of malloc(). calloc() allocates memory and initializes it to 0. malloc() allocates memory without initializing it, so you take your chances when you use memory allocated by malloc().
---- Use arrays of characters instead of pointers in C
---- Use strncpy() instead of strcpy() to avoid endless strings.
strcpy() and strcmp() keep going until they run into a null terminator. Their safer companions, strncpy() and strncmp(), take a parameter for maximum length so that even if the strings go on forever, your function calls won't.
Boolean variables
---- Use boolean variables to simplify complicated tests to document your program
if ( ( elementIndex < 0 ) || ( MAX_ELEMENTS < elementIndex ) ||
( elementIndex == lastElementIndex )
) {
...
}
finished = ( ( elementIndex < 0 ) || ( MAX_ELEMENTS < elementIndex ) );
repeatedEntry = (elementIndex == lastElementIndex );
if ( finished || repeatedEntry ) {
...
}
Named Constants
---- Use named constants in data declarations
---- Avoid literals, even 'safe' ones
For i = 1 To 12
For month = Month_January To Month_December
Arrays
---- In C, use the ARRAY_LENGTH(x) macro to work with arrays
#define ARRAY_LENGTH(x) (sizeof(x)/sizeof(x[0]))
Creating your own types (type aliasing)
typedef float Coordinate;
---- Avoid predefined types
---- Don't redefine a predefined type
---- Define substitue types for portability
define a type INT32 and use it instead of int, or a type LONG64 instead of long
---- Consider creating a class rather than using a typedef
=========================================================
4. Unusual data types
Structures
---- use structures to clarify data relationships
---- use structures to simplify operations on blocks of data
---- use structures to simplify parameter lists
---- use structures to reduce maintenance
Pointers
The knowledge of how to interpret the contents of a location in memory is provided by the base type of the pointer. If a pointer points to an integer, what that really means is that the compiler interprets the memory location given by the pointer as an integer.
General tips on pointers
---- lsolate pointer operations in routines or classes
---- Declare and define pointers at the same time
---- Delete pointers at the same scoping level as they were allocated
---- Check pointers before using them
---- Check the variable reference by the pointer before using it
---- Usd dog-tag fields to check for corrupted memory
---- Use extra pointer variable for clarity to simplify complicated pointer expressions
startNode->next->previous
Node *followingNode = startNode->next
---- Shred your garbage/Set pointers to null after deleting or freeing them/Check for bad pointers before deleting a variable
ASSERT(pointer != NULL, "Attempting to delete null pointer.");
memset(pointer, GARBAGE_DATA, MemoryBlockSize(pointer));
delete pointer;
pointer = NULL;
---- Keep track of pointer allocations
IsPointerInList(pointer)
---- Write cover routines to centralize your strategy to avoiding pointer problems











C++ pointers
---- Understand the difference between pointers and references
The most significant differences are that a reference must always refer to an object, whereas a pointer can point to null, and what a reference refers to can't be changed after the reference is initialized.
---- Use pointers for "pass by reference" parameters and use const references for "pass by value" parameters
Global Data
Using Access Routines Instead of Global Data
---- You get centralized control over the data.
---- You can ensure that all references to the variable are barricaded.
---- You get the general benefits of information hiding automatically.
---- Access routines are easy to convert to an abstract data type.
How to Use Access Routines
Hide data in a class. Declare that data by using the static keyword or its equivalent to ensure only a single instance of the data exists. Write routines that let you look at the data and change it. Require code outside the class to use the access routines rather than working directly with the data.
if you have a global status variable g_globalStatus that describes your program's overall status, you can create two access routines: globalStatus.Get() and globalStatus.Set()
---- Require all code to go through the access routines for the data
---- Don't just throw all your global data into the same barrel
---- Build a level of abstraction into your access routines
---- Keep all accesses to the data at the same level of abstraction
node = node.next account = NextAccount( account )
employee = NextEmployee( employee )
event = EventQueue[ queueFront ] event = HighestPriorityEvent()
event = EventQueue[ queueBack ] event = LowestPriorityEvent()
eventCount = eventCount - 1 RemoveEvent( event )