Efficient Programs
• Trước hết là giải thuật
– Hãy dùng giải thuật hay nhất có thể
– Sau đó hãy nghĩ tới việc tăng tính hiệu quả
của code
– Ví dụ: Tính tổng của n số tự nhiên kể từ m
Dùng chỉ thị chương trình dịch
Một số compilers có vai trò rất lớn trong việc tối
ưu chương trình
– Chúng phân tích sâu mã nguồn và làm mọi
điều “machinely” có thể
– Ví dụ GNU g++ compiler trên Linux/Cygwin
cho chương trình viết = c
• g++ –O5 –o myprog myprog.c
– Có thể cải thiện hiệu năng từ 10% đến 300%
116 trang |
Chia sẻ: candy98 | Lượt xem: 621 | Lượt tải: 0
Bạn đang xem trước 20 trang tài liệu Bài giảng Kỹ thuật lập trình - Chương 3: Tăng hiệu năng chương trình và phong cách lập trình - Trịnh Thành Trung, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
Trịnh Thành Trung
trungtt@soict.hust.edu.vn
Bài 3
TĂNG HIỆU NĂNG CHƯƠNG TRÌNH
VÀ PHONG CÁCH LẬP TRÌNH
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
-
TĂNG HIỆU NĂNG
CHƯƠNG TRÌNH
1
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
3
• Trước hết là giải thuật
– Hãy dùng giải thuật hay nhất có thể
– Sau đó hãy nghĩ tới việc tăng tính hiệu quả
của code
– Ví dụ: Tính tổng của n số tự nhiên kể từ m
Efficient Programs
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
4
Efficient Programs
void main() {
long n,m,i , sum ;
cout << ‘ vào n ‘ ; cin << n;
cout << ‘ vào m ‘ ; cin << m;
sum =0;
for(i = m ; i < m+n; i++)
sum += i;
cout << ‘ Tổng = ‘ <<sum;
}
void main()
{
long n,m ;
cout << ‘ vào n ‘ ; cin << n;
cout << ‘ vào m ‘ ; cin << m;
cout << ‘ Tổng = ‘
<< (m + m+ n) * n / 2.0;
}
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
5
• Một số compilers có vai trò rất lớn trong việc tối
ưu chương trình
– Chúng phân tích sâu mã nguồn và làm mọi
điều “machinely” có thể
– Ví dụ GNU g++ compiler trên Linux/Cygwin
cho chương trình viết = c
• g++ –O5 –o myprog myprog.c
– Có thể cải thiện hiệu năng từ 10% đến 300%
Dùng chỉ thị chương trình dịch
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
6
• Bạn vẫn có thể thực hiện những cải tiến mà trình
dịch không thể
• Bạn phải loại bỏ tất cả những chỗ bất hợp lý
trong code
– Làm cho chương trình hiệu quả nhất có thể
• Có thể phải xem lại khi thấy chương trình chạy
chậm
– Vậy cần tập trung vào đâu để cải tiến nhanh
nhất, tốt nhất ?
Nhưng...
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
7
• Xác định nguồn gây kém hiệu quả
– Dư thừa tính toán - redundant computation
– Chủ yếu
• Trong các functions
• Các vòng lặp: Loops
Writing Efficient Code
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
-
PHẠM VI BIẾN
• Before
• After
8
float f()
{ double value = sin(0.25);
//
...
}
double defaultValue = sin(0.25);
float f()
{ double value = defaultValue;
//
...
}
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
9
• Kiểu dữ liệu Static tham chiếu tới global hay static
variables, chúng được cấp phát bộ nhớ lần đầu khi hàm
được gọi và tồn tại cho đến lúc kết thúc chương trình.
int int_array[100];
int main() {
static float float_array[100];
double double_array[100];
char *pchar;
pchar = new char [100];
/* .... */
return (0);
}
Static Variables
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
10
• Các biến khai báo trong chương trình con được cấp phát bộ nhớ khi
chương trình con được gọi và chỉ bị loại bỏ khi kết thúc chương trình
con.
• Khi bạn gọi lại chương trình con, các biến cục bộ lại được cấp phát và
khởi tạo lại.
• Nếu bạn muốn 1 giá trị vẫn được lưu lại cho đến khi kết thúc toàn
chương trình, bạn cần khai báo biến cục bộ của chương trình con đó là
static và khởi tạo cho nó 1 giá trị.
– Việc khởi tạo sẽ chỉ thực hiện lần đầu tiên chương trình được gọi
và giá trị sau khi biến đổi sẽ được lưu cho các lần gọi sau.
– Bằng cách này 1 chương trình con có thể “nhớ” một vài mẩu tin
sau mỗi lần được gọi.
• Dùng biến Static thay vì Global
– Cái hay của 1 biến static là nó là local của chương trình con =>
tránh được các side efects.
Static Variables
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
11
• Nếu 1 hàm trong c++ chỉ gồm những lệnh đơn
giản, không có for, while... Thì có thể khai báo
inline.
– Inline code sẽ được chèn vào bất cứ chỗ nào
hàm được gọi.
– Chương trình sẽ lớn hơn chút ít
– Nhưng nhanh hơn , không dùng stack– 4
bước khi 1 hàm được gọi
Inline functions
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
-
INLINE FUNCTIONS
#include
#include
inline double delta (double a,
double b)
{
return sqrt (a * a + b * b);
}
void main () {
double k = 6, m = 9;
cout << delta (k, m) << ‘\n’;
cout << sqrt (k * k + m * m) <<‘\n’;
}
12
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
13
#define max(a,b) (a>b?a:b)
• Các hàm Inline cũng giống như macros vì cả 2 được khai triển
khi dịch - compile time
– macros được khai triển bởi preprocessor, còn inline
functions được truyền bởi compiler.
• Điểm khác biệt:
– Inline functions tuân thủ các thủ tục như 1 hàm bình
thường.
– Inline functions có cùng syntax như các hàm khác, chỉ có
điều là có thêm từ khóa inline khi khai báo hàm.
– Các biểu thức truyền như là đối số cho inline functions
được tính 1 lần. Trong 1 số trường hợp, biểu thức truyền
như tham số cho macros có thể được tính lại nhiều hơn 1
lần.
– Bạn không thể gỡ rối cho macros, nhưng với inline
functions thì có thể.
Macros
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
14
• Khi thực hiện , vùng dữ liệu data segment của 1 chương
trình được chia làm 3 phần :
– static, stack, và heap data.
• Static: global hay static variables
• Stack data:
– các biến cục bộ của chương trình con
• ví dụ: double_array trong ví dụ trên.
• Heap data:
– Dữ liệu được cấp phát động (vd, pchar trong ví dụ
trên).
– Dữ liệu này sẽ còn cho đến khi ta giải phóng hoặc khi
kết thúc chương trình.
Stack, heap
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
15
• Nếu bạn phải tính đi tính lại 1 biểu thức, thì nên
tính trước 1 lần và lưu lại giá trị, rồi dùng giá trị
ấy sau này
Tính toán trước các giá trị
int f(int i) {
if (i = 0) {
return i * i - i;
}
return 0;
}
static int values[] =
{0, 0, 2,3*3-3, ..., 9*9-9};
int f(int i) {
if (i = 0) {
return values[i];
}
return 0;
}
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
16
• Đừng tính cùng một biểu thức nhiều lần!
• Một số compilers có thể nhận biết và xử lý.
Loại bỏ những biểu thức thông
thường
for (i = 1; i<=10;i++) x += strlen(str);
Y = 15 + strlen(str);
len = strlen(str);
for (i = 1;i<=10;i++) x += len;
Y = 15 + len;
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
17
• Đừng lặp các biểu thức tính toán không cần thiết
• Một số Compilers có thể tự xử lý
Dịch chuyển những biểu thức bất
biến ra khỏi vòng lặp
for (i =0; i<100;i++)
plot(i, i*sin(d));
M = sin(d);
for (i =0; i<100;i++)
plot(i, i*M);
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
18
• Trình dịch không thể tự động xử lý
Sử dụng các biến đổi số học
if (a > sqrt(b))
x = a*a + 3*a + 2
if (a * a > b)
x = (a+1)*(a+2);
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
19
Không dùng các vòng lặp ngắn
for (i =j; i< j+3;i++)
sum += q*i -i*7;
i = j;
sum += q*i -i*7;
i ++;
sum += q*i -i*7;
i ++;
sum += q*i-i*7;
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
20
• Tránh những kiểm tra không cần thiết
• Trước
Dùng “lính canh”
char s[100], searchValue;
int pos,tim, size ;
//Gán giá trị cho s, searchValue
size = strlen(s);
pos = 0;
while (pos < size) && (s[pos] != searchValue) {
pos++;
}
if (pos >= size) tim = 0;
else tim = 1;
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
21
• Ý tưởng chung
– Đặt giá trị cần tìm vào cuối xâu
– Luôn tìm thấy!
– Nhưng nếu vị trí > size => không tìm thấy !
Dùng “lính canh”
size = strlen(s);
strcat(s, searchValue);
pos = 0;
while ( s[pos] != searchValue)
{ pos++; }
if (pos >= size) tim = 0 else
tim = 1;
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
-
TỐI ƯU ĐOẠN CODE SAU:
found = FALSE;
for (i = 0; i<10000; i++) {
if (list[i] == -99) {
found = TRUE;
}
}
if (found) printf(“Thay -99!\n");
else printf(“Khong co -99!\n");
22
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
23
• Trong mô phỏng Neural Network người ta
thường dùng hàm có tên sigmoid
• Với X dương lớn ta có sigmoid(x) 1
• Với x âm “lớn”
sigmoid (x) 0
Giảm thời gian tính toán
kxe
xsigmoid
1
1
)(
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
Tính Sigmoid
24
float sigmoid (float x )
{
return 1.0 / (1.0 + exp(-x))
};
ex = 1+x/1!+ x2/2! + ... + xn/n!
kxe
xsigmoid
1
1
)(
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
25
• Hàm exp(-x) mất rất nhiều thời gian để tính!
– Những hàm kiểu này người ta phải dùng khai
triển chuỗi
• Chuỗi Taylor /Maclaurin
• Tính tổng các số hạng dạng ((-x)n / n!)
• Mỗi số hạng lại dùng các phép toán với số chấm động
• Các mô phỏng neural network gọi hàm này trăm
triệu lần trong mỗi lần thực hiện.
• Chính vì vậy , sigmoid(x) chiếm phần lớn thời gian
(khoảng 70-80%)
Tính Sigmoid
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
26
• Thay vì tính hàm mọi lúc
– Tính hàm tại N điểm và xây
dựng 1 mảng.
– Trong mỗi lần gọi sigmoid
• Tìm giá trị gần nhất của x và kết
quả ứng với giá trị ấy
• Thực hiện nội suy tuyến tính -
linear interpolation
Tính Sigmoid – Giải pháp
sigmoid(x0) x0
sigmoid(x0) x1
sigmoid(x0) x2
sigmoid(x0) x3
sigmoid(x0) x4
sigmoid(x0) x5
sigmoid(x0) x6
sigmoid(x99) x99
.
.
.
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
Tính Sigmoid
27
sigmoid(x0) x0
sigmoid(x0) x1
sigmoid(x0) x2
sigmoid(x0) x3
sigmoid(x0) x4
sigmoid(x0) x5
sigmoid(x0) x6
sigmoid(x99) x99
.
.
.
if (x > x99) return (1.0);
if (x <x0) return (0.0);
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
28
• Chọn số các điểm (N = 1000, 10000, v.v.) tùy
theo độ chính xác mà bạn muốn
– Tốn kếm thêm không gian bọ nhớ cho mỗi
điểm là 2 float hay double tức là 8 – 16 bytes/
điểm
• Khởi tạo giá trị cho mảng khi bắt đầu thực hiện
Tính Sigmoid
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
29
• Bạn đã biết X0
– Tính Delta = X1-X0
– Tính Xmax = X0 + N * Delta;
• Với X đã cho
– Tính i = (X – X0)/Delta;
• 1 phép trừ số thực và 1 phép chia số thực
– Tính sigmoid(x)
• 1 phép nhân float và 1 phép cộng float
Tính Sigmoid
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
30
• Nếu dùng exp(x) :
– Mỗi lần gọi mất khoảng 300 nanoseconds với
1 máy Pentium 4 tốc độ 2 Ghz.
• Dùng tìm kiếm trên mảng và nội suy tuyến tính :
– Mỗi lần gọi mất khoảng 30 nanoseconds
• Tốc độ tăng gấp 10 lần
– Đổi lại phải tốn kếm thêm từ 64K to 640 K bộ
nhớ.
Results
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
31
• Với đại đa số các chương trình, việc tăng tốc độ
thực hiện là cần thiết
• Tuy nhiên, cố tăng tốc độ cho những đoạn code
không sử dụng thường xuyên là vô ích !
Lưu ý
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
32
• Đặt kích thước mảng = 2n
– Với mảng, khi tạo chỉ số, trình dịch thực hiện
các phép nhân, vì vậy, hãy đặt kích thước
mảng bằng 2n để phép nhân có thể được
chuyển thành phép toán dịch chuyển nhanh
chóng
• Đặt các case trong phạm vi hẹp
– Nếu số case trong câu lệnh switch nằm trong
phạm vi hẹp, trình dịch sẽ biến đổi thành if –
else if lồng nhau, và tạo thành 1 bảng các chỉ
số, như vậy thao tác sẽ nhanh hơn
Optimizing C and C++ Code
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
33
• Đặt các trường hợp thường gặp trong lệnh switch
lên đầu
– Khi số các trường hợp tuyển chọn là nhiều và
tách biệt, trình dịch sẽ biến lệnh switch thành các
nhóm if – else if lồng nhau. Nếu bố trí các case
thường gặp lên trên, việc thực hiện sẽ nhanh hơn
• Tái tạo các switch lớn thành các switches lồng
nhau
– Khi số cases nhiều, hãy chủ động chia chúng
thành các switch lồng nhau, nhóm 1 gồm những
case thường gặp, và nhóm 2 gồm những case ít
gặp=> kết quả là các phép thử sẽ ít hơn, tốc độ
nhanh hơn
Optimizing C and C++ Code
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
-
VÍ DỤ
switch (queue) {
case 0: letter = ‘D'; break;
case 1: letter = ‘H'; break;
case 2: letter = ‘B'; break;
case 3: letter = ‘K'; break;
}
// Hoặc có thể là:
if (queue == 0)
letter = ‘D';
else if (queue == 1)
letter = ‘H';
else if (queue == 2)
letter = ‘B';
else letter = ‘K';
34
static char *classes=“DHBK";
letter = classes[queue];
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
35
• Minimize local variables
– Các biến cục bộ được cấp phát và khởi tạo khi
hàm được gọi, và giải phóng khi hàm kết thúc,
vì vậy mất thời gian
• Khai báo các biến cục bộ trong phạm vi nhỏ
nhất
• Hạn chế số tham số của hàm
• Với các tham số và giá trị trả về > 4 bytes, hãy
dùng tham chiếu
Optimizing C and C++ Code
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
36
• Đừng định nghĩa giá trị trả về, nếu không sử
dụng (void)
• Lưu ý vị trí của tham chiếu tới code và data
– Các dữ liệu hoặc code được lưu trong bộ nhớ
cache để tham khảo về sau (nếu được). Việc
tham khảo từ bộ nhớ cache sẽ nhanh hơn. Vì
vậy mã và dữ liệu được sử dụng cùng nhau thì
nên được đặt với nhau. Điều này với object
trong C++ là đương nhiên. Với C: Không dùng
biến tổng thể, dùng biến cục bộ
Optimizing C and C++ Code
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
37
• Nên dùng int thay vì char hay short (mất thời gian convert), nếu biết
int không âm, hãy dùng unsigned int
• Hãy định nghĩa các hàm khởi tạo đơn giản
• Thay vì gán, hãy khởi tạo giá trị cho biến
• Hãy dùng danh sách khởi tạo trong hàm khởi tạo
Employee::Employee(String name, String
designation) { m_name = name;
m_designation = designation;
}
/* === Optimized Version === */
Employee::Employee(String name, String
designation): m_name(name), m_destignation
(designation) { }
• Đừng định nghĩa các hàm ảo tùy hứng: "just in case" virtual functions
• Các hàm gồm 1 đến 3 dòng lệnh nên định nghĩa inline
Optimizing C and C++ Code
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
38
- Đưa các điều kiện có xác suất xảy ra cao nhất, tính
toán nhanh nhất lên đầu biểu thức.
- Đối với các biểu thức luận lý, ta có thể linh động
chuyển các biểu thức điều kiện đơn giản và xác suất
xảy ra cao hơn lên trước, các điều kiện kiểm tra phức
tạp ra sau.
- Ví dụ: ((A || B ) && C ) => (C && ( A || B )) vì điều
kiện C chỉ cần một phép kiểm tra TRUE, trong khi điều
kiện (A || B) cần đến 2 phép kiểm tra TRUE và một
phép OR (||). Như vậy trong trường hợp C có giá trị
FALSE, biểu thức logic này sẽ có kết quả FALSE và
không cần kiểm tra thêm giá trị (A || B).
Tối ưu các biểu thức điều kiện và
luận lý
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
39
• Đối với các biểu thức kiểm tra điều kiện phức tạp, ta có
thể viết đảo ngược bằng cách kiểm tra các giá trị cho
kết quả không thoả trước, giúp tăng tốc độ kiểm tra.
• Ví dụ: Kiểm tra một giá trị thuộc một miền giá trị cho
trước.
if (p = min && q = min)
{ ... }
else //không thoả
{ ..... }
=>
if (p > max || p max || q < min)
{ ... }
else
{ ... }
Tối ưu các biểu thức điều kiện và
luận lý
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
-
• (x >= min && x <= max) có thể chuyển
thành
(unsigned)(x - min) < (max - min)
• Fact2_func nhanh hơn, vi phép thử != đơn
giản hơn <=
int fact1_func(int n) {
int i, fact = 1;
for (i = 1; i <= n; i++) fact *= i;
return (fact);
}
int fact2_func(int n) {
int i, fact = 1;
for (i = n; i != 0; i--) fact *= i;
return (fact);
}
40
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
41
• Con trỏ (pointer) có thể được gọi là một trong
những “niềm tự hào” của C/C++, tuy nhiên thực tế
nó cũng là nguyên nhân làm đau đầu cho các LTV, vì
hầu hết các trường hợp sụp đổ hệ thống, hết bộ
nhớ, vi phạm vùng nhớ đều xuất phát từ việc sử
dụng con trỏ không hợp lý.
• Hạn chế pointer dereference: pointer dereference là
thao tác gán địa chỉ vùng nhớ dữ liệu cho một con
trỏ. Các thao tác dereference tốn nhiều thời gian và
có thể gây hậu quả nghiêm trọng nếu vùng nhớ đích
chưa được cấp phát.
Tối ưu việc sử dụng bộ nhớ và con
trỏ
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
-
VÍ DỤ
42
for (int i = 0; i < max_number; i++)
{
SchoolData->ClassData->StudentData->Array[i] = my_value;
}
Bằng cách di chuyển các pointer dereference nhiều cấp ra ngoài
vòng lặp, đoạn mã trên có thể viết lại như sau:
unsigned long *Tmp = SchoolData->ClassData->StudentData->Array;
for (int i = 0; i < max_number; i++)
{
Tmp[i] = my_value;
}
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
43
• Để đảm bảo tốc độ truy xuất tối ưu, các bộ vi xử lý
(CPU) 32-bit hiện nay yêu cầu dữ liệu sắp xếp và
tính toán trên bộ nhớ theo từng offset 4-byte. Yêu
cầu này gọi là memory alignment. Do vậy khi biên
dịch một đối tượng dữ liệu có kích thước dưới 4-
byte, các trình biên dịch sẽ bổ sung thêm các byte
trống để đảm bảo các dữ liệu được sắp xếp theo
đúng quy luật. Việc bổ sung này có thể làm tăng
đáng kể kích thước dữ liệu, đặc biệt đối với các cấu
trúc dữ liệu như structure, class
Tận dụng đặc tính xử lý của CPU
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
44
class Test {
bool a;
int c;
int d;
bool b; };
• Theo nguyên tắc alignment 4-byte (hai biến “c” và “d”
có kích thước 4 byte), các biến “a” và “b” chỉ chiếm 1
byte và sau các biến này là biến int chiếm 4 byte, do đó
trình biên dịch sẽ bổ sung 3 byte cho mỗi biến này. Kết
quả tính kích thước của lớp Test sẽ là 16 byte.
Ví dụ
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
45
• Ta có thể sắp xếp lại các biến thành viên của lớp Test
như sau theo chiều giảm dần kích thước:
class Test {
int c;
int d;
bool a;
bool b; };
• Khi đó, hai biến “a” và “b” chiếm 2 byte, trình biên dịch
chỉ cần bổ sung thêm 2 byte sau biến “b” để đảm bảo
tính sắp xếp 4-byte. Kết quả tính kích thước sau khi
sắp xếp lại class Test sẽ là 12 byte.
Ví dụ
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
47
So sánh :
x = x / 3.0;
Và
x = x * (1.0/3.0) ;
?
(biểu thức hằng được thực hiện ngay khi dịch)
Hãy dùng float thay vì double
Tránh dùng sin, exp và log (chậm gấp 10 lần * )
Lưu ý : nếu x là float hay double thì : 3 * (x / 3) x.
Thậm chí thứ tự tính toán cũng quan trọng: (a + b) + c
a + (b + c).
Floating point
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
48
• Tránh dùng ++, -- trong biểu thức lặp
– VD: while ( n--) {}
• Dùng x * 0.5 thay vì x / 2.0.
• x+x+x thay vì x*3
• Mảng 1 chiều nhanh hơn mảng nhiều chiều
• Tránh dùng đệ quy
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
49
• Việc truyền theo tham trị hay tham biến đôi khi dẫn đến
những hiệu ứng phụ bên lề, sau đây là một vài ví dụ
int F(int &x) {
x += 5;
return x;
}
void main() {
int n = 10;
printf(“\n Tong cua F(n) + F(n) = %d “, f(n) + f(n));
n = 10;
printf(“\n Tich cua 2 * f(n) = %d “, 2 * f(n));
}
Các hiệu ứng lề
Kết quả :
35
30
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
void SwapInt(int x, int y )
{
int t;
t= x; x= y;
y= t;
}
void main () {
int m,n ;
printf (“\n‘ m = “);
scanf(“%d”,& m);
printf (“\n n = “);
scanf(“%d”,& n) ;
SwapInt(m,n);
cout << ‘ m = ‘ << m
<< ’ n =‘ << n;
}
int USCLN(int &x,int &y ) {
int t = x % y;
while (t) {
x=y;
y=t;
t=x % y;
}
return y;
}
void main() {
int tu,mau, d ;
cout > tu;
cout > mau;
d = USCLN(tu,mau);
cout <<‘ Dang toi gian cua phan so
là : ’
<< tu / d << ’ / ‘ << mau / d;
}
Hiệu ứng lề .
50
KQ không đổi =>Truyền trỏ hoặc tham
chiếu
KQ luôn = x/1 =>Truyền tham trị
©
C
o
p
yrigh
t Sh
o
w
eet.co
m
51
• Nói chung, không nên lạm dụng biến tổng thể cho toàn
bộ chương trình.
• Chương trình càng lớn thì việc theo dõi các biến và sử
dụng chúng càng phức tạp. Nhầm lẫn có thể xảy ra khi
thay đổi giá trị của 1 biến mà biến đó lại có vai trò quan
trọng trong việc thực hiện của một đoạn nào khác trong
chương trình
• Chương trình lớn => tách thành nhiều chương trình con,
mỗi chương trình con lại có các biến riêng, đôi khi
chương trình con được viết rất lâu sau khi đã viết
chương trình chính và LTV quên mộ