justlinux.com
Mon, 13-Feb-2012 12:35:05 GMT

Forum: Registered Users: 75964, Online: 360
nhfs Here you can view your subscribed threads, work with private messages and edit your profile and preferences Registration is free! Calendar Find other members Frequently Asked Questions Search Home Home

Help File Library: Good Programming Practice: Version 1.0


Written By:X_console
shellscope@yahoo.com

Introduction

This is a guide to learning how to write good programs. I'm not going to teach you a programming language, rather I'm going to teach you how to do proper programming so that you can write good code, good programs and easy to maintain code. Obviously I'm assuming that you're familiar or learning at least one programming language. By the way, when I say programming language, I also refer to scripting languages. In this document, I will be doing examples in C and all source code is compiled with egcs-2.91.66.

Terminating statements

The most common programming error is a novice programmer forgetting to put a semi-colon to terminate a statement. The errors you get from doing this are sometimes so cryptic it befuddles a novice programmer. Always be sure to check each line of your code to see if you've properly terminated it. Granted that not all languages require a terminating semi-colon. Here's an example of forgetting a semi-colon:

int main(void)
{
        /* no semi-colon! expect an error! */
        printf("Hello World!\n")
        return(0);
}

You'd be surprised how many people make this mistake. Sure, that's a small program... but wait 'till you write code that's 1000 lines in length! You can bet that you missed out a semi-colon somewhere! To help you remember the semi-colon, think of it as a period when you write a sentence in English. Make it a habit.

Now the other thing about semi-colons is that some people aren't sure where to put them, so they put them everywhere. Bad idea. Now some of you experienced programmers are probably laughing, but I've seen this happen in the school I go to. People sending emails wondering why their program doesn't work, and it's all thanks to the semi-colon. An example of this:


/* semi-colon after main() is wrong: */
int main(int argc, char *argv[]);
{
        printf("Hello World");
        return(0);
}

You do not put a semi-colon when you're starting the block of a function or method, or procedure. This is wrong.

White Space

In C, white space is ignored. That means you can write surprisingly obfuscated code by ignoring white space yourself. Let's take a look at a C program that ignores white space:

int main(void){printf("HelloWorld");return(0);}

How's that for conciseness? Obviously the above is a little exagerated, but you'll most commonly come across code like the following:


if(x==0) {a=b=c=d=MAX; x++;}

Yes it saves space, but it's damn confusing for anyone who looks at your code, and for you when you look at it one month later. Write it the right and clean way!


if(x == 0)
{
        a = b = c = d = MAX;
        x++;
}

See how much better it is? White space was meant to make code look clear. The computer doesn't care about white space because it can't read your code. People need white space to read your code.

Braces and Blocks

Braces and blocks vary depending on the programmer. This can cause a little confusion sometimes when source code passes from one programmer to another. For instance the K & R style of looks like this:

int main()
{
        int x = 1;
        int y = 10;
        while(x < y ){
                printf("Value of x is %d\n", x);
                x++;
        }
}

This is how a lot of C programmers write their code. Some programmers prefer to do their braces this way:


int main()
{
        int x = 1;
        int y = 10;
        while(x < y)
        {
                printf("Value of x is %d\n", x);
                x++;
        }
}

I prefer the latter method because I can see where the block starts and ends clearly. The K & R programmers will of course tend to disagree. The only solution to this is to get used to both styles of indenting, but when writing a program, be consistent with what you use. If you use K & R, then use K & R for the program. Don't suddenly change it halfway through.

Abusing "if"

Some people love using "if" statements so much they come up with the following:

if(a == 0)
{
        a++;
        return(a);
}
if(a == 1)
{
        a += 5;
        return(a);
}
if(a == 2)
{
        a += 10;
        return(a);
}
if(a == 3)
{
        a += 20;
        return(a);
}
if(a == 4)
        exit(1);

Was there a better way of writing this? You bet. Use the "switch" case statement instead. Switch is neat:


switch(a)
{
        case 0: a++; 
                return(a);

        case 1: a += 5;
                return(a);

        case 2: a += 10;
                return(a);

        case 3: a += 20;
                return(a);

        default: exit(1);
}

Here we see the various cases and what they do depending on the value of "a". When a no-match is found, it automatically goes to the default action, which in this case is to exit.

Abusing Nesting

Now we're going to talk about how people abuse nesting. It is considered bad programming practice to have more than three levels of nesting. Why? It causes confusion, and the code is hard to read. Here's an example in:
int a = 10;
int b = 20;
int c = 30;
int d = 40;

if(a == 10)
{
a = a + d;
if(b == 20)
{
b = b + a;
if(c != b)
{
c = c + 1;
if(d > (a + b))
printf("Made it all the way to the bottom!\n");
}
}
}

Obviously this is exaggerating a little, but you'd be surprised how some people actually do this. So how do we fix it? One way, is perhaps to break it up with functions:


void next(int a, int b, int c, int d)
{
if(c != b)
{
        c = c + 1;
        if(d > (a + b))
        printf("Made it all the way to the bottom!\n");
        }
}       

