----> Lời khuyên cho các bạn bắt đầu học lập trình: nếu có tham khảo code trên mạng thì các bạn nên xem nó và tự viết lại rồi tìm hiểu xem từng phần trong code ý nó có mục đích là gì chứ đừng nên copy và paste một cách máy móc mà mình chả hiểu được code ý nó viết gì.
Như mình đã nói, lập trình cấu trúc (structured programming) được xây dựng dựa theo mô hình toán học của Bohm và Guiseppe. Theo đó một chương trình máy tính có thể được viết dựa trên ba cấu trúc là: tuần tự, rẽ nhánh và lặp. C++ được thiết kế không chỉ hỗ trợ lập trình hướng đối tượng mà còn cả cho lập trình cấu trúc vì vậy nó cung cấp những cấu trúc điều khiển (control flows) để thực hiện việc cài đặt các cấu trúc trên. Bài này mình sẽ giới thiệu chi tiết cách sử dụng cũng như những đánh giá sơ lược về các cấu trúc lựa chọn (selection), lặp (iteration) và các lệnh nhảy (jump statements).
1. Các lệnh lựa chọn (selection statements)
Trong cuộc sống chúng ta luôn luôn phải ra những quyết định. Nếu bạn gái bạn phải lòng một gã nào đó bạn sẽ làm gì? Có rất nhiều phương án cho bạn lựa chọn:
Trong lập trình cũng vậy, bạn sẽ phải đưa ra những lựa chọn trong những hoàn cảnh cụ thể. C++ cung cấp 2 lệnh lựa chọn là if (mà dạng đầy đủ là if … else) và switch.
a. Câu lệnh if (if statement)
Cú pháp cho câu lệnh if là:
if(condition){
// câu lệnh ở đây
}
// dạng đầy đủ
if(condition){
// câu lệnh ở đây
}
else{
// câu lệnh ở đây
}
Hoạt động của câu lệnh if (dạng đầy đủ) như sau: nếu điều kiện đúng (true) thì chương trình sẽ thực hiện câu lệnh ngay sau if nếu sai thì nó sẽ thực hiện câu lệnh ngay sau else. Chương trình sau đây minh họa cho hoạt động của lệnh if. Nó nhận một số nguyên nhập và từ bàn phím và xác định xem số đó là chẵn (even) hay lẻ (odd).
using namespace std;
int main(){
int n;
cout << "Enter an integer: ";
cin >> n;
if(n%2){
cout << n << " is ODD" << endl;
}
else{
cout << n << " is EVEN" << endl;
}
return 0;
}
Các câu lệnh if có thể lồng nhau (nested if) rất hay gặp. Một điều cần chú ý là else sẽ tương ứng với if gần nó nhất. Chương trình sau mô tả lệnh if lồng nhau.
using namespace std;
int main(){
int n;
cout << "Enter an integer in range from 0 to 2: "; // gợi ý nhập vào 1 số nguyên từ 0 đến 2
cin >> n;
if(n==0){ // nếu là số 0
cout << "Zero" << endl;
}
else{ // nếu không là số 0
if(n==1){ // nếu là số 1
cout << "One" << endl;
}
else{ // nếu cũng không là số 1
if(n==2){ // nếu là số 2
cout << "Two" << endl;
}
else{ // nếu cũng không là số 2
cout << "Ivalid" << endl;
}
}
}
return 0;
}
Tuy nhiên, nhìn chương trình trên thật “rối rắm” và đau mắt. Một phong cách lập trình biểu thị cấu trúc if-else-if lồng nhau hay được sử dụng là “thang if-else-if” (if-else-if-ladder). Chương trình trên sẽ được viết lại như sau.
using namespace std;
int main(){
int n;
cout << "Enter an integer in range from 0 to 2: ";
cin >> n;
if(n==0){
cout << "Zero" << endl;
}
else if(n==1){
cout << "One" << endl;
}
else if(n==2){
cout << "Two" << endl;
}
else{
cout << "Invalid" << endl;
}
return 0;
}
b. Câu lệnh switch
Khi phải đối mặt với nhiều hơn hai lựa chọn thì ta có thể dùng thang if-else-if như trên. Tuy nhiên C++ cung cấp một cách khác, trong một số trường hợp hiệu quả hơn, đó là lệnh switch. Cú pháp cho câu lệnh switch như sau.
case constant1:
// các câu lệnh ở đây
break;
case constant2:
// các câu lệnh ở đây
break;
case constant3:
// các câu lệnh ở đây
break;
...
default:
// các câu lệnh ở đây
}
Biểu thức expression của switch phải có giá trị nguyên (có thể là char, integer hoặc bool). Hoạt động câu lệnh switch như sau: giá trị của expression sẽ được so sánh lần lượt với giá trị của các case (tức là các constant1, constant2, …). Nếu phát hiện ra trường hợp nào “khớp” thì những câu lệnh sau case đó được thực hiện. Chú ý rằng nếu như không có lệnh nào “ngắt” (ví dụ break; chẳng hạn) thì nó cứ thế chạy tuột đến cuối thậm chí những case sau đó không khớp. Các câu lệnh sau nhãn default sẽ được thực hiện nếu như chạy qua hết các case mà không có trường hợp nào khớp. Chúng ta sẽ xem xét hai chương trình sử dụng switch sau đây.
#include <iostream>
using namespace std;
int main(){
int n;
cout << "Enter an integer in range 0-5: ";
cin >>n;
switch(n){
case 0:
cout << "Zero" << endl;
case 1:
cout << "One" << endl;
case 2:
cout << "Two" << endl;
case 3:
cout << "Three" << endl;
case 4:
cout << "Four" << endl;
case 5:
cout << "Five" << endl;
default:
cout << "Invalid" << endl;
}
return 0;
}
Điều gì sẽ xảy ra khi bạn nhập vào số 3. Kết quả sẽ như sau:
Như mình đã nói ở trên, một khi đã khớp với một trường hợp nào đó thì “tất cả những câu lệnh phía sau case đó sẽ được thực thi”. Vì vậy ta cần phải có cái gì đó để “ngắt” tại những thời điểm phù hợp và giải pháp là sử dụng lệnh break. Hãy xem xét phiên bản thứ hai của chương trình trên.
using namespace std;
int main(){
int n;
cout << "Enter an integer in range 0-5: ";
cin >>n;
switch(n){
case 0:
cout << "Zero" << endl;
break;
case 1:
cout << "One" << endl;
break;
case 2:
cout << "Two" << endl;
break;
case 3:
cout << "Three" << endl;
break;
case 4:
cout << "Four" << endl;
break;
case 5:
cout << "Five" << endl;
break;
default:
cout << "Invalid" << endl;
}
return 0;
}
Bằng cách sử dụng lệnh break; chương trình sẽ hoạt động theo ý muốn của chúng ta. Khi gặp lệnh break; chương trình sẽ nhảy ra khỏi lệnh switch mà không thực thi những câu lệnh còn lại trong khối.
Bây giờ ta sẽ so sánh một chút giữa lệnh if và switch:
2. Các câu lệnh lặp (iteration statements)
C++ cung cấp 3 câu lệnh lặp là for, while, và do while. Một đoạn chương trình viết được bởi một trong ba câu lệnh trên thì cũng có thể viết được qua hai câu lệnh còn lại. Chúng là tương đương nhau. Điều này thuộc về vấn đề programming style. Trong một số trường hợp, vòng lặp này có thể thích hợp hơn những cái còn lại.
a. Vòng lặp for
Cú pháp chung cho vòng lặp for như sau:
// câu lệnh ở đây
}
Vòng for sẽ thực hiện việc lặp cho đến khi biểu thức điều kiện tiếp tục vòng lặp không còn đúng nữa. Chương trình sau mô tả sự họa động của vòng lặp for. Nó in ra bảng chữ cái (hoa) từ A-Z.
#include <iostream>
using namespace std;
int main(){
for(char i='A'; i<='Z'; i++){
cout << i << endl;
}
return 0;
}
Bây giờ ta sẽ chế biến vòng for một chút để biết thêm một số cách sử dụng vòng for. Hãy xem xét chương trình sau:
#include <iostream>
using namespace std;
int main(){
int x, y;
for(x=0, y=9; (x<=9) && (y>=0); x++, y--){
cout << "x= " << x << " " << "y= " << y << endl;
}
system("pause");
return 0;
}
Để ý header của vòng for
Nhận thấy biểu thức khởi tạo có tới hai câu lệnh gán x=0 và y=9 được phân cách nhau bởi dấu phẩy (,). C++ cho phép chúng ta khởi tạo bao nhiêu biến cũng được miễn là các câu lệnh gán này phân cách nhau bởi dấu phẩy. Dấu phẩy này thực ra là một toán tử (comma operator), nó quy định các toán hạng ở hai bên của nó được đánh giá từ trái sang phải, có thể phát biểu như sau: “hãy thực hiện việc này, rồi việc này, rồi việc này …. “, trong trường hợp của ta thì nó có nghĩa là: “hãy gán x=0 rồi gán y=9”. Tương tự biểu thức thay đổi biến điều khiển cũng vậy: “hãy tăng x lên 1 đơn vị rồi giảm y đi một đơn vị”.
Header của vòng for có thể khuyết đi một số phần, hoặc khuyết hết cũng chả sao. Miễn là dấu chấm phẩy ngăn cách giữa các phần vẫn đủ là được. Ví dụ chương trình sau.
using namespace std;
int main(){
int i=0;
for( ; ; ){ // header vòng for rỗng
int n;
cout << "\nIf you enter a negative number, program will exit";
cout << "\nYour number: ";
cin >> n;
if(n<0)
break;
else
cout << "Number is: " << n << endl;
}
return 0;
}
b. Vòng lặp while
Cú pháp cho vòng lặp while như sau.
// câu lệnh ở đây
}
Hoạt động của vòng while rất đơn giản. Nếu điều kiện vẫn còn đúng thì vòng while vẫn còn được lặp lại, tức là các câu lệnh bên trong vòng while vẫn được thực hiện, nó sẽ lặp cho tới khi điều kiện sai mới thôi. Chương trình sau sẽ sử dụng vòng while để thực hiện việc in ra số đảo ngược của một số nhập vào từ bàn phím.
using namespace std;
int main(){
int n;
cout << "Enter an integer: ";
cin >> n;
while(n){
cout << n%10;
n/=10;
}
cout << endl;
return 0;
}
c. Vòng lặp do while
Vòng lặp for và while đều kiểm tra điều kiện ở đầu vòng lặp, vì vậy có thể xảy ra khả năng ngay ở lần kiểm tra đầu tiên điều kiện tiếp tục vòng lặp đã không thỏa mãn. Và do đó các câu lệnh trong thân vòng for và do while sẽ không được thực hiện lần nào. Khác với for và while, vòng lặp đo while thực hiện câu lệnh trước rồi mới kiểm tra điều kiện ở cuối vòng lặp. Vì vậy các câu lệnh bên trong vòng do while luôn được thực hiện ít nhất là một lần. Ngoài khác biệt duy nhất đó ra thì while và do while là hoàn toàn giống nhau. Cú pháp của vòng do while là.
// câu lệnh ở đây
} while(condition);
3. Các câu lệnh nhảy (jump statements)
Khi chương trình thực thi một vòng lặp, nếu thỏa mãn một số điều kiện nào đó nó có thể thoát ra ngoài vòng lặp mà không cần phải thực hiện nốt các câu lệnh còn lại trong thân vòng lặp. Để làm được được ddieuf này C++ hỗ trợ 4 câu lệnh nhảy (jump statements) là: break, continue, goto và return. Tuy nhiên return là một trường hợp đặc biệt được sử dụng để trả lại giá trị cho hàm nên mình sẽ không nói ở đây.
a. Sử dụng continue
Chúng ta dùng continue khi muốn kết thúc sớm lần lặp hiện tại để chuyển qua lần lặp tiếp theo. Khi gặp câu lệnh continue; chương trình sẽ bỏ qua mọi câu lệnh còn lại bên trong vòng lặp để chuyển qua lần lặp mới. Chương trình sau mô tả hoạt động của continue.
#include <iostream>
using namespace std;
int main(){
for(int i=0; i<=100; i++){
if(i%4) continue; // nếu không chia hết cho 4
cout << i << endl;
}
system("pause");
return 0;
}
b. Sử dụng break
continue chỉ dùng để kết thúc sớm một lần lặp, rồi lại chuyển ngay qua lần lặp tiếp theo. Để thoát hoàn toàn khỏi vòng lặp thì ta phải sử dụng lệnh break. Khi chương trình gặp câu lệnh break, nó sẽ thoát luôn khỏi vòng lặp và tiếp tục thực thi câu lệnh tiếp theo ngay sau vòng lặp. Điều này hoàn toàn giống như trong ví dụ về lệnh switch mà mình đã đề cập ở trên. Chương trình sau mô tả cách hoạt động của lệnh break cho vòng lặp while.
#include <iostream>
using namespace std;
int main(){
int i=100; // 100 là số tự nhiên nhỏ nhất có 3 chữ số
while(i<=999){ // 999 là số tự nhiên lớn nhất có 3 chữ số
if(i%13==0){ // nếu i chia hết cho 13
cout << i << endl;
break;
}
i++; // nếu không duyệt tiếp
}
return 0;
}
Chú ý: khi có nhiều vòng lặp lồng nhau thì lệnh break chỉ thoát ra khỏi vòng lặp hiện tại, chứ không phải thoát hết ra khỏi mọi vòng lặp.
c. Sử dụng goto
Câu lệnh goto là lệnh rẽ nhánh vô điều kiện (unconditional branch statement). Kinh nghiệm của các tiền bối đi trước cho thấy sử dụng lệnh goto sẽ là chương trình trở nên rối rắm và khó kiểm soát. Người ta thường gọi những đoạn code tối nghĩa như vậy là “spaghetti code”. Tuy nhiên có một vài trường hợp nó tỏ ra khá hữu dụng. Ví dụ khi ta muốn thoát từ vòng lặp trong cùng của một khối các vòng lặp lồng nhau, ra bên ngoài thì dùng goto là một phương án tốt, hoặc khi tốc độ xử lý chương trình là mối quan tâm hàng đầu thì goto có thể được chọn. Để sử dụng goto ta chỉ cần chỉ định một nhãn (label) để nó “nhảy tới”. Một nhãn là một định danh hợp lệ theo sau bởi dấu hai chấm. Chương trình sau in ra 100 số nguyên dương đầu tiên sử dụng lệnh goto.
#include <iostream>
using namespace std;
int main(){
int i=1;
begin: // nhãn
cout << i << endl;
i++;
if(i<=100)
goto begin; // nhảy tới nhãn
return 0;
}
Chú ý: goto chỉ nhảy từ bên trong khối lệnh ra bên ngoài khối lệnh chứ không thể nhảy từ bên ngoài vào bên trong khối lệnh được.