1.6. Các lệnh cấu trúc

Chương trình mà chúng ta tạo ra cần có khả năng tự đưa ra quyết định trong một số trường hợp. Để làm được điều này, các lập trình viên sử dụng thứ mà họ gọi là các lệnh cấu trúc. Hơi trừu tượng nhỉ? Cụ thể hơn thì chúng được chia thành 2 nhóm mà chúng ta sẽ tìm hiểu trong bài học này:

  • Các lệnh điều kiện : cho phép chúng ta đinh nghĩa những quy tắc dạng như "Nếu trường hợp này xảy ra thì hãy làm thế này".
  • Các vòng lặp : chó phép thực hiện lặp lại nhiều lần liên tiếp một chuỗi hành động

Hiểu cách sử dụng các lệnh cấu trúc là cơ bản trong cơ bản của lập trình. Dù theo tôi thì bài học này không quá khó, tuy nhiên các bạn cần chú ý hiểu rõ các khái niệm cơ bản. Chúng không chỉ hữu dụng trong suốt quá trình bạn sử dụng C++ mà còn có thể áp dụng trong các ngôn ngữ lập trình khác nữa vì chúng sử dụng 1 cơ chế chung.

Lệnh điều kiện

Để chương trình có thể tự đưa ra quyết định, chúng ta cần sử dụng trong mà nguồn các lênh điều kiện ( hay các cấu trúc điều kiện ). Yêu cầu rất đơn giản : ta muốn chương trình sẽ đưa ra phản ứng khác nhau trong những tình huống khác nhau. Chúng ta sẽ tìm hiểu sau đây các lệnh này trong C+.

Để bắt đầu, các bạn cần biết rằng các lệnh điều kiện cho phép kiểm tra các biến. Hẳn là các bạn vẫn còn nhớ tới các biến được lưu trong các ô nhớ ở bài học trước chứ? Phần này sẽ dạy bạn cách đưa ra các câu hỏi kiểu như "Giá trị của biến này có lớn hơn 10?", "Trong này có chứa siêu cấp mật khẩu?", vv...

Để thực hiện việc kiểm tra, chúng ta cần sử dụng các dấu so sánh. Bảng các dấu so sánh dưới đây cần phải được bạn thuộc nằm lòng :

 Các dấu so sánh   Ý nghĩa
 ==  bằng
 >  lớn hơn
 <  nhỏ hơn
 >=  lớn hơn hoặc bằng
 <=  nhỏ hơn hoặc bằng
 !=  khác

 ! Cần đặc biệt chú ý là trong C++, để kiểm tra 2 giá trị có bằng nhau không, chúng ta dùng dấu == chứ không phải =. Những người mới thường quên mất điều này và chỉ dùng 1 dấu =. Kết quả đưa ra hoàn toàn không phải điều mà bạn mong chờ.

Chúng ta sẽ sử dụng các dấu này trong câu lệnh điều kiện của mình. Trong C++ thì có nhiều loại lệnh điều kiện nhưng mà cái quan trọng nhất cần phải nắm được, đó là lệnh điều kiện if.

Lệnh điều kiện if

Lại nhắc lại lần nữa, lệnh điều kiện dùng để kiểm tra các biến. Tôi đề nghị các bạn hay cùng tôi viết 1 chương trình để kiểm tra xem bạn có hiểu rõ cơ chế của nó không.

Chúng ta bắt đầu với đoạn mã :

#include <iostream>

using namespace std;

int main(){
    int soCon(2);

    return 0;
}

Chương trình này vẫn chưa làm gì cả ngoài việc khai báo 1 biến soCon ( chứa giá trị số con cái của bạn ) và kết thúc

Lệnh if đầu tiên

Chúng ta muốn là chương trình in ra lời chúc mừng là bạn đã sinh ra được những đứa trẻ xinh đẹp và giỏi giang. Điều kiện cần xét đến là chỉ in thông điệp nếu số con cái lớn hơn 0.

#include <iostream>

using namespace std;

int main(){
    int soCon(2);

    if (soCon > 0){
        cout << "Chuc mung ban da sinh ra nhung dua tre xinh dep va gioi giang !" << endl;
    }

    cout << "Ket thuc" << endl;
    return 0;
}

Kết quả chúng ta nhận được

Quan sát ky dòng sau đây if (soCon > 0)

Chính nó thực hiện việc kiểm tra xem số con cái có lớn hơn 0 không ("if" là từ tiếng Anh nghĩa là "nếu"). Nếu phép kiểm tra thành công ( tức là có con cái ), vậy máy tính sẽ đọc những lệnh bên trong dấu ngoặc nhọn {}, ở đây là lệnh in thông báo ra màn hình.