int main()
{
        int a = 10; 
        int b = 20; 
        int c = 30; 
        int d = 40;

        if(a == 10)
        {
                a = a + d;
                if(b == 20)
                {
                        b = b + a;
                        next(a, b, c, d);
                }
        }
        return(0);
}

Okay, this took a bit more work, but now your code is structured and easier to read. Sometimes it takes a little bit more code in order to make the overall code easier to understand. Don't be lazy.

Commenting Code

Commenting code is good programming practice. Always comment statements or variables that you think people will have trouble understanding:

int x = 100;
int y = 1000;

if(x < y)
        a = 0;
else
        a = 1;

Can you tell what this program does? What it's purpose is? No. Not without knowing what the variables x, y, and a represent. Now let's comment it: /*
* a program that checks for profit and loss:
*/
int x = 100;
/* x is the total amount of money gained from selling books */
int y = 1000
/* y is the cost spent to manufacture the books */
int a;
/* determines whether we made a profit or loss */
/* check to see if x is less than y: */
if(x < y)
/* we made a loss. let the number 1 stand for loss:
a = 1;
else
/* we made a profit. let the number 0 stand for profit */
a = 0;

Now even if you don't program in C, you already know what this program does. You have an idea of what each statement does, and you know what each variable stands for. I said that commenting is good. But over commenting your code is bad. You use comments to make certain code easier to understand, but you don't use comments to tell a story:


int profit = 1; /* profit is equal to 1 */
int loss = 0;   /* loss is equal to 0 */

/* if profit equals to 1: */
if(profit == 1)
        /* print "We made a profit!" */
        printf("We made a profit!\n");

/* else: */
else
        /* print "We made a loss!" */
        printlf("We made a loss!\n");

This is an amazing waste of time. You're telling the programmer the obvious. Commenting is best used when you're declaring variables, and when you're performing some operation on variables. You need to tell the programmer what each variable represents, and what happens to each variable. You need to tell the programmer what your program does, and how it does what it does. You need to tell the programmer what each function, method, procedure, and error code does. The programmer does not want to guess.

Naming

Always try to name your variables, functions, methods, procedures, unions, structures, and classes by what it is they represent. This means that naming your variables "x", "y", and "z" is a bad idea. Now you're probably screaming "HYPOCRITE", but hang on. The reason I used one letter variables is because I'm just doing examples. When I write actual programs, I use meaningful names. So. Let's take a look at this code:

void x(int a, int b)
{
        int z;
        z = a + b;
        printf("z is %d\n", z);
}

Here we have an idea of what procedure x does, but we don't know what it represents. We also do not know what a, b, and z represent. Let's modify it a litte:


void sum_of_ages(int jacks_age, int jills_age)
{
        int total_age;
        total_age = jacks_age + jills_age;
        print("total_age is %d\n", total_age);
}

Now here, the code is uncommented, but because of the way you've named the variables, anyone who looks at your code, can get a general idea of what you're trying to do here. We know what each variable represents and we know for sure what the function's supposed to do. A program with well named functions and variables might not need a lot of commenting.

Checking The Buffer

This is important. When you're writing code, make sure to always check for buffer overflows. Make sure the data that's being put into your array does not exceed the amount of data the array can hold. A lot of programs fail to do this and they are often the cause of many system break-ins because a programmer can use a buffer overflow to run priveleged programs. Let's look at a C program that takes input from a user:

char city[10];
/* array that holds the name of a city */ printf("Enter a city name: "); scanf("%s", city); printf("City is %s\n", city);

Now this assumes that the city name is 9 elements long (the 10th element is the terminating null character: \0). So what happens when the user enters a name longer than 9 characters? Nothing might happen, or the program might crash, or overwrite data beyond the buffer. Either way, don't risk it. Check the input length:


char city[10];
/* array that holds the name of a city */ printf("Enter a city name: "); fgets(city, sizeof(city), stdin); printf("City is %s\n", city);

What happens here is that if the user enters more than what the buffer can hold, the extra input is truncated off.

Never Trust The User

This is an important rule. Do not trust the people who will be using your programs. Treat them as people who are looking for ways to crash your program, so make sure you have a counter attack to their actions. For instance, the program we did above that was succeptible to a buffer overflow, we countered it by checking the length of the string input by the user. When using languages like C which require you to specify data types, make sure you check that the data type the user inputs is the data type that you're using. Otherwise, your program could very well crash. The bottom line is that you implement error checking. Use your debugger to monitor what goes on when your program runs, for instance, what happens to the variables. I know that error checking takes a lot of time, but if you don't want your program to crash, it's the only way.

Conclusion

Well that's it for now. As soon as I think up of more stuff to write, I'll update this document. In the meantime, try to practice good programming. It really helps in the long run, especially when you need to maintain code, and it makes it easy for your fellow programmers to read what you write. If you find any mistakes in this article, or have any constructive criticisms, I welcome your feedback.

X_console
shellscope@yahoo.com