Вы находитесь на странице: 1из 14

Programming Techniques – II

Programming Style

Objectives

At the end of this session, the participants will be able to follow programming style with respect to:
Naming
Expressions and statements
Consistency
Magic numbers
Writing clear code
Indenting code
Commenting

Coding Style

Why worry about a coding style when a program works?


The answer is that well-written code is easy to understand and typically has fewer bugs. Due to project
pressures it is easy to push style aside and deliver sloppy code that works…well, sometimes!
This is precisely the reason why good style should be a matter of habit.

Naming

Naming

The naming scheme is one of the most influential aids to understanding the logical flow of an application
A name should tell "what" rather than "how"

Use Descriptive Names for Globals

char empName [30];


int speed = 0; // Speed of the vehicle
int noBoysInDivision [10]; // total number of boys in // the class
int port2addr = 0x02F8; // Address of the port of // server to be connected
struct EmpNode {
char* name;
int no;
struct EmpNode* pnext};

Descriptive names emphasize on the meaning of global variables which are widely used in a program.

Use Short Names for Locals

for (theElementIndex = 0; theElementIndex < numberOfElements; theElementIndex++)


elementArray[theElementIndex] = theElementIndex;
Compare above code with the following equivalent code
for (i=0; i < nelems; i++)
elem[i] = i;

Naming Constants

Use capital letters and underscore character for naming constant


e.g.
const int MAX_TEMPERATURE = 38;
const int MIN_TEMPERATURE = 20;

Naming enum

Labels All Upper Case with underscore Separators


e.g.
enum SwitchState { SWITCH_OFF, SWITCH_ON };

Naming Variables

Since most names are constructed by concatenating several words, use mixed-case formatting to
simplify reading them.
Use Pascal casing (CalculateInvoiceTotal) for routine names where the first letter of each word is
capitalized.
For variable names, use camel casing (documentFormatType) where the first letter of each word
except the first is capitalized.
Boolean variable names should contain Is which implies Yes/No or True/False values, such as
fileIsFound.
Avoid using terms such as Flag when naming status variables, which differ from Boolean variables in
that they may have more than two possible values. Instead of documentFlag, use a more descriptive
name such as documentFormatType.

Naming Variables

Use a meaningful name even for a short-lived variable that may appear in only a few lines of code. Use
single-letter variable names such as i, or j for short-loop indexes only.
Do not use literal numbers or literal strings, such as For i = 1 To 7. Instead, use named constants, such
as For i = 1 To NUM_DAYS_IN_WEEK for ease of maintenance and understanding.

Recommendations

The length of the variable names should depend on the context


Clarity is often achieved through simplicity
Normally global variables start with initial letters capital and constants have all letters capital
Specific rules are less important than consistent adherence to a sensible convention
Good naming conventions make your code easier to understand
Side effect is that you coin good names by following consistent strategy

Be Consistent

struct UserQueue
{
int noOfItemsInQ;
int frontOfTheQueue;
int queueCapacity;
}

Observations

The word “queue” appears as Q, Queue and queue


Structure members will be accessed in an awkward way such as
queue.queueCapacity //queue word redundant
This structure definition needs to be improved

New Structure Definition

struct UserQueue
{
int nitems;
int front;
int capacity;
}
New structure definition is easier to understand
Structure members can be accessed in an elegant way such as:
queue.capacity++;
Users and items (structure members) in this example mean the same thing. Hence, this definition can be
improved further

New Definition

struct UserQueue
{
int nusers;
int front;
int capacity;
}
This new definition is simple and readable.
If you think that your code is sufficiently simple and readable, move ahead. Do not over-engineer it.

Naming Function Names

The names should be a concatenation of words with in principle one word to indicate the action (verb)
plus others for the objects (names). Each word is capitalized (all cases lower except the first one which
should be uppercase) but the first word is all lowercase.
e.g. setNextSubscanDocument

Naming Function Names

Avoid elusive names that are open to subjective interpretation, such as AnalyzeThis(). Such names
contribute to ambiguity more than abstraction.
In object-oriented languages, it is redundant to include class names in the name of class properties,
such as Book.BookTitle. Instead, use Book.Title.
Use the verb-noun method for naming routines that perform some operation on a given object, such as
CalculateInvoiceTotal().
In languages that permit function overloading, all overloads should perform a similar function.

Naming Function Names

Functions returning boolean values should be named properly. e.g.


if(checkLower(c))
……;
Do you understand the return value of this function returning a Boolean value?
if (islower(c)) // or isLower(c) if not using a
// a standard function
This resolves ambiguity