? Chuyện gì xảy ra nếu 1 người không có con cái ?

Trong trường hợp đấy, phép thử thất bại và máy tính sẽ không đọc các lệnh trong dấu {} mà nhảy thẳng đến lệnh sau dấu }. Lúc đấy, chỉ có thông điệp Ket thuc là được in ra.

Hãy viết lại chương trình! Thay giá trị của soCon thành 0 và bạn sẽ thấy.

else : phản ứng xảy ra nếu phép thử thất bại

Bạn muốn là chương trình sẽ có phản ứng cụ thể nào đó khi phép thử thất bại ? Tôi thừa nhận là vừa rồi chương trình hơi bị im tiếng khi bạn không có con cái.

Thật tốt là chúng ta có thể dùng từ khóa else nghĩa là "nếu không". Chúng ta sẽ hiện 1 thông báo khác cho những người không có con.

#include <iostream>

using namespace std;

int main(){
    int soCon(0);

    if (soCon > 0){
        cout << "Chuc mung ban da sinh ra nhung dua tre xinh dep va gioi giang !" << endl;
    }else{
        cout << "O, hay tan huong tu do khi con chua co bon tre !" << endl;
    }

    cout << "Ket thuc" << endl;
    return 0;
}

Và cho ra kết quả

Chú ý kỹ là tôi đã thay đổi giá trị của soCon ở đầu chương trình nhé.

Nếu bạn thay vào đó là 1 giá trị lớn hơn 0, thông điệp bạn nhận được ban đầu sẽ lại hiện ra.

Chuyện gì đã xảy ra thế ? Rất đơn giản, máy tính làm phép thử cho điều kiện ở trong lệnh if và thấy thất bại vì điều kiện thành công là soCon phải lớn hơn 0. Vậy là máy tính bỏ qua những lệnh ở trong dấu {} đầu tiên và đi tới chỗ else và nó thực hiện những lệnh được viết trong {} ngay sau else.

else if :  thực hiện tiếp 1 lệnh điều kiện khác

Ta có thể thực hiện nhiều điều kiện liên tiếp nhau. Thử nghĩ đến trường hợp sau :

  • Nếu số con cái bằng 0 thì in "..."
  • Nếu không nếu số con cái bằng 1 thì in "..."
  • Nếu không nếu số con cái bằng 2 thì in "..."
  • Nếu không thì in "..."

Để thực hiện phép thử cái này tiếp sau cái kia theo thứ tự, chúng ta sẽ sử dụng else if có nghĩa là nếu không nếu... Các phép thử sẽ được kiểm tra lần lượt tới khi nào 1 trong số chúng được thỏa mãn.

#include <iostream>

using namespace std;

int main(){
    int soCon(2);

    if (soCon == 0){
        cout << "Ban chua co con ?" << endl;
    }else if (soCon == 1){
        cout << "Bao gio thi den dua thu 2 day ?" << endl;
    }else if (soCon == 2){
        cout << "Ban da sinh ra nhung dua tre that xinh dep va gioi giang !" << endl;
    }else{
        cout << "Chac la nen dung lai thoi nhi?" << endl;
    }

    cout << "Ket thuc" << endl;
    return 0;
}

Trroong khá phức tạp nhỉ ? Thật ra thì cũng không đến nỗi.

Đặt trường hợp là bạn có 2 con :

  1. Máy tính kiểm tra xem bạn có 0 con ?
  2. Điều này không đúng nên chuyển qua else if và máy tính kiểm tra nêu bạn có 1 con.
  3. Vẫn không đúng nên chuyên qua else if thứ 2, máy tính sẽ kiểm tra nếu bạn co 2 con.
  4. Phép thử thành công và máy tính in ra "Ban da sinh ra nhung dua tre that xinh dep va gioi giang  !"

Nếu không phép thử nào thành công thì thông điệp trong else là "Chac nen dung lai thoi nhi?" được in.

Lệnh điều kiện switch

Trên lý thuyết thì lệnh if cho phép ta thực hiện tất cả các phép thử mà ta muốn. Trong thực tiễn thì vẫn còn những cách khác để đưa ra các phép thử. Ví dụ nổi bật nhất là lệnh điều kiện switch cho đơn giản phép thử nhiều giá trị khác nhau của cùng 1 biến.

Quay lại với ví dụ ta vừa mới làm ở trên nhé:

