Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1699455
  • 博文数量: 607
  • 博客积分: 10031
  • 博客等级: 上将
  • 技术积分: 6633
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-30 17:41
文章分类

全部博文(607)

文章存档

2011年(2)

2010年(15)

2009年(58)

2008年(172)

2007年(211)

2006年(149)

我的朋友

分类: C/C++

2006-06-23 13:38:03

_Secret

programming@teo.nauticom.net




C and C++ Coding Style

This was written in order to show new C and C++ coders the coding style that
I use and prefer. It also contains explanations of why I use some of the techniques
that I will show you. Other people's styles vary (Of course, I think most other styles
are crap) but this is my preferred style. Use it if you like it or don't use it.

First Note: use ONLY /* */ comments in C and use // comments in C++. This is a
bit organized and makes the programs look cleaner. not all C compilers allow // comments,
so you're just making your program more portable for not using them. In C++, I wouldn't use
/* */ since they would make the code uglier.

At the top of every program, header, resource or any source code I place
a block comment with the intent of the file.


in C:

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*
| This is an Example |
| |
| |
| Place Dates, Compiling Info, Contact Info |
| Purpose of program |
*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/


in C++:
/////////////////////////////////////////////////////////////
// This is an Example //
// //
// Place Dates, Compiling Info, Contact Info //
// //
// Purpose of Program //
/////////////////////////////////////////////////////////////


Next come the header files, I usually like to have them 1 space from
the side of the file like:


C++:
//////////////////
// Header Files
//////////////////
#include


C:
/* Header Files */
#include

Like the above, except some old C compilers will not compile any code with a header
file like this, they require all preprocessor commands to be 0 spaces from the side.


Like so:
C:
/* Header Files */
#include


So, you decide your platform and style if you should have 1 space or 0 space.


Next, come the constants and structures

C++:
/////////////////////////////
// Constants and Structures
/////////////////////////////
const int MAX_X = 10;
const int MAX_Y = 20;

C:
/* Constants and Structures */
#define MAX_X 10
#define MAX_Y 20


I always have constants in ALL CAPITAL LETTERS this makes them easier to notice in the program.

Structures should always be typedefs.


typedef struct pixel_type {
char Color;
int X, Y;
} Pixel;

And have that form. With the first bracket on the same line as the struct.
I like to give structures Capital first letters.

Next, come the global variables (If any).
In windows, it is necessary to use Globals and when implementing interrupt handlers in DOS.
Other than that, if you do not have to use a global, do not ever use globals. Try to
use as few as possible.

C++:

/////////////////////
// Global Variables
/////////////////////
Pixel g_Screen[64000];

C:
/* Global Variables */
Pixel g_Screen[64000];

Globals should begin with a "g_" so they are easier to point out. And the first
name should begin with a capital letter.


Next, in C++ come the Object defines.

////////////////////
// Objects
////////////////////


////////////
// Foo Object
////////////
class Foo {

public:

private:

} ;


The brackets I like to take on the same style as the 0 and I like to have the public
first, then the private. When you're going to use a class, You'd want to see the interface
that you can use first, not the hidden implementations. private goes second. Objects I like
to have named with a capital first letter as well.


Next, come the function prototypes.

C++:
//////////////////////////
// Function Prototypes
//////////////////////////
void Foo(void);
void RainMan(int, char *);
int Team(int [10][]);

C:
/* Function Prototypes */
void Foo(void);
void RainMan(int, char *);
int Team(int [10][]);

One space in, and any function with NO parameter should specify VOID. All functions should
be prototyped. No function should come before the main. Prototypes give you a quick reference
list of what functions the program uses and what parameters they take. Easier and quicker
to look at a ref list like the above than to have the whole place jammed with all of the
functions. Also, about 0 void, this is for ANSI-compliant C compilers.
In the Old K&R C, you just had to list the name of the function, which looked like:

int Foo();
void RainMan();
int Team();

So, in order to clarify everything, I would list the parameter types (Even void, that's
an implied "int" but it's just ignored when you don't use it), but I would NOT list
the variable names, they are irrelevant. Only list the types.


Next comes the main function.

C++:
/////////////////////////////
// Main Function
/////////////////////////////
int main(void)
{
...

return 0;
}

C:
/* Main Function */
int main(void)
{
...

return 0;
}

