An important part of writing good code is using good style. Both C and C++ ignore most whitespace as a language so you can technically write the entire program on one line with almost no spaces. For the sake of being able to read your code again, this is almost never a good idea. However, this does mean that you have a lot of flexibility in how the code looks visually. The exact details of how your code looks is what we call your programming “style”.
I am not going to tell you to one specific style. There are as many different styles as there are programmers. I do, however, recommend that you pick a style that you like and stick to it. What follows is a brief guide to the kinds of things to think about when it concerns style.
A given programming style can be broken down into how you handle certain cases. While there are probably more ways to break it down than I’m going to describe here, I’ll try and cover the basics.
Probably the most obvious and important component of any style is indentation. You can write code like this:
#include <stdio.h>
int main(int argc, char **argv) {
int i;
printf("Hello World! Let me count to 10\n");
for (i = 1; i <= 10; ++i) {
printf("%d\n", i);
}}
While this works, if your program gets very long, it gets almost impossible to read. Usually programmers indent blocks of code with each block indented more than the one around it. For example, I would indent the above chunk of code as follows:
#include <stdio.h>
int
main(int argc, char **argv)
{
int i;
printf("Hello World! Let me count to 10\n");
for (i = 1; i <= 10; ++i) {
printf("%d\n", i);
}
}
As you can see, this makes the blocks stand out much better and makes the code easier to read. How far you indent and exactly what blocks get indented is a matter of personal taste.
Another important component of any C/C++ style is the placing of
braces. In C/C++, braces denote blocks of code. Some people prefer to
put the brace at the end of the line that starts the block, others
prefer to put it on its own line. Exactly how it’s handled frequently
depends on the type of block. For instance, a programmer may put the
opening brace associated with a function declaration on its own line but
put braces associated with if
statements and loops on the
same line as the statement.
For if
statements, one thing to consider is how to
handle the else
condition. For example
if (ptr != NULL) {
/* Do something with ptr */
} else {
/* Handle the null pointer error */
}
vs
if (ptr != NULL)
{
/* Do something with ptr */
}
else
{
/* Handle the null pointer error */
}
or
if (ptr != NULL) {
/* Do something with ptr */
}
else {
/* Handle the null pointer error */
}
The problem with control statements like these is that different placements of the curly braces work better than others depending on the code in the block and whether or not the condition wraps onto another line. Different people also have different (and frequently strong) preferences on what they think looks better and is most readable.
The two major parts of function declarations are the return value and the opening curly brace. Different programmers will put one, both, or neither of these on its own line. For example:
int main(int argc, char **argv) {
printf("Hello World!\n");
}
vs.
int
main(int argc, char **argv) {
printf("Hello World!\n");
}
vs.
int
main(int argc, char **argv)
{
printf("Hello World!\n");
}
The switch
statement is a bit of a special case because
of the labels. I would recommend doing the curly braces the same as with
other control flow statements but the real problem is indentation. There
are three different ways this is commonly done:
switch (c) {
case 'h':
print_help();
return 0;
case 'r':
recursive = 1;
break;
default:
fprintf(stderr, "Invalid Argument");
print_help();
return 1;
}
vs.
switch (c) {
case 'h':
print_help();
return 0;
case 'r':
recursive = 1;
break;
default:
fprintf(stderr, "Invalid Argument");
print_help();
return 1;
}
vs.
switch (c) {
case 'h':
print_help();
return 0;
case 'r':
recursive = 1;
break;
default:
fprintf(stderr, "Invalid Argument");
print_help();
return 1;
}
One thing to consider is that if you chose the third option is that, if you indent a long ways, switch statements get double-indented. This can be a problem if you care about your code getting indented too far.
Sometimes a line of code will begin to get long enough that it won’t fit on one line. In this case, you have to decide if you will make it two lines or just let the text editor wrapper and where you will break the line. Personally, I try and make sure lines are no more than 80 characters so that they don’t unnaturally wrap in a standard-width terminal. Many people would consider my indenting habits to be quite extreme especially if you work in a graphical or full-screen text editor. How you handle this is up to you.
This is an example of my personal style. Most of the code I write looks something like this (although sometimes I do something a little different for various reasons.)
#include <stdlib.h>
#include <stdio.h>
int
main(int argc, char ** argv)
{
int i;
char * arg;
for (i = 0; i < argc; ++i) {
/* Check to see if it's a one-letter flag */
if (argv[i][0] == '-' && argv[i][1] != '\0' && argv[i][2] == '\0') {
switch(argv[i][1]) {
case 'h':
/* Help flag */
printf("This is the program help.");
return 0;
case 'n':
case 's':
case 'd':
/* Normally we would do something specific for each one of
* these */
printf("The -%c flag\n", argv[i][1]);
break;
default:
printf("\"-%s\" is not a valid one-letter flag\n", argv[i][1]);
return 1;
}
} else if (argv[i][0] == '-' && argv[i][1] == '-') {
/* Get rid of the first 2 characters (both are '-') */
arg = argv[i] + 2;
/* Normally we would do some parsing here */
printf("The --%s flag\n", arg);
} else {
/* Everything else we simply treat as a parameter */
printf("\"%s\" as a parameter\n", argv[i]);
}
}
}
Here is an example of something similar to my preferred style. This one isn’t quite as compact. Some people prefer a more “open” feel.
#include <stdlib.h>
#include <stdio.h>
int
main(int argc, char ** argv)
{
int i;
char * arg;
for (i = 0; i < argc; ++i)
{
/* Check to see if it's a one-letter flag */
if (argv[i][0] == '-' && argv[i][1] != '\0' && argv[i][2] == '\0')
{
switch(argv[i][1])
{
case 'h':
/* Help flag */
printf("This is the program help.");
return 0;
case 'n':
case 's':
case 'd':
/* Normally we would do something specific for each one of
* these */
printf("The -%c flag\n", argv[i][1]);
break;
default:
printf("\"-%s\" is not a valid one-letter flag\n", argv[i][1]);
return 1;
}
}
else if (argv[i][0] == '-' && argv[i][1] == '-')
{
/* Get rid of the first 2 characters (both are '-') */
arg = argv[i] + 2;
/* Normally we would do some parsing here */
printf("The --%s flag\n", arg);
}
else
{
/* Everything else we simply treat as a parameter */
printf("\"%s\" as a parameter\n", argv[i]);
}
}
}
This is the style described in the GNU coding standards and used in the source code for the GNU toolchain. I personally don’t recommend it because I think it’s ugly, but it’s a good example of some of the variety you may see.
#include <stdlib.h>
#include <stdio.h>
int
main (int argc, char **argv)
{
int i;
char *arg;
for (i = 0; i < argc; ++i)
{
/* Check to see if it's a one-letter flag */
if (argv[i][0] == '-' && argv[i][1] != '\0' && argv[i][2] == '\0')
{
switch (argv[i][1])
{
case 'h':
/* Help flag */
printf ("This is the program help.");
return 0;
case 'n':
case 's':
case 'd':
/* Normally we would do something specific for each one of
* these */
printf ("The -%c flag\n", argv[i][1]);
break;
default:
printf ("\"-%s\" is not a valid one-letter flag\n", argv[i][1]);
return 1;
}
}
else if (argv[i][0] == '-' && argv[i][1] == '-')
{
/* Get rid of the first 2 characters (both are '-') */
arg = argv[i] + 2;
/* Normally we would do some parsing here */
printf ("The --%s flag\n", arg);
}
else
{
/* Everything else we simply treat as a parameter */
printf ("\"%s\" as a parameter\n", argv[i]);
}
}
}
This is the style produced by the Berkely UNIX indent
command.
#include <stdlib.h>
#include <stdio.h>
int
main(int argc, char **argv)
{
int i;
char *arg;
for (i = 0; i < argc; ++i) {
/*
* Check to see if it's a one-letter flag
*/
if (argv[i][0] == '-' && argv[i][1] != '\0' && argv[i][2] == '\0') {
switch (argv[i][1]) {
case 'h':
/*
* Help flag
*/
printf("This is the program help.");
return 0;
case 'n':
case 's':
case 'd':
/*
* Normally we would do something specific for each one of
* these
*/
printf("The -%c flag\n", argv[i][1]);
break;
default:
printf("\"-%s\" is not a valid one-letter flag\n",
argv[i][1]);
return 1;
}
} else if (argv[i][0] == '-' && argv[i][1] == '-') {
/*
* Get rid of the first 2 characters (both are '-')
*/
arg = argv[i] + 2;
/*
* Normally we would do some parsing here
*/
printf("The --%s flag\n", arg);
} else {
/*
* Everything else we simply treat as a parameter
*/
printf("\"%s\" as a parameter\n", argv[i]);
}
}
}
This is the style used in Kernighan & Ritchie’s book, “The C Programming Language”. It is also sometimes known as “the one true style”. While that’s a bit over-the-top, it’s a decent starting point.
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int i;
char *arg;
for (i = 0; i < argc; ++i) {
/* Check to see if it's a one-letter flag */
if (argv[i][0] == '-' && argv[i][1] != '\0' && argv[i][2] == '\0') {
switch (argv[i][1]) {
case 'h':
/* Help flag */
printf("This is the program help.");
return 0;
case 'n':
case 's':
case 'd':
/* Normally we would do something specific for each one of
* these */
printf("The -%c flag\n", argv[i][1]);
break;
default:
printf("\"-%s\" is not a valid one-letter flag\n",
argv[i][1]);
return 1;
}
} else if (argv[i][0] == '-' && argv[i][1] == '-') {
/* Get rid of the first 2 characters (both are '-') */
arg = argv[i] + 2;
/* Normally we would do some parsing here */
printf("The --%s flag\n", arg);
} else {
/* Everything else we simply treat as a parameter */
printf("\"%s\" as a parameter\n", argv[i]);
}
}
}
This is the style used in the source code for the Linux kernel.
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int i;
char *arg;
for (i = 0; i < argc; ++i) {
/* Check to see if it's a one-letter flag */
if (argv[i][0] == '-' && argv[i][1] != '\0'
&& argv[i][2] == '\0') {
switch (argv[i][1]) {
case 'h':
/* Help flag */
printf("This is the program help.");
return 0;
case 'n':
case 's':
case 'd':
/* Normally we would do something specific for each one of
* these */
printf("The -%c flag\n", argv[i][1]);
break;
default:
printf
("\"-%s\" is not a valid one-letter flag\n",
argv[i][1]);
return 1;
}
} else if (argv[i][0] == '-' && argv[i][1] == '-') {
/* Get rid of the first 2 characters (both are '-') */
arg = argv[i] + 2;
/* Normally we would do some parsing here */
printf("The --%s flag\n", arg);
} else {
/* Everything else we simply treat as a parameter */
printf("\"%s\" as a parameter\n", argv[i]);
}
}
}