Avoid Meaningless Names

Function names such as HandleCalculations(), DealWithOutput(), HandleOutput() should be avoided.


They don’t tell you what these functions do.
Compare these names with following names: getNextCustomerId(), isPrinterReady(). These names are
cleaner.
e.g. printOutput() is a better replacement for HandleOutput().

Difficulty Inventing Function Name

If you can’t think of a good name for a function, have a deeper look at the function. The function may not
have a good purpose, or maybe it can be split into two separate functions that make code more
readable.

Be Accurate

The name of a variable or function conveys valuable information to the user. Misleading names result in
confusion and further in creating bugs.
public boolean inTable(Object obj)
{
int j = this.getIndex(obj);
return (j == NotFound)
}
What is this function doing?

Be Accurate

The function getIndex returns a value between 0 and nTable-1, if it finds the object in the table and
returns “NotFound” if it cannot.
However, when we compare that return value with “NotFound”, the check returns 1 if it succeeds. So
you have a function that returns 1 (success), when the object is not found in the table, which is
misleading.

Be Accurate

What will you do?


One option is to negate the check or use “!=“ operator in place of “==“.
However, it would be better if we rewrite the code, with proper “if-else” condition and make it more
readable.

Handling Conditional Expressions

Use the Natural Form for Conditional Expressions

Conditional expressions that include negations are difficult to understand. Here is an example:

if (!(block_id < act_blocks) || !(block_id >= unblocks))


……;

here is how this condition will look after eliminating “!”


If ((block_id >= actblks) || (block_id < unblocks))
……;

This expression is easy to understand.

Parenthesize to Resolve Ambiguity


If (x&MASK == BITS)
……;
There is certainly no issue here with the formatting. However, the operator precedence is different from
whatever is intended.
The above code actually means
If (x & (MASK == BITS))
……;
This is certainly not what programmer wants
The best practice here is to put parenthesis and make the meaning explicit
If ((x&MASK) == BITS)
……;

Parenthesize to Resolve Ambiguity

A typical problem may arise while handling the ternary conditional operator in C.
leap_year = y %4 == 0 && y % 100 != 0 || y % 400 == 0;
Could you notice the bug in this piece of code?

Parenthesize to Resolve Ambiguity

This code, in fact, does not have a bug. However, it is hard to convince yourself that the code will work.
A better practice will be to use the parentheses.

leap_year = ((y%4 == 0) && (y%100 != 0)) || (y%400 == 0);

This code certainly looks more convincing!

Handling Complex Expressions

Handling Complex Expressions

It is easy to get carried away by the rich set of operators and syntax that modern languages such as
Java, C, C++ provide. There is a tendency of cramming everything into one single statement.
Another argument that is often made in the support this tendency is code optimization.
We must remember that there is no direct relation between the size of source and the assembly or the
object code that is generated by the compiler.

Break up Complex Expressions

*x += (*xp=(2*k < (n-m) ? c[k+1] : d[k--]));


This code is probably comprehensible only to the programmer who wrote it. Moreover, this may become
incomprehensible to her after few days or even after few hours!
A better approach would be to split it in separate steps.
if (2*k < (n-m))
*xp = c[k+1];
else
*xp = d[k--];
*x += *xp;

Break up Complex Expressions

Please remember that programs are primarily written for programmers and not for computers!!

Writing Clear Code

Be Clear

Clarity is often achieved by writing shorter code, however a clearer code may sometimes be longer. We
must remember that the final objective is to make a programmer understand the code.

Be Careful with Side-effects

Following two snippets of code are not equivalent.


1. str[i++] = str[i++] = ‘\n’;
2. str[i++] = ‘\n’;
str[i++] = ‘\n’;
Depending on when “i” is accessed and updated “i” might end up getting incremented only ONCE!! This
happens because order of evaluation of operands of expression is undefined in C and C++.

Be Careful with Side-effects

Another example,
n = (getchar() << 8) | getchar();
Now, here since the order of evaluation is undefined, second getchar() maybe invoked first.
Similarly,
arr[count] = ++count;
might assign the incremented count to wrong array element.

Be Careful with Side-effects

What is wrong with the following code?


int readcoordinate (int *i) {
scanf(“%d”, i);
return *i;
}
insert(graph, readcoordinate(x); readcoordinate(y));
Exercises

Improve following fragments of the code.


if (!(ch == ‘y’ || ch == ‘Y’))
……;
if((i = getchar()) < (j = getchar()) )
……;
len = (len < MAX) ? len : MAX;

Indenting Code

Indent to Show Structure

