10 Tips to Make Your C Program Effective

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:

1 void str_print( char *str )
2  
3 {
4  
5     int i;
6  
7     for ( i = 0; i < strlen ( str ); i++ ) {
8  
9         printf("%c",str[ i ] );
10  
11     }
12  
13 }
1 void str_print1 ( char *str )
2  
3 {
4  
5     int len;
6  
7     len = strlen ( str );
8  
9     for ( i = 0; i < len; i++ ) {
10  
11         printf("%c",str[ i ] );
12  
13     }
14  
15 }

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.

1 int multiply ( int *num1 , int *num2 )
2  
3 {
4  
5     *num1 = *num2;
6  
7     *num1 += *num2;
8  
9     return *num1;
10  
11 }
1 int multiply1 ( int *num1 , int *num2 )
2  
3 {
4  
5     *num1 = 2 * *num2;
6  
7     return *num1;
8  
9 }

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 )

1 struct {
2  
3     char c;
4  
5     int i;
6  
7     short s;
8  
9 }str_1;
1 struct {
2  
3     char c;
4  
5     short s;
6  
7     int i;
8  
9 }str_2;

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

1 int x = 4;
2  
3 if ( x = 1 ) {
4  
5     x = x + 2;
6  
7     printf("%d",x);          // Output is 3
8  
9 }
1 int x = 4;
2  
3 if ( 1 = x ) {
4  
5     x = x + 2;
6  
7     printf("%d",x);   // Compilation error
8  
9 }

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.

1 typedef int* INT_PTR;
2  
3 INT_PTR a , b;
4  
5 # define INT_PTR int*;
6  
7 INT_PTR a , b;

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:

1 /*first_file.c*/
2  
3 static int foo ( int a )
4  
5 {
6  
7 /*Whatever you want to in the function*/
8  
9 }
10  
11 /*second_file.c*/
12  
13 int foo ( int )
14  
15 int main()
16  
17 {
18  
19     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.
20  
21     return 0;
22  
23 }

8. Use memoization to avoid repititious calculation in Recursion

Consider the Fibonacci problem;

The Fibonacci problem can be solved by simple recursive approach:

1 int fib ( n )
2  
3 {
4  
5     if ( n == 0 || n == 1 ) {
6  
7         return 1;
8  
9     }
10  
11     else {
12  
13         return fib( n - 2 ) + fib ( n - 1 );
14  
15     }
16  
17 }

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:

1 int calc_fib ( int n )
2  
3 {
4  
5     int val[ n ] , i;
6  
7     for ( i = 0; i <=n; i++ ) {
8  
9         val[ i ] = -1;      // Value of the first n + 1 terms of the fibonacci terms set to -1
10  
11     }
12  
13     val[ 0 ] = 1;               // Value of fib ( 0 ) is set to 1
14  
15     val[ 1 ] = 1;           // Value of fib ( 1 ) is set to 1
16  
17     return fib( n , val );
18  
19 }
20  
21 int fib( int n , int* value )
22  
23 {
24  
25     if ( value[ n ] != -1 ) {
26  
27         return value[ n ];              // Using memoization
28  
29     }
30  
31     else {
32  
33         value[ n ] = fib( n - 2 , value ) + fib ( n - 1 , value );          // Computing the fibonacci term
34  
35     }
36  
37     return value[ n ];                // Returning the value
38  
39 }

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.

1 void dangling_example()
2  
3 {
4  
5     int *dp = malloc ( sizeof ( int ));
6  
7     /*........*/
8  
9     free( dp );             // dp is now a dangling pointer
10  
11     dp = NULL;      // dp is no longer a dangling pointer
12  
13 }

 

1 void wild_example()
2  
3 {
4  
5     int *ptr;       // Uninitialized pointer
6  
7     printf("%u"\n",ptr );
8  
9     printf("%d",*ptr );
10  
11 }

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.

http://www.fortystones.com/tips-to-make-c-program-effective/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据整理是数据科学家或数据分析师工作中不可避免的一部分。使用Python进行数据整理可以大大简化这个过程,提高工作效率。以下是一些使用Python进行数据整理的技巧和工具,可以使你的工作更加轻松。 1. 使用pandas库:pandas是一个功能强大的数据处理库,它提供了灵活的数据结构和数据分析工具。你可以使用pandas读取、处理和分析各种类型的数据,例如CSV、Excel、SQL数据库等。 2. 缺失值处理:通过pandas,你可以轻松处理数据中的缺失值。使用fillna()函数可以用指定的值或方法填充缺失值,使用dropna()函数可以将包含缺失值的行或列删除。 3. 数据清洗:清洗数据是整理数据的重要步骤之一。通过使用pandas的字符串函数,你可以进行字符串处理和提取有用的信息。此外,使用正则表达式可以更方便地实现复杂的模式匹配和替换。 4. 数据转换:在处理数据时,你可能需要对数据进行转换,例如将数据类型转换为适用于分析的格式,或者按照需要的方式重塑数据框架。pandas提供了一系列内置函数,可帮助你轻松完成这些任务,例如astype()函数可以进行数据类型转换,pivot()函数可以进行数据透视。 5. 数据合并和连接:当你需要将多个数据集合并在一起时,pandas提供了多种方法,如concat()函数可以按行或列连接数据,merge()函数可以根据指定的列将多个数据集按照指定的方式合并。 6. 数据重复值处理:重复的数据可能会对分析造成影响。pandas的duplicated()和drop_duplicates()函数可以帮助你找到并删除重复的数据。 7. 数据分组和聚合:使用pandas的groupby()函数,你可以按照指定的标准对数据进行分组,并进行统计分析,如计算均值、求和、数量等。 总之,使用Python进行数据整理可以使用强大的工具和技巧,使你的工作更加轻松高效。无论是数据清洗、转换、合并还是聚合,pandas提供了丰富的函数和方法来满足你的需求。掌握这些技巧和工具,对于处理和分析数据将会非常有帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值