I would indent the function 1 space and have the { ALWAYS on the following line and NOT indented.
Where CODE is concerned, NEVER put { on the same line as anything else. They should always
be on the next line and lined up with the previous one. This is for easier following logic
and 0/ending blocks.


MAIN should ALWAYS return an int. Too many people are defining main as void and the operating
system will return a value no matter what. 0 "int" just ensures that it doesn't
return garbage.

This is retarded:

int main(void) {

}

"It's too hard to see logic when it's unlogical!"


The ONLY time in code that { can be on the same line is:

do {

...

} while(x < 0) ;

And that is because it has beginning and ending words (do/while) so the
do { is treated as the { and the } while is treated as the }.

Any code block consisting of 1 instruction should not use {'s.

This is dumb:

if(x == y)
{
return 0;
}
else
{
return 1;
}


This is smart:

if(x == y)
return 0;
else
return 1;

There is no reason to use { } unless you have more than 2 statements, OR have an if within
an if-else:

if(x == y)
{
if(z == t)
return 0;
}
else
return 1;


In the above case, the {'s } are necessary since you want the else associated with the
first if. If you want it with the second if, the following is good:

if(x == y)
if(z == t)
return 0;
else
return 1;


Declaring Pointers:

int* x;
int * x;
int *x;

Which one is the correct way? They all work, but what are the pros and cons?

int* x, y;

Having the * on the type makes you think that x and y are pointers. only x is. The * isn't
used on every variable declared, they must be separate:

int* x, *y;

so, * is a bad idea, it is confusing.


int * x;

The above has the same idea. The * is floating.

int * x, * y;

It's a little "Spacey"

This is what I consider correct:

int *x;

You know the type is int and x is a pointer.

int *x, y;

You know x is a pointer and y is not, it's more obvious.


GOTO's


Now, everyone has been saying for a long time "GOTO's are Bad! Never use them!"
This is most likely true, but there are exceptions to this rule.

This doesn't come up often, but sometimes, SOMETIMES a goto is necessary.
SOMETIMES a GOTO isn't necessary, but it's a better alternative.

If you have to use a goto, remember these rules:
Rule #1 with a goto: NEVER JUMP BACKWARD. Jumping backward is the hardest code to read,
you can easily replace this with a do { } while(); or other loop construct.

Rule #2 DO NOT JUMP FAR OR OUT OF THE FUNCTION! Don't jump functions or jump too far
from the goto, this starts to create messy and unreadable code.

Rule #3 Make your label jump names IN ALL CAPS and make them SELF EXPLANATORY
i.e. don't name them X or Y name them like EXIT_ON_BREAK:

Example of when goto is useful:

The best example of where GOTO is useful is in a big nested loop:

/* Advance Every Node */
for(x = 0; x < 100; x++)
for(y = 0; y < 100; y++)
for(z = 0; z < 100; z++)
if(!Matrix[x][y][z])
goto COLLISION_EXIT;
else
Matrix[x][y][z]++;

COLLISION_EXIT:


Now, you see the above (a hypothetical case) when the Matrix entry is 0, a 0 has 0.
Otherwise, we advance the point. Let's just say that we want to advance this matrix unless
a collision has 0, then we want to just exit and destroy that matrix. You can use a
flag, but as you see here:


/* Advance Every Node */
for(x = 0, Flag = FALSE; x < 100 && (!Flag); x++)
for(y = 0; y < 100 && (!Flag); y++)
for(z = 0; z < 100 && (!Flag); z++)
if(!Matrix[x][y][z])
Flag = TRUE;
else
Matrix[x][y][z]++;


The above is the alternative.

#1, the logic IS harder to read!
#2, The loop will run slower since it has extra conditions.
#3, When it does find a Collision, It has to set the Memory to True, THEN it has to
increment 3 memory locations, and perform 3 conditional tests.

When SPEED is of the essence, a GOTO can solve the problem, but these problems
do not come up a lot and this is not a license to code crap.


Spacing

for(;;)
for (;;)

I 0 the first, The second looks a little weird.

I also like to use ;'s and ,'s like you use them in English:

for(x = 1, y = 10; x < y; x++, y--) ;

Unless used in loops or classes

The above, I like to space ;'s out by 1 if it's representing a Loop or a struct that's not
typedef'ed. blah, SPACE blah I like to put spaces after ; and ,'s in other normal use.


EVERY 0 OPERATOR SHOULD HAVE A SPACE ON BOTH SIDES!




x = 1;
NOT
x=1;

[ed. do you mean if(x == 1); NOT if(x==1);?]

I also like indents. The Indents I use are more relevant to how must[ed. ?] nesting will occur.

If there is not much nesting, I will use 3-4 spaces. If a lot of nesting will occur, depending,
I will use 1-2 or 3 spaces. I try to keep the lines of code fitting on the screen. Which means
that in lines that extend over, you should find a good midpoint to put on the next line, but you
put them on the next line INDENTED.


Another bit we must cover is void *'s.

MyThing = malloc(sizeof(char *)*20);

for(x = 0; x < 20; x++)
MyThing[x] = malloc(20);


The above code is crap. All void *'s should be typecast for the following reasons:

1. Most compilers aren't completely standard. They have OS extensions on and
many will give a warning or error on not typecasting a void *. Especially
if sometime you have to compile it on a C++ compiler.

2. More readable. MyThing = malloc(2); That could mean MyThing is a short * or a char *
depending on the system architecture.

3. You should always check for allocation problems.


The correct code:


C:
/* Allocate Pointers */
if(!(MyThing = (char **)malloc(sizeof(char *)*20)))
{
printf("Not Enough Memory\n");
exit(0xFF);
}


/* Allocate The Character Strings */
for(x = 0; x < 20; x++)
if(!(MyThing[x] = (char *)malloc(20)))
{
printf("Not Enough Memory\n");
exit(0xFF);
}


C++:
// Allocate Pointers
if(!(MyThing = new char *[20]))
{
cout << "Not Enough Memory" << endl;
exit(0xFF);
}


// Allocate The Character Strings
for(x = 0; x < 20; x++)
if(!(MyThing[x] = new char[20]))
{
cout << "Not Enough Memory\n" << endl;
exit(0xFF);
}



NOTE: C++: "cout" can be replaced by "cerr" and
C: printf("") can be replaced by fprintf(stderr,"")
** For those who want to use standard error print out procedures. **


Declaring a function as "inline" is something that was brought into C++. As you know though,
if you do the following in a class:


// Class X
class x {
public:
x(void) { cout << "Initialize" ; }
} ;


x::x() will be inline. This I do not feel is nice coding, it mixes prototypes
with function definitions and makes a big mess. I feel that you should use
the keyword "inline".


// Class X
class x {
public:
inline x(void);
} ;


That way, you can easily switch functions from and to
inline without moving around code. Also, some people don't know that the top
version is inline, this way, it's explicitly shown.


The coding style listed above is only the opinion of the author, you can adapt some of it
or adapt all of it, it's left up to you how you think code should look.
This page is Copyright © 1999 By . All Rights Reserved
阅读(757) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~