Bạn có 0 con? Bạn có 1 con? Bạn có 2 con? ....

 Kiểu phép thử liên tiếp này có thể đươc thực hiện nhờ các lệnh else if liên tục nhưng cũng có thể dùng lệnh switch với mục đích giúp đoán mã dễ đọc và dễ hiểu hơn. Đây là 1 phiên bản khác của đoạn mã bên trên.

#include <iostream>

using namespace std;

int main(){
    int soCon(2);

    switch (soCon){
        case 0:
            cout << "Ban chua co con ?" << endl;
            break;

        case 1:
            cout << "Bao gio thi den dua thu 2 day ?" << endl;
            break;

        case 2:
            cout << "Ban da sinh ra nhung dua tre that xinh dep va gioi giang !" << endl;
            break;

        default:
            cout << "Chac la nen dung lai thoi nhi?" << endl;
            break;
    }

    return 0;
}

Và kết quả nhận được là

Kết cấu có đôi chút khác biệt. Ở dòng thứ 8, chúng ta nói cho máy tính biết là chúng ta sẽ phân tích biến soCon. Sau đó chúng ta kiểm tra tất cả các trường hợp (case) giá trị là 0, 1, 2,...

Lệnh break là bắt buộc phải có ở đây vì máy tính không dừng việc kiểm tra dù là phép thử với 1 giá trị đã thành công. Theo kinh nghiệm thì tôi khuyên các bạn thêm nó vào cuối mỗi lệnh case.

Cuối cùng, phép thử default ở cuối tương tự như else, sẽ được chạy nếu không có phép thử nào trong những giá trị cung cấp trước đó cho kết quả thành công.

!Lệnh switch chỉ cho phép thử phép so sánh bằng. Điều đó nghĩa là bạn không thể kiểm tra "Số lượng con cái có lớn hơn 2?". Trong trường hợp đó thì bạn phải dùng lệnh if. Ngoài ra switch chỉ có thể làm việc với kiểu dữ liệu số nguyên như int, unsigned int, char (vì trong C++, ký tự được coi là 1 số nguyên đặc biệt) chứ không hoạt động với số thực như double. Về cơ bản thì switch chịu khá nhiều hạn chế nhưng bù lại cung cấp cho ta một phương thức lệnh khác trong những trường hợp đơn giản.

Kiểu bool và điều kiện kết hợp

 Hãy tìm hiểu sâu thêm về các điều kiện. Chúng ta sẽ khám phá 2 khái niệm hơi nâng cao : kiểu bool và điều kiện kết hợp.

Kiểu bool

Các bạn còn nhớ kiểu dữ liệu bool chứ? Nó chỉ có thể chúa được 2 giá trị : true (đúng) và false (sai).

Kiểu dữ liệu này rất hay được sử dụng với các lệnh điều kiện. Rất hợp lý đúng không, bởi vì một điều kiện thì cũng chỉ có thể là đúng hoặc sai và biến kiểu bool cũng thế.

Nếu tôi nói về kiểu dữ liệu này thì là tại vì trong các lệnh điều kiện, nó được sử dụng 1 cách rất đặc biệt. Trước tiên, hãy bắt đầu với đoạn mã sau.

bool truongThanh(true);

if (truongThanh == true){
    cout << "Ban la nguoi truong thanh!" << endl;
}

Điều kiện này kiểm tra 1 biến truongThanh và in ra 1 thông điệp nếu biến đấy có giá trị true.

Các bạn đang thắc mắc về ý định của tôi phải không? Cái tôi muốn nói là với kiểu bool trong câu điều kiện thì phần == true có thể được bỏ qua ! Đoạn mã dưới đây hoàn toàn tương đương.

bool truongThanh(true);

if (truongThanh){
    cout << "Ban la nguoi truong thanh!" << endl;
}

Máy tính hiểu rằng bạn muốn kiểm tra xem giá trị của truongThanh có phải là true không mà không cần thêm == true.

? Như thế không khiến mã khó đọc hơn à ?

Không hề, ngược lại, đoạn mã sẽ ngắn hơn và dễ đọc hơn! if (truongThanh) có thể dễ dàng hiểu là "Nếu bạn là người trưởng thành".

Tôi đề nghị các bạn sử dụng cách viết rút gọn này khi kiểm tra các biến kiểu bool để mã ngắn và dễ đọc hơn.

Các điều kiện kết hợp

Một điều cần biết là chúng ta có thể đồng thời thực hiện nhiều phép thử cho nhiều điều kiện khác nhau trong cùng 1 câu lệnh if. Để làm điều này thì chúng ta cần thêm một số dấu mới. Dưới đây là các dấu mà chúng ta sẽ dùng để kết hợp các điều kiện và ý nghiã của chúng

     &&          và
     ||      hoặc
     !      phủ định    

 