While writing programs, pay attention to the formatting. Neatly formatted programs make them more
readable, and help you locate bugs easily.
for(n=0;n<20;arr[n++]=‘\0’);
*ptr = ‘\0’; return(‘\n’);
This confusing piece of code can easily be converted into this more readable form.
for (n = 0; n < 20; n++)
arr[n] = ‘\0’;
*ptr = ‘\0’;
return ‘\n’;

Use Consistent Indentation and Braces

Whatever your style of indentation be consistent with it


Most of the modern syntax-directed editors will allow you to follow a consistent style of your choice
Use braces to resolve ambiguity even when they are not needed by the compiler

Use Consistent Indentation and Braces

if (month == FEB) {
if (year%4 == 0)
if (day > 29)
return FALSE;
else
if (day > 28)
return FALSE;
}

This code apparently looks correct as it is well indented. However the indentation is misleading. The
problem is that a compiler will attach the “else” part to third “if” and not the second as you would have
expected.
Note

If you are maintaining a program written by someone else, please follow the style of indentation and
coding followed by the programmer who had written the program. That will keep the style of the program
consistent.

Please remember it is more important to have a consistent style rather than any particular style.

Use Conventional Ways for Consistency

Modern programming languages will allow you to write your code in multiple ways so that all forms are
semantically equivalent. However, more standard ways would make your code more readable to you
and your colleagues.
Another advantage of using idioms is that they allow you to catch nonstandard constructs - origin of
bugs – quickly
Here are few examples on how you should write certain constructs.

Examples

An array will typically be initialized in following way.


for (i = 0; i < n; i++) // “i = 0” can be
arr[i] = 0; // “int i = 0” in Java and C++
There are multiple ways in which you can use “for” and “while” to initialize an array. However, the above
form is most popular.
A typical infinite loop will be written either as for (;;) or as while (1);

if-else and Switch

Use else-ifs for Multi-way Decisions

if (argc == 3)
if((fin = fopen(argv[1], “r”)) != NULL)
if ((fout = fopen(argv[2], “w”)) != NULL) {
while ((c = getc(fin)) != EOF)
putc(c, fout);
fclose(fin); fclose(fout);
}
else
printf(“Can’t open output file %s\n”, argv[2]);
else
printf(“Can’t open input file %s\n”, argv[1]);
else printf(“Usage: cp inputfile outputfile\n”);

Use else-ifs for Multi-way Decisions


The code that contains “nested ifs” is often difficult to understand. The above example contains few “ifs”,
but what we really need are “else-ifs” since “only one” action will be performed.
Rewriting the code using “else-ifs” makes it clearer.

Use else-ifs for Multi-way Decisions

if (argc != 3)
printf(“Usage: cp inputfile outputfile”);
else if ((fin = fopen(argv[1], “r”)) == NULL)
printf(“Can’t open input file %s\n”, argv[1]);
else if ((fout = fopen(argv[2], “w”)) == NULL)
{
printf(“Can’t open output file %s\n”, argv[2]);
fclose(fin);
}
else
{
while ((c = getc(fin)) != EOF)
putc(c, fout);
fclose(fin);
fclose(fout);
}

Be Careful with “Switch”

Switch statement allows you to have a fall-through which is sometimes done intentionally to reuse the
code. At other times it is done unintentionally which gives rise to bugs.
Code with “switch” should always be written without fall-through. In case any of the statement has a fall-
through it should be clearly commented.

Example

switch (c) {
case ‘-’ :
sign = -1; // Fall-through
case ‘+’ :
c = getchar();
break;
case ‘.’ :
break;
default:
if (!isdigit(c))
return 0;
break;
}

Example

This code can better be written with if-else conditions.


Fall-through is acceptable when several cases have identical code e.g.
case ‘0’:
case ‘1’:
case ‘2’:

break;

Be Careful with Magic Numbers

Give Names to Magic Numbers

Magic numbers are literal numeric values in programs. Presence of magic numbers makes program
hard to understand and maintain.
for (j = k; j < 22; j++)
draw(j, col, star);
draw(23, 2, ‘ ‘); //label x axis;
This program can very well be converted to following program which is semantically equivalent, but
much easier to understand.

Give Names to Magic Numbers

enum {
MINROW = 1, /* top edge */
MAXROW = 24; /* bottom edge (<=24)*/
MINCOL = 1; /* left edge */
MAXCOL = 80; /* right edge */
LABELROW = 1; /* position of labels */
NLET = 26; /* size of alphabet */
HEIGHT = MAXROW – 4; /* height of bars */
WIDTH = (MAXCOL – 1)/NLET /* width of bars */
};

