http://www.fortystones.com/tips-to-make-c-program-effective/
代码之美不仅在于能够寻求一种合理的解决方案,更在于其简洁、高效与紧凑。代码的设计往往比实际的代码编写要难。因此,每一个程序员在代码编写的过程中,需要在头脑中时常保持一些基本的原则。
这里有10种使你的C程序更加高效的方法:
1. 避免不必要的函数调用
考虑下面两个函数调用:
Void str_print(char * str) { int I; for(I = 0; I < strlen(str); i++){ Printf(“%c”, str[i]); } }
这段代码在循环的过程中不停地调用函数strlen(str),而实际上只需要一次调用即可:
Void str_print(char * str) { Int I; Int len = strlen(str); For(I = 0; I < len; i++){ Printf(“%c”, str[i]); } }
2.避免不必要的指针引用:
考虑下面两个函数调用:
Int multiply(int *num1, int *num2 ) { *num1 = *num2; *num1 += *num2; Return *num1; }
第一个例子中有5处指针引用,而第二个例子中只有三处指针引用,你觉得哪一个会更好一些呢?
3.
考虑下面两个结构体:
Struct{ Char c; Int I; Short s; };
Struct{ Char c; Short s; Int I; };
假设一个char需要1 byte的内存空间,一个short占2 byte的内存空间,一个int占4个字节的内存空间。
首先,我们会认为这两个结构体占据相同大小的内存空间,然而,而str_1占用12个字节的第二个结构体只需要8个字节?这怎么可能呢?
内存对齐规则:
① 对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是min(#pragma pack()指定的数,这个数据成员的自身长度) 的倍数。
② 在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
4.
如果你知道某个值永远为正,使用unsigned int 而不是 int,因为某些处理器处理unsigned int类型的数据比int要快得多。
5.
在逻辑语句中把常量放在运算符的左侧,防止出现拼写错误的情况。
比如:
If(a = 3) { a++; }
与
If(3 = a) //Compilation Error { a++; }
6.
更多得使用typedef而不是宏,虽然有时宏是不可取代的。Typedef能使程序更加直观。
typedef int* INT_PTR;
INT_PTR a , b;
# define INT_PTR int*;
INT_PTR a , b;
在这个宏定义中,a是指向int的指针,而b只是int。使用typedef, a、b都是整型指针。
7.
尽量把函数定义为static型除非你希望在其它的文件中调用该函数。Static函数是指只在当前文件中有效的函数。Static型的函数能够有效地限定对函数的访问,这样我们就不需要创建特殊的头文件来存放内部的函数。这样做的好处有:
A). 在不同的文件中可以将static函数以相同的名字命名。
B). 编译开销减少因为没有外部文件符号处理。
//first_file.c Static int foo(int a) { //Whatever you want to in the function } //second_file.c Int foo( int ); Int main() { Foo(); //This is not a valid function call as the function foo can only be called by any other function within first_file.c where it is defined. Return 0; }
8.
使用记忆,避免递归过程的重复计算。
以Fibonacci数列为例:
常规的递归方法是:
Int fib( int n) { If(n == 0 || n == 1){ Return 1; } Else{ Return fib(n - 2) + fib(n - 1); } }
从递归树中可以发现,在计算fib(5)时,我们计算fib(3)函数2次,fib(2)函数3次。这是相同函数的重复计算。如果n非常大,fib<n(i)函数增长i<n。解决这一问题的快速方法将是计算函数值1次,存储在一些地方,需要时计算,而非一直重复计算。
int calc_fib ( int n ) { int val[ n ] , i; for ( i = 0; i <=n; i++ ) { val[ i ] = -1; // Value of the first n + 1 terms of the fibonacci terms set to -1 } val[ 0 ] = 1; // Value of fib ( 0 ) is set to 1 val[ 1 ] = 1; // Value of fib ( 1 ) is set to 1 return fib( n , val ); } int fib( int n , int* value ) { if ( value[ n ] != -1 ) { return value[ n ]; // Using memoization } else { value[ n ] = fib( n - 2 , value ) + fib ( n - 1 , value ); // Computing the fibonacci term } return value[ n ]; // Returning the value }
9.
避免野指针与悬挂指针。
养成在释放空间之后将指针赋空的习惯。
10.
及时释放内存。
The beauty of any code lies not only in finding the solution to a given problem but is in its simplicity, effectiveness, compactness and efficiency( memory ). Designing the code is harder than actually implementing it. Hence every programmer should keep a couple of basic things in mind while programming in C. Here we introduce you to such 10 ways of standardizing your C code.
1. Avoid unwarranted function calls
Consider the following two functions:
|
| |
|
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| ||
|
|
|
| |
|
|
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| ||
|
|
|
| |
|
|
|
| |
|
|
|
|
Notice the similarity in function of the two functions. However the first function calls the strlen() multiple times whereas the second function only calls
the function strlen() a single time. Thus performance of the second function is obviously better than the first one.
2. Avoid unnecessary memory references
Again lets take a couple more examples to explain this.
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| ||
|
|
|
| |||
|
| |||
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
|
Again these two functions have similar functionality. The difference is there are 5 memory references in the first function ( 1 for reading *num1 , 2 for reading *num2 and 2 for writing to *num1 )whereas in the second function there is only 2 memory references ( one for reading *num2 and one for writing to *num1 ).
Now which one do you think is better of the two?
3. Saving memory used( concept of Memory Alignment and Padding )
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
|
Assume that a char takes 1 byte , short takes 2 bytes and int takes 4 bytes of memory. At first we would think that both the structures defined above are the same and hence occupy the same amount of memory. However whereas str_1 occupies 12 bytes the second structure takes only 8 bytes? How is that possible?
Notice in the first structure that 3 different 4 bytes need to be assigned to accomodate the three data types( as we have int declaration between char and short). Whereas in the second structure in the first 4 bytes both char and short can be accomodated hence int can be accomodated in the second 4 bytes boundary( thus a total of 8 bytes ).
4. Use unsigned ints instead of ints if you know the value will never be negative. Some processors can handle unsigned integer arithmetic considerably faster than signed ( this is also good practise, and helps make for self-documenting code).
5. In a logical conditional statement always keep the constant item of the comparison on the left hand side
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
|
Using the “=” assignment operator instead of the “==” equality comparison operator is a common typing error we can’t make out until execution. Puttin the constant term on the left hand side will generate a compile-time error, letting you catch your error easily.
Note : ‘=’ is the assignment operator. b = 1 will set the variable b equal to the value 1.
‘==’ is the equality operator. it returns true if the left side is equal to the right side, and returns false otherwise.
6. Whenever possible use typedef instead of macro. Sometimes you just cannot avoid macros but using typedef is better.
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
|
Here in the macro definition a is a pointer to an integer whereas b is declared as only an integer. Using typedef both a and b are integer pointers.
In addition, debugging with typedef is more intuitive compared to macros.
7. Always declare and define functions as static unless you expect the function to be called from different files.
Functions that are visible only to other functions in the same file are known as static functions.
It restrict others from accessing the internal functions which we want to hide from outside world. Now we don’t need to create private header files for internal functions.Others don’t see the function and so theyh don’t use them therefore don’t cast those function definition in concrete.
Advantages of declaring a function static include:
a) Two or more static functions with the same name can be used in different files.
b) Compilation overhead is reduced as there is no external symbol processing.
Let’s understand this better with the examples below:
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| ||
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
|
8. Use memoization to avoid repititious calculation in Recursion
Consider the Fibonacci problem;
The Fibonacci problem can be solved by simple recursive approach:
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| ||
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
|
Note : Here we are considering the fibonacci series from 1. Thus the series looks like : 1 , 1 , 2 , 3 , 5 , 8 , …
Notice from the recursive tree that we have calculated the fib( 3 ) function 2 times and fib ( 2 ) function 3 times. This is repeated calculation for the same function. If n is extremely large the calculation of the fib( i ) function increases for i < n. A faster way of solving this problem would be to compute the value of the function once , store it in some place and retrieve it whenever needed rather than recomputing it all over again. This simple technique is called memoization which can be used with recursion to enhance the speed of computation.
The memoized code for the above fibonacci function would look something like this:
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| ||
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
|
Here the calc_fib( n ) function is called from the main().
9. Avoid dangling pointers and wild pointers
A pointer whose pointing object has been deleted is known as a dangling pointer.
On the other hand, wild pointers are those pointers which are not initialized. Note that wild pointers do not point any specific memory location.
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| ||
|
|
|
| |
|
|
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| |
|
|
|
| ||
|
|
|
|
The program usually shows weird behaviour when these pointers are encountered.
10. Always remember to free whatever memory you have allocated in your program. Notice in the example above how we freed the dp pointer which we allocated using the malloc() function call.