Dấu

Hãy giả định rằng chúng ta muốn kiểm tra xem một người là người trưởng thành và đồng thời người đó có ít nhất 1 đứa con.

if (truongThanh && soCon >= 1)

Dấu && có nghĩa là và, vậy nên lệnh điều kiện của chúng ta có thể được dịch là "Nếu 1 người là người trưởng thành có ít nhất 1 đứa con".

! Hãy để ý là tôi cúng có thể viết là if (truongThanh == true && soCon >= 1) nhưng như đã nhắc đến ở bên trên, chúng ta có thể bỏ == true đi.Đây là 1 thói quen tôi cho rằng các bạn nên học.

Dấu hoặc

Để thực hiện phép hoặc, chúng ta dùng dấu || . Dấu này bao gồm 2 dấu | mà trên bàn phím QWERTY chúng ta có thể gõ bằng cách giữ phím shift và phím \ nằm bên gần phím Enter.

Một ví dụ phép thử 1 người có 1 hoặc 2 con.

if (soCon == 1 || soCon == 2)

Dấu phủ định

Đê phủ định, chúng ta sẽ sử dụng dấu !. Trong tin học thì dấu này có nghĩa là "không".

Bạn có thể đặt dấu này trước mệnh đề đang xét và nó sẽ có nghĩa là "nếu mệnh đề không đúng".

if (!truongThanh)

... có nghĩa là nếu một người chưa trưởng thành.

Vòng lặp 

Những vòng lặp giống như tên gọi, cho phép chúng ta lặp đi lặp lại một chuỗi hành động trong chương trình. Nguyên tắc như sau :

  • Máy tính thực hiện chạy các dòng lệnh từ trên xuống dưới như bình thường
  • Khi đến cuối vòng lặp, máy tính quay lại dòng lệnh đầu tiên
  • Lặp lại chuỗi lệnh từ đầu tới cuối
  • ... và lại quay lại dòng đầu tiên

Vòng lặp sẽ được lặp đi lặp lại khi mà một mênh đề còn đúng. Ví dụ như, "Khi nào mà người dùng còn nhập 1 số âm cho biến lưu trữ thông tin về số con cái, máy tính sẽ yêu cầu nhập lại".

Có 3 kiêu vòng lặp cần biết :

  • while
  • do ... while
  • for
Vòng lặp while

Vòng lặp này trông như sau : 

while (<điều kiện>){
    /* Chuoi lenh can lap */
}

Tất cả các câu lệnh nắm giữa dấu {} sẽ được lặp đi lặp lại khi nào mà dieuKien còn đúng.

Hãy thử thực hiện những gì mà tôi nói bên trên : Yêu cầu người dùng nhập lại số con cái khi nào cho đến khi nào dữ liệu nhập vào không nhỏ hơn 0. Kiểu vòng lặp này thường được dùng khi chúng ta muốn chắc chắn dữ liệu người dùng nhập vào phù hợp với yêu cầu.

int main(){
  int soCon(-1); // So am de bat dau vong lap
  while ( soCon < 0 ){
    cout << "Ban co bao nhieu dua con ?" << endl;
    cin >> soCon;
  }

  cout << "Cam on vi da nhap dung so con. Ban co " << soCon << " con." << endl;
  return 0;
}

1 ví dụ khi chạy chương trình trên

Khi nào mà bạn còn nhập vào một số âm thì vòng lặp còn bắt đầu lại. while ( soCon < 0) có nghĩa là "Khi nào mà số lưu trong biến soCon còn âm". Lần đầu tiên bạn nhập vào một số lớn hơn hoặc bằng 0, vòng lặp sẽ kết thúc và tiếp tục sau dấu {}.

!Các bạn hẳn nhận ra tôi đã khởi tạo biến soCon với giá trị -1. Để máy tính có thể bước vào vòng lặp lần đầu tiên thì một giá làm cho mệnh đề đúng ở thời điểm đó là cần thiết.

Vòng lặp do ... while

Kiểu vòng lặp này rất giống với vòng lặp trước, chỉ khác mỗi là điều kiện sẽ được kiểm tra ở cuối vòng lặp. Điều này có nghĩa là vòng lặp sẽ được thực hiện ít nhất một lần.

Vòng lặp này trông như sau :

do{
  /* Chuoi lenh can lap */
} while (<điều kiện>);

