Open nixawk opened 7 years ago
When declaring variables of the same type, declare each on a separate line unless the variables are self-explanatory and related, for example:
int year, month, day;
Add a brief comment to variable declarations:
int x; /* comment */
int y; /* comment */
Group related variables. Place unrelated variables, even of the same type, on separate lines.
int x, y, z;
int year, month, day;
When defining constants, capitalize constant names and include comments. In constant definitions, align the various components, as shown in the examples below. In ANSI C, there are several ways to specify constants: const modifier, #define command, and enumeration data types.
Use the const modifier as follows:
const int SIZE 32; /* size in inches */
const int SIZE 16 + 16; /* both evaluate to the number 32 */
The #define preprocessor command instructs the preprocessor to replace subsequent instances of the identifier with the given string of tokens. It takes the form:
#define IDENTIFIER token-string
In general, avoid hard-coding numerical constants and array boundaries. Assign each a meaningful name and a permanent value using #define. This makes maintenance of large and evolving programs easier because constant values can be changed uniformly by changing the #define and recompiling.
#define NULL 0
#define EOS '\0'
#define FALSE 0
#define TRUE 1
Using constant macros is a convenient technique for defining constants. They not only improve readability, but also provide a mechanism to avoid hard-coding numbers.
Enumeration types create an association between constant names and their values. Using this method (as an alternative to #define), constant values can be generated, or you can assign the values. Place one variable identifier per line and use aligned braces and indentation to improve readability. In the example below showing generated values, low would be assigned 0, middle 1, and high 2. When you assign values yourself, align the values in the same column, as shown in the second example.
Example: generated values
enum position
{
LOW,
MIDDLE,
HIGH
};
Example: assigned values
enum stack_operation_result
{
FULL = -2,
BAD_STACK = -1,
OKAY = 0,
NOT_EMPTY = 0,
EMPTY = 1
};
Use the const modifier instead of the #define preprocessor to define simple constants. This is preferable because #define cannot be used to pass the address of a number to a function and because #define tells the preprocessor to substitute a token string for an identifier, which can lead to mistakes (as illustrated in the example below).
Example: using #define
#define SIZE 10 + 10 /* 10 + 10 will be substituted for SIZE */
...
area = SIZE * SIZE; /* this evaluates to 10 + 10 * 10 + 10 */
/* which is 10 + (10 * 10) + 10 = 120 */
Example: using the const modifier
const int SIZE = 10 + 10; /* SIZE evaluates to the number 20 */
...
area = SIZE * SIZE; /* this evaluates to 20 * 20 = 400 */
Floating point numbers should have at least one number on each side of the decimal point:
0.5 5.0 1.0e+33
Start hexadecimal numbers with 0x (zero, lower-case x) and upper case A-F:
0x123 0xFFF
End long constants in upper-case L:
123L
Always associate qualifiers (e.g., short, long, unsigned) with their basic data types:
short int x;
long int y;
unsigned int z;
The use of structures is one of the most important features of C. Structures enhance the logical organization of your code, offer consistent addressing, and will generally significantly increase the efficiency and performance of your programs.
Using common structures to define common elements allows the program to evolve (by adding another element to the structure, for example), and lets you modify storage allocation. For example, if your program processes symbols where each symbol has a name, type, flags, and an associated value, you do not need to define separate vectors.
Example: structures
typedef struct symbol
{
char *name;
int type;
int flags;
int value;
} symbol_type;
symbol_type symbol_table[NSYMB];
An automatic variable can be initialized either where it is declared or just before it is used. If the variable is going to be used close to where it is declared (i.e., less than one page later), then initialize it where it is declared. However, if the variable will be used several pages from where it is declared, then it is better practice to initialize it just before it is used.
Example: variable initialized where declared
int max = 0;
/* use of max is within a page of where it is declared */
for (i=0; i<n; i++)
if (vec[i] > max)
max = vec[i];
Example: variable initialized where used
Use an assignment statement just before the for loop:
int max;
...
/* several pages between declaration and use */
...
max = 0;
for (i=0 ; i<n ; i++)
if (vec[i] > max)
max = vec[i];
Or use the comma operator within the for loop:
int max;
...
/* several pages between declaration and use */
...
for (max = 0, i=0; i<n; i++)
if (vec[i] > max)
max = vec[i];
Type conversions occur by default when different types are mixed in an arithmetic expression or across an assignment operator. Use the cast operator to make type conversions explicit rather than implicit.
Example: explicit type conversion (recommended)
float f;
int i;
...
f = (int) i;
Example: implicit type conversion
float f;
int i;
...
f = i;
Explicitly declare pointer entities (variables, function return values, and constants) with pointer type. Put the pointer qualifier (*) with the variable name rather than with the type.
Example: pointer declaration
char *s, *t, *u;
Programs should not contain pointer conversions, except for the following:
p->m s.m a[i]
exp(2, x)
!p ~b ++i -n *p &x
(long) m
c1 = c2
z = (a > b) ? a : b;
strncat(t, s, n)
for (i = 0; i < n; ++i)
x + y a < b && b < c
printf(fmt, a+1)
if ((a < b) && (c==d)) ...
If a is not < b, the compiler knows the entire expression is false so (c == d) is never evaluated. In this case, (c == d) is just a test/relational expression, so there is no problem. However, if the code is:
if ((a < b) && (c==d++))
d will only be incremented when (a < b) because of the same compiler efficiency demonstrated in the first example.
CAUTION: Avoid using side-effect operators within relational expressions. Even if the operators do what the author intended, subsequent reusers may question what the desired side-effect was.
for (i = 0, j = 1; i < 5; i++, j++);
Use parentheses liberally to indicate the precedence of operators. This is especially true when mask operators (&, |, and ^) are combined with shifts.
Split a string of conditional operators that will not fit on one line onto separate lines, breaking after the logical operators:
if (p->next == NULL &&
(total_count < needed) &&
(needed <= MAX_ALLOT) &&
(server_active(current_input)))
{
statement_1;
statement_2;
statement_n;
}
C is an expression language. In C, an assignment statement such as “a = b” itself has a value that can be embedded in a larger context. We recommend that you use this feature very sparingly. The following example shows a standard C idiom with which most C programmers are familiar.
Example: embedded assignments
while ((c = getchar()) != EOF)
{
statement_1;
statement_2;
statement_n;
}
Try to avoid assignments inside if-conditions (assignments inside while-conditions are ok). For example, don’t write this:
if ((foo = (char *) malloc (sizeof *foo)) == NULL)
fatal ("virtual memory exhausted");
instead, write this:
foo = (char *) malloc (sizeof *foo);
if (foo == NULL)
fatal ("virtual memory exhausted");
However, do not overdo embedding of multiple assignments (or other side-effects) in a statement. Consider the tradeoff between increased speed and decreased maintainability that results when embedded statements are used in artificial places.
Example: nonembedded statements
total = get_total ();
if (total == 10)
printf(“goal achieved\n”);
Example: embedded statements (not recommended)
if ((total = get_total() == 10)
printf(“goal achieved\n”)
In C, conditional expressions allow you to evaluate expressions and assign results in a shorthand way. For example, the following if then else statement
if (a > b)
z = a;
else
z = b;
could be expressed using a conditional expression as follows:
z = (a > b) ? a : b; /* z = max(a, b) */
While some conditional expressions seem very natural, others do not, and we generally recommend against using them. The following expression, for example, is not as readable as the one above and would not be as easy to maintain:
c = (a == b) ? d + f(a) : f(b) - d;
Do not use conditional expressions if you can easily express the algorithm in a more clear, understandable manner. If you do use conditional expressions, use comments to aid the reader’s understanding.
Rather than trying to memorize the rules or look them up every time you need them, remember these simple guidelines from Steve Oualline’s C Elements of Style:
There are several general guidelines to follow when working with types: