6.Tách các biểu thức phức tạp thành các biểu thức đơn giản hơn
Biểu thức sau đây rất ngắn gọn nhưng lại chứa quá nhiều phép toán:
*x += ( *xp = ( 2*k < ( n – m ) ? c[ k + 1 ] : d[ k -- ] ) );
if( 2*k < n - m )
*xp = c[ k + 1 ];
else
*xp = d[ k- ];
*x += *xp;
*xp = c[ k + 1 ];
else
*xp = d[ k- ];
*x += *xp;
7.Viết các lệnh dễ hiểu, không viết các lệnh “khôn ngoan”
Các lập trình viên thường thích viết các lệnh càng ngắn gọn càng tốt. Tuy nhiên điều này thường gây phiền toái cho người khác.
Hãy xem biểu thức sau đây làm gì:
subkey = subkey >> ( bitoff – ( ( bitoff >> 3 ) << 3 ) );
Biểu thức trong cùng ( bitoff >> 3 ) dịch phải bitoff 3 bit. Kết quả thu được lại được dịch trái 3 bit. Bởi vậy 3 bit cuối cùng của bitoff được thay thế bởi các số 0. Kết quả này lại được trừ đi bởi giá trị ban đầu của bitoff, kết quả của phép trừ chính là 3 bit cuối cùng trong giá trị ban đầu của bitoff. Ba bit này được dùng để dịch subkey sang phải.
Bởi vậy, biểu thức nói trên tương đương với biểu thức sau đây:
subkeu = subkey >> ( bitoff & 0×7 );
Rõ ràng cách viết thứ hai dễ hiểu hơn nhiều. Một ví dụ khác về cách viết biểu thức ngắn gọn nhưng làm phức tạp hóa vấn đề:
child = ( ! LC && ! RC ) ? 0 : ( ! LC ? RC : LC );
Cách viết dưới đây dài hơn, nhưng dễ hiểu hơn nhiều:
if( LC == 0 && RC == 0 )
child = 0;
else if( LC == 0 )
child = RC;
else
child = LC;
child = 0;
else if( LC == 0 )
child = RC;
else
child = LC;
Toán tử ? : chỉ thích hợp cho những biểu thức ngắn kiểu như sau đây:
max = ( a > b ) ? a : b;
hoặc:
printf( “The list has %d item%s\n”, n, n == 1 ? “” : “s” );
Hãy nhớ rằng mục tiêu của chúng ta là viết những đoạn mã dễ hiểu, chứ không phải các đoạn mã ngắn gọn.
8. Cẩn thận với dấu =
= và == là 2 toán tử gây nhần lẫn nhất trên C, nhưng bạn có thể tránh gặp nó bằng thói quen viết r-value (biểu thức bên phải phép gán) sang bên trái phép so sánh:
if ( a == 42 ) { … }// Cách viết thông thường.
if ( 42 == a ) { … }// Nên viết thế này.
if ( 42 == a ) { … }// Nên viết thế này.
Và đây là sự khác biệt, khi bạn nhầm…
if ( a = 42 ) { … } // Chạy bình thường, khó tìm ra lỗi
if ( 42 = a ) { … } // Báo lỗi ngay chỗ này
if ( 42 = a ) { … } // Báo lỗi ngay chỗ này
9. Các idiom
Cũng giống như ngôn ngữ tự nhiên, ngôn ngữ lập trình cũng có các idiom (thành ngữ !?), là các cách viết code chính tắc cho các trường hợp thông dụng, tạm hiểu idiom là các chuẩn không bắt buộc nhưng được đa số người dùng tuân theo. Sử dụng các idiom giúp giảm bớt khả năng mắc lỗi đồng thời làm chương trình dễ đọc hơn và nhất là có vẻ “chuyên nghiệp” hơn Sau đây là một số idiom phổ biến:
a. Các idiom cho mảng
Để duyệt qua n phần tử của một mảng và khởi tạo chúng, có các cách viết sau đây:
i = 0;
while ( i <= n - 1 )
array[ i++ ] = 1.0;
while ( i <= n - 1 )
array[ i++ ] = 1.0;
hoặc
for( i = 0; i < n; )
array[ i++ ] = 1.0;
array[ i++ ] = 1.0;
hoặc
for( i = n; --i >= 0; )
array[ i ] = 1.0;
array[ i ] = 1.0;
Tất cả những cách viết trên đều đúng, tuy nhiên idiom cho trường hợp này là:
for( i = 0; i < n; ++i )
array[ i ] = 1.0;
array[ i ] = 1.0;
Một lưu ý nhỏ là sự khác biệt giữa i++ và ++i:
- i++ lấy giá trị của i trước rồi tăng nó lên.
- ++i tăng giá trị của i rồi lấy giá trị mới.
Do đó đối với các con đếm vòng lặp (for(), while()) nên dùng ++i để tăng tốc độ.
Idiom của vòng lặp duyệt qua các phần tử của một danh sách (list) là
for( p = list; p != NULL; p = p->next )
Đối với container:
vector<string>::iterator it;
for(it = v.begin(); it != v.end(); ++it)
std::cout << *it;
for(it = v.begin(); it != v.end(); ++it)
std::cout << *it;
Đối với các vòng lặp vô hạn, idiom là: for ( ; ; ) hoặc while( 1 )
Khởi tạo danh sách:
struct info
{
char *name;
char *job;
char *address;
};
info *array[] = {
{ "name1", "job1", "add1" },
{ "name1", "job1", "add1" },
{ "name1", "job1", "add1" },
//...
};
{
char *name;
char *job;
char *address;
};
info *array[] = {
{ "name1", "job1", "add1" },
{ "name1", "job1", "add1" },
{ "name1", "job1", "add1" },
//...
};
Hàm tìm kiếm tuyến tính:
template <class T>
int find (T obj, T* array, int size, int from = 0)
{
for(int i = from; i<size; ++i)
if(array[i] == T) return i;
return size;
}
int find (T obj, T* array, int size, int from = 0)
{
for(int i = from; i<size; ++i)
if(array[i] == T) return i;
return size;
}
Sao chép mảng: Giả sử 2 mảng double *a,*b; thay vì:
for( int i=0; i<n; ++i)
b[i]=a[i];
b[i]=a[i];
ta có thể dùng:
//#include <string.h>
memcpy(b,a,n*sizeof(double));
memcpy(b,a,n*sizeof(double));
Cấp phát động cho mảng 2 chiều:
int **pp = new type*[n];
int *p = new type[n*m];
for (int i = 0; i < n; ++i)
pp[i] = p + i * m;
//...
//use array here
delete[] p;
delete[] pp;
int *p = new type[n*m];
for (int i = 0; i < n; ++i)
pp[i] = p + i * m;
//...
//use array here
delete[] p;
delete[] pp;
b. Idiom cho lệnh if
Tiếp theo là một idiom dành cho câu lệnh if. Hãy xem đoạn mã loằng ngoằng sau đây làm gì
if ( argc==3 )
if ( ( fin = fopen(argv[l] , “r” ) ) != NULL )
if ( ( fout = fopen( argv[2], “w” ) ) != NULL ) {
while ( ( c = getc( fin ) ) != EOF )
putc( c, fout );
fclose( fin );
fclose( fout );
} else
printf ( “Can’t open output file %s\n”, argv[2] ) ;
else
printf( “Can’t open input file %s\n”, argv[l] ) ;
else
printf ( “Usage: cp input file outputfile\n” ) ;
if ( ( fin = fopen(argv[l] , “r” ) ) != NULL )
if ( ( fout = fopen( argv[2], “w” ) ) != NULL ) {
while ( ( c = getc( fin ) ) != EOF )
putc( c, fout );
fclose( fin );
fclose( fout );
} else
printf ( “Can’t open output file %s\n”, argv[2] ) ;
else
printf( “Can’t open input file %s\n”, argv[l] ) ;
else
printf ( “Usage: cp input file outputfile\n” ) ;
Viết lại đoạn mã này theo đúng idiom như sau:
if ( argc != 3 )
printf ( “Usage: cp input file outputfile\n” ) ;
else if ( ( fin = fopen( argv[l] , “r” ) ) == NULL )
printf( “Can’t open input file %s\n”, argv[l] );
else if ( ( fout = fopen( argv[2], “w” ) ) == NULL )
{
printf ( “Can’t open output file %s\n”, argv[2] ) ;
fclose( fin ) ;
}
else
{
while ( ( c = getc( fin ) ) != EOF)
putc( c, fout );
fclose( fin ) ;
fclose( fout ) ;
}
printf ( “Usage: cp input file outputfile\n” ) ;
else if ( ( fin = fopen( argv[l] , “r” ) ) == NULL )
printf( “Can’t open input file %s\n”, argv[l] );
else if ( ( fout = fopen( argv[2], “w” ) ) == NULL )
{
printf ( “Can’t open output file %s\n”, argv[2] ) ;
fclose( fin ) ;
}
else
{
while ( ( c = getc( fin ) ) != EOF)
putc( c, fout );
fclose( fin ) ;
fclose( fout ) ;
}
Nguyên tắc khi viết các lệnh if() là đặt các phép toán kiểm tra điều kiện càng gần các hành động tương ứng càng tốt.
c. Idiom cho switch() case:
Xét ví dụ:
Xét ví dụ:
switch (c)
{
case '-': sign = -1;
case '+': c = getchar();
case '.': break;
case '0': case 'o': default: if (!isdigit(c)) return 0;
}
{
case '-': sign = -1;
case '+': c = getchar();
case '.': break;
case '0': case 'o': default: if (!isdigit(c)) return 0;
}
cách viết sau tuy dài nhưng dễ đọc hơn:
switch (c)
{
case '-':
sign = -1;
case '+':
c = getchar();
break;
case '.':
break;
default: case '0': case 'o':
if (!isdigit(c))
return 0;
break;
}
{
case '-':
sign = -1;
case '+':
c = getchar();
break;
case '.':
break;
default: case '0': case 'o':
if (!isdigit(c))
return 0;
break;
}
d.Số 0 trong chương trình
Số 0 thường xuyên xuất hiện trong các chương trình với nhiều ý nghĩa khác nhau. Trình dịch sẽ tự động chuyển số 0 thành kiểu thích hợp. Tuy nhiên nên viết ra một cách tường minh bản chất của số 0 mà chúng ta đang nói đến. Cụ thể, hãy sử dụng ( void* ) 0 hoặc NULL để biểu diễn con trỏ null trong C, sử dụng ‘\0′ cho kí tự null ở cuối mỗi xâu và sử dụng 0.0 cho các số float hoặc double có giá trị không. Đừng viết đoạn mã như sau
p = 0;
name[ i ] = 0;
x = 0;
name[ i ] = 0;
x = 0;
Hãy viết:
p = NULL;
name[ i ] = ‘\0′;
x = 0.0;
name[ i ] = ‘\0′;
x = 0.0;
Số 0 nên để dành cho các số nguyên có giá trị bằng không. Tuy nhiên trong C++, 0 (thay vì NULL) lại được sử dụng rộng rãi cho các con trỏ null, điều này không được khuyến khích.
Mọi sự vi phạm đều được cho phép nếu nó giúp cho tối ưu đoạn mã của bạn.
Mục đích chính của các quy tắc này là làm cho mã nguồn dễ đọc hiểu hơn, dễ dàng sửa lỗi và bảo trì, nâng chất lượng chung của mã nguồn. Tuy nhiên, nó sẽ không thể áp dụng đúng với mọi trường hợp cụ thể, và các lập trình viên phải sử dụng mềm dẻo các quy ước này.
0 nhận xét