Hãy chú ý là có dấu ; nằm ở cuối cùng !

Viết lại đoạn mã bên trên với vòng do ... while

int main(){
  int soCon(0);

  do{
    cout << "Ban co bao nhieu dua con ?" << endl;
    cin >> soCon;
  } while (soCon < 0);
  
  cout << "Cam on vi da nhap dung so con. Ban co " << soCon << " con." << endl;
 
  return 0;
}

Nguyên tắc là như nhau, khi chạy chương trình cũng nhận được kết quả giống nhau. Lợi ích duy nhất là do ... while đảm bảo vòng lặp được thực hiện ít nhất 1 lần.

Thêm vào đó, không cần thiết phải khai báo giá trị khởi đầu là -1 nữa. Trên thực tế, số con cái được khởi tạo với giá trị 0 giống như chúng ta vẫn làm và bời vì điều kiện chỉ được kiểm tra sau khi đã thực hiện xong 1 lần vòng lặp, chuổi các lệnh sẽ được thực hiện ít nhất 1 lần.

Vòng lặp for

Kiểu vòng lặp này có 3 yếu tố :

  • Khởi điểm
  • Điều kiện
  • Phép tăng bước

Vòng lặp này có cấu trúc như sau:

for(<khởi điểm>; <điều kiện>; <tăng bước>){

}

Hãy xem ví dụ cụ thể chúng ta dùng để in ra màn hình các số từ 0 tới 9.

int main(){
  int chiSo(0);

  for (chiSo = 0 ; chiSo < 10 ; chiSo++){
    cout << chiSo << endl;
  }

  return 0;
}

Và đây là kết quả.

Ở dòng lệnh for, chúng ta tìm thấy 3 yếu tố đã được nhắc ở trên.

  • Khởi điểm chiSo = 0 : Biến chiSo được khởi tạo với giá trị 0 ở đàu vòng lặp. Vì việc này vừa được làm ngay trong dòng lệnh trước đó, dòng lệnh này trở nên không cần thiết
  • Điều kiện chiSo < 10 : Máy tính sẽ kiểm tra điều kiện này mỗi khi bắt đầu 1 vòng lặp mới
  • Phép tăng bước chiSo++ : Ở cuối mỗi vòng lặp, ta sẽ tăng giá trị của chiSo lên 1. Đây là lý do mà chúng ta hiển thì ra màn hình được các số từ 0 đến 9.

! Bạn có thể làm 1 xử lý khác ngoài phép tăng bước ở phần cuối của vòng for. Thật ra phần này được dành để thay đổi giá trị của biến để dùng cho vòng lặp tiếp theo. Vậy nên nếu muốn, bạn có thể làm chiSo-- hoặc chiSo += 2 cũng không sao cả

Cần biết là việc khai báo biến trực tiếp ở trong vòng for cũng rất hay được các lập trình viên sử dụng.

int main(){
  for (int chiSo(0) ; chiSo < 10 ; chiSo++) {
    cout << chiSo << endl;
  }

  return 0;
}

 Và biến chiSo sẽ chỉ có tồn tại lúc xử lý vòng for chứ không phải trong toàn bộ thời gian hoạt động của chuong trình. Đây là dạng hay gặp nhất của vòng lặp này. Chúng ta sẽ chỉ khai báo biến bên ngoài vòng for nếu cần nó cho mục đích sau này. Mà việc này thì khá hiếm.

! Khi nào thì chúng ta sử dụng vòng for và khi nào thì dùng vòng while?

Chúng ta dùng vòng for khi xác định được số lần chúng ta muốn lặp chuỗi hành động và vòng while cho những trường hợp không xác định được chỉ số này.

Tóm tắt bài hoc :
  • Lệnh điều kiện dùng để kiểm tra giá trị của biến và thay đổi xứ lý của chương trình cho phù hợp.
  • Lệnh điều kiện if ... else if ... else nghĩa là "nếu ... nếu không thì nếu ... nếu không" là dạng hay gặp nhất của lệnh điều kiện
  • Lệnh điều kiện switch đặc biệt hơn cho phép kiểm tra những giá trị khác nhau của cùng 1 biến
  • Vòng lặp cho phép ta thực hiện chuỗi hành động 1 cách lặp đi lặp lại
  • Có 3 loại vòng lặp là for, whiledo ... while
  • Vòng for dùng khi mà chúng ta biết chính xác số lần mà chúng ta muốn lặp lại chuỗi hành động trong khi 2 vòng còn lại thường dùng để lặp số lần không xác định đến khi nào điều kiện không được thỏa mãn nữa.