CHƯƠNG 5 : CÂU LỆNH LẶP
5.2. Câu lệnh lặp while
a. Cú pháp
while (<điều kiện>) <lệnh>;
Trong đó:
97
<lệnh> có thể là câu lệnh đơn, khối lệnh, hoặc câu lệnh điều khiển.
b. Ý nghĩa
Chừng nào mà điều kiện cịn thỏa mãn thì thực hiện <lệnh>.
Lệnh while thực hiện như sau:
B1: Kiểm tra <điều kiện>.
B2: Nếu <điều kiện> đúng thì thực hiện <lệnh>, sau đó quay về B1. Còn ngược lại <điều kiện > sai thì chuyển sang B3.
B3: Thốt khỏi vịng lặp, và chuyển quyền điều khiển sang câu lệnh kế tiếp sau lệnh while.
c. Lưu đồ
Hình 14: Sơ đồ hoạt động của lệnh while
Chú ý:
- Do <điều kiện> được kiểm tra trước, nên phần <lệnh> của vòng lặp while có thể khơng được thực hiện lần nào.
- Để bảo đảm cho vịng lặp while (…) khơng xác định, thì phải có ít nhất một câu lệnh trong phần thân vịng lặp có tác dụng làm thay đổi việc đánh giá lại <điều kiện > thoát sau mỗi lần lặp.
Ví dụ 5.7: Sử dụng vịng lặp while để in các số từ 1 đến 10. #include <iostream> #include <math.h> using namespace std; int main () { int i=1; while (i<=10) ĐK False True <Lệnh>
98 { cout << i << endl; i++; } return 0; }
Ví dụ 5.8: Chương trình nhập một câu, đếm số từ và ký tự trong câu đó, và in kết
quả ra màn hình. #include <iostream> #include <math.h> using namespace std; int main () { int demkytu=0; int demtu=1;
cout << "Ban nhap mot cau gom cac chu thuong: " << endl; char ch='a'; while(ch!='\r') { ch=getch(); if(ch==' ') demtu++; else demkytu++; }
cout << "\n So tu trong cau la = " << demtu << endl;
cout << "\nSo ky tu trong cau la = " << demkytu-1 << endl; return 0;
}
Ví dụ 5.9: Chương trình tìm ước chung lớn nhất (UCLN) của 2 số nguyên m và n.
Áp dụng thuật toán Euclide bằng cách liên tiếp lấy số lớn trừ đi số nhỏ khi nào 2
99
Thêm biến phụ r để tính hiệu của 2 số. Sau đó đặt lại m hoặc n bằng r sao cho m > n và lặp lại. Vòng lặp dừng khi m = n. #include <iostream> #include <math.h> using namespace std; int main () { int m, n, r; cout << "Nhập m, n: " ; cin >> m >> n ;
if (m < n) { int t = m; m = n; n = t; } // nếu m < n thì đổi vai trị hai số while (m != n) { r = m - n ; if (r > n) m = r; else { m = n ; n = r ; } } cout << "UCLN = " << m ; }
Ví dụ 5.10: Chương trình nhân 2 số nguyên theo phương pháp Ấn độ int main ()
{
long m, n, kq; // Các số cần nhân và kết quả kq cout << “Nhập m và n: “ ; cin >> m >> n ; kq = 0 ; while (m) { if (m%2) kq += n ; m >>= 1; n <<= 1; } cout << “m nhân n =” << kq ; }
100 5.3. Câu lệnh lặp do while a. Cú pháp do { <lệnh>;
} while (<điều kiện>);
Trong đó:
<điều kiện> thường là biểu thức logic.
<lệnh> có thể là câu lệnh đơn, khối lệnh, hoặc câu lệnh điều khiển.
b. Ý nghĩa
Thực hiện <lệnh> cho đến khi <điều kiện> khơng cịn được thỏa mãn.
Lệnh do while thực hiện như sau:
B1: Thực hiện <lệnh>. B2: Kiểm tra <điều kiện>.
B3: Nếu <điều kiện> đúng thì quay về B1.
Cịn ngược lại nếu <điều kiện> sai thì chuyển sang B4.
B4: Thốt khỏi vịng lặp, và chuyển quyền điều khiển sang câu lệnh kế tiếp sau lệnh do while.
c. Lưu đồ
Hình 15: Sơ đồ hoạt động của lệnh do while.
Chú ý: Khác với vòng lặp while(…), phần <lệnh> trong do.. while (…) luôn được
thực hiện ít nhất là 1 lần, do <điều kiện> được kiểm tra sau.
Trong trường hợp vòng lặp do .. while (…) không xác định, ta nên xem xét và hiệu chỉnh lại các câu lệnh trong phần thân vòng lặp do … while (…) có liên quan đến <điều kiện> thốt của vịng lặp.
<Lệnh>
True
101 Ví dụ 5.11: Chương trình nhập một số nguyên và in kết quả ra màn hình dưới dạng
số đảo ngược về thứ tự của số nguyên đó.
#include <iostream> #include <math.h> using namespace std;
int main () {
long int so1, so2, sodaonguoc = 0;
cout << "Nhap mot so nguyen : " << endl; cin>>so1; so2=so1; do { sodaonguoc=sodaonguoc*10; int digit=so1%10; sodaonguoc+=digit; so1/=10; } while(so1);
cout << "So nguyen da nhap la " << so2 << "." << endl;
cout << "So nguyen dao nguoc la " << sodaonguoc << "." << endl; return 0;
}
Ví dụ 5.12: Kiểm tra một số n có là số nguyên tố.
Để kiểm tra một số n > 3 có phải là số nguyên tố ta lần lượt chia n cho các số i đi từ 2 đến một nửa của n. Nếu có i sao cho n chia hết cho i thì n là hợp số ngược lại n là số nguyên tố. #include <iostream> #include <math.h> using namespace std; int main () {
102 int i, n ; // n: số cần kiểm tra
cout << "Cho biết số cần kiểm tra: " ; cin >> n ; i = 2 ; do { if (n%i == 0) { cout << n << "là hợp số" ; return ; // dừng chương trình } i++; } while (i <= n/2); cout << n << "là số nguyên tố" ; }
Ví dụ 5.13: Chương trình kiểm tra dữ liệu nhập vào có thể là một tháng trong năm hay không: #include <iostream> #include <math.h> using namespace std; int main () { int month; do { cin >> month;
} while (month < 1 || month > 12); return 0;
103
5.4. Câu lệnh nhảy goto
Một dạng khác của rẽ nhánh là câu lệnh nhảy goto cho phép chương trình chuyển đến thực hiện một đoạn lệnh khác bắt đầu từ một điểm được đánh dấu bởi một nhãn trong chương trình.
Lệnh goto thường được sử dụng để tạo vòng lặp. Tuy nhiên việc xuất hiện nhiều lệnh goto dẫn đến việc khó theo dõi trình tự thực hiện chương trình, vì vậy lệnh này thường được sử dụng rất hạn chế.
a. Cú pháp
goto <nhãn> ;
Trong đó, “nhãn” là một tên hợp lệ được sử dụng để đánh dấu vị trí dịng lệnh mà người sử dụng muốn “nhảy” đến.
Vị trí chương trình chuyển đến thực hiện là đoạn lệnh đứng sau nhãn và dấu hai chấm (:).
b. Một số ví dụ
Ví dụ 5.14a: Chương trình nhập vào số nguyên dương. Nếu nhập một số âm, chương trình sẽ sử dụng lệnh goto để nhảy đến nhãn “nhaplai”. Chương trình sẽ lặp lại thao tác nhập và chỉ kết thúc khi người dùng nhập vào một số nguyên dương.
#include <iostream> #include <math.h> using namespace std; int main () { int n; nhaplai: // nhãn
cout << "Nhap so nguyen duong:"; cin >> n;
if (n < 0)
goto nhaplai; // nhảy đến nhãn nhaplai
cout << n << " la so nguyen duong" << endl; return 0;
104 Ví dụ 5.14b: Minh họa lệnh goto
#include <iostream> #include <math.h> using namespace std; int main () { int n=10; loop: ; cout << n << ", "; n--; if (n>0) goto loop; cout << "Kết thúc!"; return 0; }
5.5. Câu lệnh break và continue
a. Lệnh break
Công dụng của lệnh dùng để thoát ra khỏi các câu lệnh cấu trúc, chương trình sẽ tiếp tục thực hiện các câu lệnh tiếp sau câu lệnh vừa thốt.
Từ khóa break được dùng để kết thúc vòng lặp, hoặc cấu trúc switch.
Khi sử dụng trong lệnh switch, từ khóa break thường được đặt tại cuối mỗi khối lệnh mỗi nhãn case.
Ví dụ 5.15:
for (int i = 0; i < 100; i++) {
<lệnh A>; // thực hiện <lệnh A>
break; // dừng vòng lặp for ngay lập tức <lệnh B>; // <lệnh B> sẽ khơng được thực hiện
}
Ví dụ 5.16: Chương trình cho phép người dùng nhập liên tục giá trị n cho đến khi nhập giá trị âm thì dừng.
int main () {
105 int n; while (1) { cout<<“\nNhap n: ”; cin>>n; if(n<0) break; } } b. Lệnh continue
Khi gặp câu lệnh continue trong 1 vịng lặp, chương trình dịch bỏ qua các lệnh còn lại trong thân vòng lặp này để bắt đầu một lần lặp mới.
Sử dụng continue khi cần dừng bước lặp hiện tại, tiếp tục ln bước lặp mới Ví dụ 5.17:
for (int i = 0; i < 100; i++) {
<lệnh A>; // thực hiện <lệnh A>
continue; // trở về đầu vòng lặp, chạy bước mới. <lệnh B>; // <lệnh B> sẽ không được thực hiện. } Ví dụ 5.18: In ra màn hình giá trị từ 10 đến 20 trừ đi số 14 và số 18. #include <iostream> #include <math.h> using namespace std; int main () {
for (int i=10 ; i<=20; i++) {
if (i==14||i==18) continue;
cout<<i<<","; }
106 cout<<"Ket thuc"; } Ví dụ 5.19: Tính tổng các số lẻ từ 1 đến n. #include <iostream> #include <math.h> using namespace std; int main () { int n, sum = 0; cout << "Nhập n = "; cin >> n;
for (int i = 0; i <= n; i++) { if (i % 2 == 0) { continue; } sum += i; }
cout << "Sum = " << sum; return 0;
}
Nhận xét: Khi i là số chắn lúc đó “i % 2 == 0;” trả về true, lệnh continue được thực hiện, dòng lệnh “sum += i;” sẽ được bỏ qua, để nhảy tới cuối thân vòng lặp, thực hiện vòng lặp tiếp theo.
Khi i là số lẻ dòng lệnh “sum += i;” sẽ được thực hiện, để tính tống các số lẻ.
Câu hỏi thảo luận
1. Trình bày cú pháp, ý nghĩa và cách thực hiện các câu lệnh if, while và do while. 2. So sánh sự giống nhau và khác nhau giữa lệnh while và lệnh do while.
3. Trình bày lưu đồ của các lệnh if, while và do while.
4. Trình bày cách dùng các lệnh goto, break và continue. Nêu ưu nhược điểm của các lệnh đó.
107 5. Cho ví dụ minh họa hoạt động của lệnh if, while và do while.
Bài tậpvận dụng
1. Viết chương trình tính giai thừa của một số nguyên n nhập từ bàn phím.
2. Viết chương trình tính dân số của một thành phố sau 10 năm nữa, biết rằng dân số hiện tại là 1.700.000 người, và tỉ lệ tăng dân số hằng năm của thành phố này là 1.7%.
3. Viết chương trình in ra bảng cửu chương.
4. Viết chương trình tìm ước chung lớn nhất, bội chung nhỏ nhất của 2 số nguyên M, N nhập từ bàn phím.
5. Nhập vào 1 số bất kỳ (0->9), cho biết cách đọc số vừa nhập.
6. Viết chương trình in trên màn hình các số từ 1->10, các số ngăn cách nhau bởi 1 đoạn khoảng trắng.
7. Viết chương trình tính tổng: 1 + 2 + 3 + 4 + 5 +….+ 50.
8. Viết chương trình tính tổng: 1*2 + 2*3+ 3*4 + 4*5 +.….+ n(n+1). 9. Viết chương trình tính tích: 1*2*3*4*5*….*n, trong đó n nhập từ phím. 10. Viết chương trình in bảng cửu chương từ 1 đến 5 theo hàng ngang
11. Viết chương trình hiển thị tất cả các số lẻ nhỏ hơn n, trong đó n nhập từ phím. 12. Viết chương trình tính tổng các số chẵn nhỏ hơn n, trong đó n nhập từ bàn phím.
13. Viết chương trình in ra các số là bội số của 5 nhỏ hơn n, trong đó n nhập từ phím.
14. Viết chương trình đếm số lượng số chẵn trong [n,m], trong đó n,m nhập từ phím.
15. Viết chương trình tính tổng các số tự nhiên nhỏ hơn n (sử dụng vòng lặp while)
16. Viết chương trình tìm tổng các số tự nhiên lớn nhất nhỏ hơn 100. 17. Viết chương trình tính tính tiền điện sử dụng trong tháng:
Từ 1 - 100KW: 700đồng Từ 101 - 200KW: 1000đồng Từ 201 - 300KW: 1500 đồng Từ 300KW trở lên: 2000 đồng.
18. Hãy chuyển đổi câu lệnh for thành câu lệnh while: for ( int i = 0; i < 100; i++ )
108 { for ( int j = 0; j < 200; j++ ) cout << setw( 5 ) << i * j; cout << endl ; }
19. Hãy chuyển đổi câu lệnh while thành câu lệnh for int count = 0; while ( count < 100 ) { cout << count; count++; }
20. Số “hoàn thiện” (perfect number) là số tự nhiên có tổng các ước số (kể cả 1) bằng chính nó. VD: số tự nhiên 28 là số hồn thiện. Viết chương trình hiển thị ra màn hình tất cả các số hồn thiện < 100.
21. Viết chương trình nhập vào số n > 0 và hiển thị ra màn hình theo dạng sau: Ví dụ với n = 5. 1 2 3 4 5 2 3 4 5 1 3 4 5 1 2 4 5 1 2 3 5 1 2 3 4
22. Nhập số n nguyên dương, tính và in các số chính phương từ 12 đến n2 ra màn hình, mỗi số một dịng.
23. Viết chương trình tính giá trị số X sau: X =1×2+2×3+⋯+99×100.
24. Nhập số n ngun dương, tính giá trị số Y dưới đây Y =1+1/2+1/3+⋯+1/𝑛.
109
CHƯƠNG 6: CON TRỎ VÀ CẤP PHÁT BỘ NHỚĐỘNG
Mục tiêucủa chương
Nắm vững:
- Khái niệm con trỏ, cách khai báo và sử dụng con trỏ.
- Cấu trúc các tốn tử cấp phát và giải phóng bộ nhớ cho con trỏ. - Vận dụng lý thuyết để giải các bài tập cụ thể.
Nội dungcủa chương
Nghiên cứu các vấn đề liên quan đến việc sử dụng con trỏ và cách cấp phát bộ nhớ động trong ngôn ngữ C++.
6.1. Địa chỉ, phép toán , toán tử tham chiếu *
6.1.1. Địa chỉ, phép toán
Chúng ta đã biết các biến chính là các ơ nhớ mà chúng ta có thể truy xuất dưới các tên. Các biến này được lưu trữ tại những chỗ cụ thể trong bộ nhớ.
Để tạo điều kiện truy nhập dễ dàng trở lại các biến này, bộ nhớ được đánh số, mỗi byte sẽ được ứng với một số nguyên, được gọi là địa chỉ của byte đó từ 0 đến hết bộ nhớ.
Bộ nhớ máy tính chỉ là một dãy gồm các ơ nhớ 1 byte, mỗi ơ có một địa chỉ xác định.
Từ đó ngồi việc thơng qua tên biến người sử dụng cịn có thể thơng qua địa chỉ của chúng để truy nhập vào nội dung. Như vậy biến, ơ nhớ và địa chỉ có quan hệ khăng khít với nhau. C++ cung cấp một tốn tử một ngơi & để lấy địa chỉ của các biến (ngoại trừ biến mảng và xâu kí tự).
Nếu x là một biến thì &x là địachỉ của x.
Kết quả của phép lấy địa chỉ (&) là một con trỏ, do đó có thể dùng để gán cho một biến pointer.
Ví dụ 6.1a: int *px, num;
// px là một pointer chỉ đến biến kiểu int là num. px = #
Chú ý: int *px, num;
px = &(num +1); // sai vì ( num+1) khơng phải là một biến cụ thể
Đối với biến kiểu mảng, thì tên mảng chính là địa chỉ của mảng, do đó khơng cần dùng đến tốn tử &.
110 Ví dụ địa chỉ của mảng a chính là a (khơng phải &a).
Mặt khác địa chỉ của mảng a cũng chính là địa chỉ của byte đầu tiên mà mảng a chiếm và nó cũng chính là địa chỉ của phần tử đầu tiên của mảng a. Do vậy địa chỉ của mảng a là địa chỉ của phần tử a[0] tức &a[0].
Ví dụ 6.1b:
int x; // khai báo biến nguyên x long y; // khai báo biến nguyên dài y
cout << &x << &y; // in địa chỉ các biến x, y char s[9]; // khai báo mảng kí tự s
cout << a; // in địa chỉ mảng s
cout << &a[0]; // in địa chỉ mảng s (tức địa chỉ s[0])
Các phép toán liên quan đến địa chỉ được gọi là số học địa chỉ. Các thao tác được phép trên địa chỉ vẫn phải thông qua các biến trung gian chứa địa chỉ, được gọi là biến con trỏ.
6.1.2. Tốn tử tham chiếu *
Ta có tốn tử tham chiếu được kí hiệu bởi dấu * cho phép lấy giá trị của vùng nhớ có địa chỉ cụ thế.
Xét lại ví dụ 6.1a, ta có:
px là một pointer chỉ đến biến num như ví dụ 6.1a, thì * px là giá trị của biến num. Ví dụ 6.2:
a) //num là biến được khai báo và gán giá trị là 5. int num = 5 ;
int *px; // px là một con trỏ chỉ đến kiểu int px= &num ; //px là địa chỉ của biến num.
/*giá trị của *px (tức là num) cộng thêm 3, gán chok. Sau đó *px thực hiện lệnh tăng 1 đơn vị (++)*/ int k = (* px)++ + 3 ;
// Sau câu lệnh trên num = 6, k = 8. b) int num1 = 2, num2, *pnt;
pnt = &num1 num2 = *pnt;
111 Dòng pnt = &num1 nghĩa là biến con trỏ pnt chứa địa chỉ của biến num1.
Phép gán num2 = *pnt, dấu ‘*’ được đặt ở phía trước biến con trỏ, thì giá trị trả về của biến này l giá trị của biến được trỏ tới bởi con trỏ pnt. Do đó, num2 có giá trị là 2.