for ( j = HEIGHT – let[i]/fac; j < HEIGHT; j++)


draw(j + 1 + LABELROW, (i + 1) * WIDTH, ‘*’);
draw(MAXROW – 1, MINCOL + 1, ‘ ‘);

Comments

Comments
The sole purpose of comments is to enhance the readability of code. It is important to remember that
comments are needed only at the parts of the program that are not readily understood.
Commenting should never be done mechanically.
Sloppy code combined with comments can never be a substitute for a neatly written code. Remember
that good code needs fewer comments than bad code.

Do not Restate the Obvious

It’s not unusual to find following snippets of code in the program.


return SUCCESS; // return success
default: // Default case
return;
If (islower(c)) // Check if character is lowercase
/*-----------------------------------*/
/*Definition of the local C variables*/
/*-----------------------------------*/
int ContArr[100];
int DivElem[20][20];

Comment Functions and Global Data

const string OLD_PROT_PATH("PROT");


// Protection path for old circuit
int pingAllSessions();
// Pings all registered sessions.
// If ping fails a re-open session command is
// placed on session's queue.
// Returns total of failed pings
//random: return an integer in the
//range 0 to (n-1)
int random(int n)
{
return (int)(Math.floor(Math.random()*n));
}

Do not Comment Bad Code, Rewrite It

/* Result 0 indicates match was found else not found */


return (!result);
This should be rewritten as
return matchfound;
Do not Contradict the Code

Comments are often not changed as program evolves. This results into comments not in agreement with
the code.
/* Read input and write newline at the end to indicate end of current input */
For((c = getchar()) != EOF; c != EOF; i++)
str[i] = c;

Do not Confuse

int strcmp(char *s1, char*s2);


/* strcmp function returns –1 if s1 is above s2 in an ascending order list, 0 if equal and 1 if s1 below s2 */
This comments can very well be replaced by following simpler comment
/* strcmp returns –1 if s1<s2, 0 if equal, 1 if s1>s2 */

Don’t Comment Excessively

if (ban_rowcount > 0)
{
/* This BAN has some future requests but they couldn't be copied to the output. Mark it as to be
procesed in the next transaction. */
SET_C_YESNOIND_NO_INDICATOR(iov_pBansBuffer->ban_buffer[indx].ban_more_rows);
SET_C_CSMREQSTS_TO_BE_PROCESSED(iov_pBansBuffer->ban_buffer[indx].ban_request_status);
if (IS_C_BOOLIND_CTRUE(moreFlag))
{
/* moreFlag indicates that despite the fact that all the BANs future requests were retrieved earlier, we
couldn't copy them to the output buffer because there was not enough space in the output buffer */
DEBUG_OL_0("moreFlag is TRUE \n");
(ov_pFutureReqBuffer->moreRows) = 1;
} /* if moreFlag */
} /* if ban_rowcount > 0 */

Commented Code

/* if (fread(&record,sizeof(record),1,in_file) != 1)*/
if (fread(&record,sizeof(record),1,in_file) != 1UL)
……;
It is quite clear from the comment that the code was altered to fix a bug. But then that gives rise to
several questions.
Why was this code commented?
Why was it not working earlier?
What made this code work now?
Is bug fixed or is it still there?
Better practice would be to delete commented code.

Good Programming Practices - Summary

Comment the program before writing it


All programs should include a comment block at or near the beginning of the program
Avoid obscure programming language constructs and reliance on language-specific precedence rules
Be consistent! Stick to a method for variable naming or indenting throughout your program
Each subroutine should have one clearly defined task
Subroutines should be short enough so that reader can grasp their meaning as a unit
Every subroutine should be preceded by a comment describing its purpose and use

Good Programming Practices - Summary

keep the number of parameters to any one subroutine at a minimum


Limit the number of parameters passed by reference
Functions should affect precisely one value - the function's return value
A new level of indentation should be used at every level of statement nesting in the program
Do not put more than one statement on the same line
Pick identifiers (i.e. names for variables, functions, records, etc.) that communicate the named object's
purpose to the reader
Never change the value of a loop variable within that loop

Good Programming Practices - Summary

Use variables at the smallest possible level of scope


Be consistent about the use of local variables with the same name throughout the program
Give a comment to every variable in the program, describing its use
Carefully consider every number that is typed in the code, and substitute a name for it (unless you have
a reason not to do so)
If go to statement is used in the program, comment the reason thoroughly

Summary

Simplicity and clarity


Generality
Evolution
Interfaces
Automation

Вам также может понравиться