Trình bày tổng thể 1 Môđun hóa chương trình Chương trình của bạn nên được tách thành nhiều môđun, mỗi môđun thực hiện một công việc và càng độc lập với nhau càng tốt.. Điều này sẽ g
Trang 1Giảng viên: TS Trần Đan Thư
CÁC VẤN ĐỀ VỀ MÃ NGUỒN
Trang 3Các loại mã
Trang 5i = 1 t1 = 4*i t2 = a[t1]
t3 = 4*i t4 = b[t3]
t5 = t2*t4 t6 = prod+t5 prod = t6
t7 = i+i
i = t7
if i ? N goto 4 retval prod
leave dotprod
Trang 6Bytecode public Employee( String strName, int num)
Trang 77b bf jnp 0040008a 8b e9
mov ebp,ecx
64 b4 8b mov ah,8b
ea 64 b5 8b af 66 b4 jmp b466:af8bb564 8b 88 7b a7 8b ff mov ecx,[eax-00745885]
64 b4 8b mov ah,8b
02 7b be add bh,[ebx-42]
8b e6 mov esp,esi
64 b4 8b mov ah,8b ec
in al,dx 47
inc edi
bf 8b a0 64 b4 mov edi,b464a08b 8b 2d 62 b2 8b eb
Trang 8Obfuscated code
nghĩa (đen tối hóa mã nguồn)
_( , _, ){ _/ <=1?_( , _+1, ):
!( _% )?_( , _+1,0): _% == _/
&&! ?(printf("%d\t", _/ ),_( , _+1,0)): _% >1&& _% < _/ ?_( ,1+
_, +!( _/ %( _% ))):
_< * ?_( , _+1, ):0;}main(){_(100,0,0);}
Trang 10Trong sáng hóa mã nguồn
Rất quan trọng trong quá trình lập trình với
nhiều lý do:
80% chu kỳ sống của phần mềm là bảo trì
Mã nguồn khó đọc chỉ có thể được bảo trì bởi
chính người viết ra nó
Quy ước viết mã nguồn tăng khả năng dễ đọc
cho phần mềm, cho phép những lập trình viên khác nhanh chóng tiếp cận mã nguồn khi nó được tích hợp trên nhiều module khác nhau
Phục vụ tốt công tác thanh tra mã nguồn
Trang 13Trình bày tổng thể (1)
Môđun hóa chương trình
Chương trình của bạn nên được tách thành nhiều
môđun, mỗi môđun thực hiện một công việc và
càng độc lập với nhau càng tốt Điều này sẽ giúp bạn dễ bảo dưỡng chương trình hơn và khi đọc
chương trình, bạn không phải đọc nhiều, nhớ nhiều các đoạn lệnh nằm rải rác để hiểu được điều gì
đang được thực hiện
Cách trình bày chương trình nên nhất quán
Chương trình nên giữ được tính đơn giản và rõ ràng
trong hầu hết các tình huống
Trang 14Trình bày tổng thể (2)
Mã lệnh mà bạn viết phải thể hiện đúng cấu trúc
chương trình của bạn
Chương trình của bạn nên thực hiện như một dòng
chảy từ trên xuống dưới, không nên có những thay đổi bất chợt Để có được điều này, bạn không nên sử dụng goto hay continue
Dòng lệnh
if (count == 0) printf("No data.\n");
nên được viết là
if (count == 0)
printf("No data.\n");
Trang 15Khai báo biến và hàm (1)
Biến nên được khai báo ở gần vị trí mà nó
bắt đầu được sử dụng, như vậy bạn sẽ
tránh được việc khai báo một loạt các biến
dư thừa ở đầu hàm hay chương trình.
Mỗi biến nên khai báo trên một hàng nhằm
dễ chú thích về ý nghĩa của mỗi biến
int level = 0; //indentation level
int size = 0; //size of symbol table
int lines = 0; //lines read from input
Trang 16Khai báo biến và hàm (2)
Tên biến nên đặt ngắn gọn, nhưng đủ nghĩa Có thể
là các từ hoàn chỉnh hoặc là từ viết tắt Từ đầu tiên viết thường, các từ tiếp theo viết hoa chữ cái đầu
Riêng với các biến điều khiển nên đặt đơn giản,
Trang 17Khai báo biến và hàm (3)
Các biến không nên được sử dụng lại với nhiều
nghĩa khác nhau trong cùng một hàm
Các toán tử và toán hạng trong một biểu thức nên
được tách rời nhau nhằm làm biểu thức dễ đọc hơn
Nên có khoảng trắng giữa từ khóa và dấu (, nhưng
không nên có khoảng trắng giữa tên hàm và dấu (
mass = volume * density;
Thay vì
mass=volume*density;
Trang 18Khai báo biến và hàm (4)
Tên các hằng số được viết hoa toàn bộ, các từ viết
cách nhau bằng dấu gạch dưới
Các hằng số không nên viết trực tiếp vào chương
trình
int MIN_WIDTH = 4;
int MAX_WIDTH = 99 ;
popChange = (0.1758 - 0.1257) * population;
nên được viết là:
const double BIRTH_RATE = 0.1758;
const double DEATH_RATE = 0.1257;
Trang 19
Khai báo biến và hàm (5)
Các kiểu dữ liệu do người dùng định nghĩa được viết
hoa toàn bộ hoặc viết hoa các ký tự đầu
Khi khai báo con trỏ, dấu con trỏ nên được đặt liền
với tên, nhằm tránh trường hợp sau
BIGINT hay BigInt
char * p, q, r; //q, r không là con trỏ
Trong trường hợp này nên viết là
char *p, *q, *r;
Trang 20Khai báo biến và hàm (6)
Tên của một hàm (method) nên phản ánh công việc
hoặc giá trị trả về của nó Thường bắt đầu là động
từ, các từ tiếp theo thì viết hoa chữ cái đầu của mỗi từ
isEmpty() hoặc
getBackground() hoặc
run() hoặc
runFast()
Trang 21Khai báo biến và hàm (7)
Tên lớp (class) nên là danh từ, viết hoa chữ cái đầu
của mỗi từ Nên đặt tên lớp đơn giản nhưng phải đủ nghĩa, tránh dùng các ký hiệu và viết tắt, ngoại trừ
từ viết tắt đó là thông dụng (URL, HTML)
class Raster;
class ImageSprite;
Trang 22Trình bày dòng lệnh (1)
Một câu lệnh nên được đặt riêng trên một dòng,
điều này sẽ giúp bạn rất nhiều trong quá trình
Trang 23Trình bày dòng lệnh (2)
Nên sử dụng các dấu () khi muốn tránh các lỗi về
độ ưu tiên toán tử
Trang 24Trình bày dòng lệnh (3)
Nên dùng các dòng trắng để phân chia các hàm
trong một tập tin, các đoạn lệnh trong một hàm
như: đoạn nhập xuất dữ liệu, các đoạn tương ứng với các bước xử lý khác nhau
Mỗi dòng lệnh không nên dài quá 80 ký tự, điều
này giúp việc đọc chương trình dễ dàng hơn khi
không phải thực hiện các thao tác cuộn ngang màn hình
Trang 25
} Hoặc
for (curr = *listp, trail = listp;
curr != NULL;
Trang 26Thụt đầu dòng (1)
Cặp dấu {} bao các khối lệnh, giúp người
đọc dễ hiểu cấu trúc chương trình hơn, và
nó được sử dụng trong hầu hết các ngôn ngữ lập trình
Thụt đầu dòng (indent style) là vị trí đặt
các dấu {} và các lệnh bên trong khối lệnh
Kích cỡ khoảng cách đặt { tùy thuộc vào
chuẩn mà chúng ta chọn (style)
Nhiều chương trình rất sớm: tab
Unix: tab=8 ký tự
Macintosh: 4 ký tự
Trang 28Thụt đầu dòng (3): K&R
K&R style (Kernighan & Ritchie): được sử
dụng ở hầu hết các ngôn ngữ như: C, C++, C#
int main ( int argc, char *argv []) {
while ( x == y ) {
something () ; somethingelse () ;
if ( some_error )
do_correct () ; else
continue_as_usual () ; }
Trang 29Thụt đầu dòng (4): 1TBS
1TBS ("The One True Brace Style" ): Unix
kernel và Linux kernel sử dụng
if ( x < 0 ) {
printf ( "Negative" ) ; negative ( x ) ;
} else {
printf ( "Positive" ) ; positive ( x ) ;
}
Trang 30Thụt đầu dòng (5): Allman
Allman style (ANSI) :tương đương từ khóa
begin và end của Pascal, SQL Được sử
dụng bởi Microsoft Visual Studio 2005 and Apple's Xcode
while ( x == y ) {
something () ; somethingelse () ;
}
finalthing () ;
Trang 314
Trang 32Thụt đầu dòng (6-2): BSD KNF
if ( data != NULL && res > 0 ) {
if ( JS_DefineProperty ( cx, o, "data" ,
STRING_TO_JSVAL ( JS_NewStringCopyN ( cx, data, res )) ,
NULL , NULL , JSPROP_ENUMERATE ) != 0 ) {
QUEUE_EXCEPTION ( "Internal error!" ) ;
Trang 35Chú thích chương trình (1)
Khi đặt các chú thích, nên dùng dấu // sẽ
không có ảnh hưởng khi chúng ta sử dụng cặp ký hiệu /* */ để vô hiệu hóa một đoạn lệnh trong quá trình sửa lỗi chương trình
Và trong C,C++ không cho phép các cặp
dấu /* */ lồng nhau
Với các chú thích ngắn, ta có thể đặt nó
trên cùng dòng lệnh cần chú thích Với các chú thích dài hơn, hoặc chú thích cho cả
một đoạn lệnh, ta đặt chú thích trên một
Trang 36Chú thích dài:
if (argc > 1) {
// Get input file from command line
if (freopen(argv[1], "r", stdin) == NULL)
error("can't open %s\n", argv[1]); }
Trang 38Chú thích chương trình (4)
Block: được sử dụng để mô tả file, hàm, thuật
toán được đặt ở đầu mỗi file, hàm Chúng
cũng có thể được đặt bên trong function,
method nhưng phải thụt vào cùng cấp với đoạn code
/*
* Here is a block comment.
*/
Trang 40Chú thích chương trình (6)
Trailing: là chú thích rất ngắn, thường xuất
hiện trên cùng dòng với dòng lệnh, và thường được dịch chuyển ra xa dòng lệnh Nếu có
nhiều trailing cùng xuất hiện trên một đoạn
lệnh thì chúng nên dịch chuyển thẳng hàng với nhau
Trang 41Chú thích chương trình (7)
End-Of-Line: // có thể đặt trên một dòng mới hoặc trên
cùng dòng với dòng lệnh Không nên dùng nhiều dòng liên tục cho text comment, nhưng cũng có thể dùng
nhiều dòng liên tục nếu chú thích nằm bên ngoài đoạn lệnh
Trang 43Tự động tạo tài liệu (1)
Tài liệu dùng để
Duyệt toàn bộ thành phần của chương trình
Thông tin của chương trình
Nắm được cấu trúc chương trình
Hiểu và dùng lại các module, các hàm
Tự động tạo tài liệu
Tiết kiệm thời gian
Tạo tài liệu đầy đủ, hiệu quả
Hỗ trợ định dạng đa dạng
Trang 44Tự động tạo tài liệu (2)
(line x+2) * @Tag tagValue
(line x+3) * @Tag tagValue
(line x+y) * @Tag tagValue
(line x+y+1) */
Trang 45(line 3) * Beginning of description
(line x) * end of description
Tự động tạo tài liệu (3)
Mô tả chi tiết ý nghĩa của class,
interface, method, field.
Giúp người đọc source code dễ hiểu hơn
Trang 46Tự động tạo tài liệu (4)
Tag:
author (classes and interfaces only, required)
version (classes and interfaces only, required)
param (methods and constructors only)
return (methods only)
exception
see
since
serial (or @serialField or @serialData)
(line x+y) * @Tag tagValue
Trang 47Tự động tạo tài liệu (5)
/**
* Checks a object for "coolness" Performs a comprehensive
* coolness analysis on the object An object is cool if it
* inherited coolness from its parent; however, an object can
* also establish coolness in its own right.
*
* @param obj the object to check for coolness
* @param name the name of the object
* @return true if the object is cool; false otherwise.
* @exception OutOfMemoryError If there is not enough memory to
* determine coolness.
* @exception SecurityException If the security manager cannot be
* created
* @see isUncool
Trang 49 Mã nguồn cần được thanh tra bởi bên thứ ba
Mã nguồn nghiên cứu cần cải thiện bằng bên thứ
ba trước khi trở thành mã nguồn thương mại
Mã nguồn không được dịch thành mã máy/mã
giả trước khi phân phối
Trang 51Bảo mật hướng hành chính
Chú thích, đặt tên riêng
Đoạn giải thuật đặc trưng
Kết hợp hai đặc trưng trên
Trang 52Chú thích, đặt tên riêng
const double MYPI = 3.1415926 ;
//Print all primes in [t,cap]
void primes (int cap , int t , int composite) {
Trang 53Đoạn giải thuật đặc trưng
int sum( int n){
int k = 0;
for ( int i=1;i<n;i++)
k = k+i;
return k;
for ( int i=1,s=0;i<n;s+=i,i++);
return s;
}
int sum( int n){
return n*(n+1)/2;
Trang 54Hạn chế của bảo mật hướng hành chính
hiện mã nguồn đã bị đánh cắp
Trang 56Đen tối hóa mã nguồn
obfuscating)
Làm tối nghĩa của mã nguồn
Phương pháp có thể làm bằng tay hay tự
động hóa
Không thay đổi ngôn ngữ của mã nguồn
(không thay vai trò của compiler)
Không xét đến việc trong sáng hóa mã
nguồn đã bị làm đen tối
Trang 57Đen tối hóa mã nguồn
Loại bỏ chú thích
Mã hóa chuỗi
Đổi tên định danh
Sử dụng khai báo tiền xử lý
Thay đổi truy xuất mảng
Biến đổi biểu thức
Biến đổi cấu trúc điều khiển
Thay đổi cách gọi hàm
Trang 59Mã hóa chuỗi
Các hằng số chuỗi được mã hóa
Chuỗi được khai báo bằng mảng số
Sử dụng hàm giải mã chuỗi thay cho các vị trí sử
Trang 60Đổi tên định danh
Thay thế tên có nghĩa (tên hằng số, tên hàm, tên
biến, tên lớp,…) bằng tên vô nghĩa
void main() {
Trang 61Sử dụng khai báo tiền xử lý
#else
#define T3 printf("Hello world!"); #include FILE
#endif
#else
#define T2 int main() { #include FILE
#endif
Trang 62Thay đổi truy xuất mảng (1)
Với các ngôn ngữ hỗ trợ con trỏ như C, việc
truy xuất đến các phần tử mảng có thể được thực hiện theo nhiều cách
//declaration of array
int arr[]={0,1,2,3,4};
arr[idx]; //element <index> of array
arr; //base address of array
arr + idx; //pointer arithmetic
*(arr+idx); //<idx>th element
Trang 63Thay đổi truy xuất mảng (2)
Với các ngôn ngữ hỗ trợ con trỏ như C, việc truy xuất đến các phần tử mảng có thể được thực hiện theo nhiều cách
void printarr( int integer) {
for ( int i=0; i<10; ++i) {
printf("%d ",*(( int *)integer+i));
}
}
void main() {
int array[]={0,1,2,3,4,5,6,7,8,9};
Trang 64Biến đổi biểu thức
Tối ưu hóa biểu thức
Thay đổi thứ tự các phép toán trong biểu
Trang 65Biến đổi cấu trúc điều khiển (1)
Dùng cấu trúc tương đương
( for <-> while ) hoặc dùng biểu
thức thay cho điều kiện
Dùng dấu phẩy cho các lệnh tuần
else {
c+=b; a=c+2; }
Trang 66Biến đổi cấu trúc điều khiển (2)
Dùng hàm đệ quy thay cho cấu trúc lặp
Trang 67Thay đổi cách gọi hàm (1)
Trang 68Thay đổi cách gọi hàm (2)
void f2( char *s){ }
void f5( double s){ }
void f9( int x, float f){ }
void callfunc ( int id, ){
Trang 69else if(j == i && !composite) printf("%d\t",i);
else if(j > 1 && j < i) composite += !(i % j); }
} int main() { primes(100);
Trang 70Ví dụ từ wikipedia (2)
void primes(int cap, int t, int
composite) { int i = t / cap;
int j = t % cap;
if(i <= 1) primes(cap,t+1,composite); else if(j == 0)
int main() { primes(100,0,0);
void primes(int cap) {
Trang 71void primes(int cap, int t, int
int j = t % cap;
(i <= 1) ? primes(m,t+1,c) :
(j == 0) ? primes(m,t+1,0) : (j == i && !c) ?
(printf("%d\t",i),
primes(m,t+1,0)) : (j > 1 &&
j < i) ? primes(m,t+1,c + !(i % j)) :
(t < m * m) ? primes(m,t+1,c) : 0;
} int main() { primes(100,0,0);
Trang 72void primes(int m, int t, int c)
primes(m,t+1,c) : !(t % m) ? primes(m,t+1,0) : ((t % m)==(t / m) && !c) ?
(printf("%d\t",(t / m)), primes(m,t+1,0)) :
((t % m)> 1 && (t % m) < (t /
m)) ? primes(m,t+1,c + !((t / m) % (t % m))) :
(t < m * m) ? primes(m,t+1,c)
: 0;
} int main() { primes(100,0,0);
}
Trang 73void primes(int m, int t, int
_( , _+1, ) : !( _ % ) ? _( , _+1,0) :
(( _ % )==( _ / ) && !
) ? (printf("%d\t",( _ / )),
_( , _+1,0)) : (( _ % ) >
1 && ( _ % ) < ( _ / ))
? _( , _+1, + !(( _ / )
% ( _ % ))) : ( _ < * ) ?
_( , _+1, ) : 0;
}
Ví dụ từ wikipedia (5)
Trang 74void _(int , int _, int ) {
Ví dụ từ wikipedia (6)
Xóa các khoảng trắng và dấu ngoặc
Trang 75Đánh giá đen tối hóa mã nguồn
Ưu điểm
Bảo vệ sở hữu trí tuệ trong mã nguồn
Giảm việc bị khám phá lỗ hổng qua mã nguồn
Giảm kích thước mã nguồn
Loại bỏ lỗi thiếu thư viện
Nhược điểm
Tăng độ khó dịch ngược (reverse engine) nhưng
không phải là không thể
Không uyển chuyển (portability)
Trang 77 The International Obfuscated C Code Contest
http://www1.us.ioccc.org/main.html
Goals of the Contest
To write the most Obscure/Obfuscated C
program
To show the importance of programming
style, in an ironic way
To stress C compilers with unusual code
To illustrate some of the subtleties of the C
language
Trang 78!(OO-!I||I)?l-1:OO :(OO=main(I,O,O0,OO>>!!OO,l),
!(OO-l+!I||I)?l-1:main(I-!I-!!I,O,OO,OO,l)) :(O0+OO)%l
:main(I-I/I-I/I,O,O0,OO+OO/OO,
main(0,O,O0,OO,I-I-I)+I+1?1:printf("%d ",I-I-I) +fflush(stdout))
John Dalbec
In ra các số nguyên tố nhỏ hơn 14748 (1996)
Trang 80Ví dụ trên IOCC (3)
#include <stdio.h>
int l; int main( int o, char **O,
int I){ char c,*D=O[1]; if (o>0){
Trang 81char *O=" <60>!?\\\n"_ doubIe[010]_ int0,int1 _ Iong=0 _ inIine( int eIse){ int
O1O=!O _ l=!O; for (;O1O<010;++O1O)l+=(O1O[doubIe]*pow(eIse,O1O)); return l;} int
main( int booI, char *eIse[]){ int I=1,x=-*O; if (eIse){ for (;I<010+1;I++)I[doubIe-1]
=booI>I?atof(I[eIse]):!O switch (*O)x++)abs(inIine(x))>Iong&&(Iong=abs(inIine(x
Jonathan Hoyle
Vẽ đồ thị hàm số (2004)