Thứ Năm, 18 tháng 4, 2024

40.Chương trình tào lao với vòng lặp, mảng, hàm, con trỏ và chuỗi phần 3

Nhắc lại:

Chương trình chưa hoàn hảo thật sự:

  • Mảng sinhVien[] là cho sẵn, chúng ta cần thật sự là một mảng bất kỳ do người sử dụng nhập vào
  • Tổng số phần tử của mảng mặc định là 3, nếu số sinh viên khác thì chương trình sẽ hoạt động sai.

Chúng ta cần biết kích thước của mảng. Tức là ta cần biết số sinh viên. Đơn giản là hỏi người sử dụng và lưu vào một biến. Để bạn quen với cách viết hàm, chúng ta sử dụng hàm cho công việc "dễ ẹc" này

Chúng ta cần một hàm mà giá trị trả về sẽ là một số nguyên int, đai diện cho số sinh viên.
Khai báo hàm sẽ như sau, chúng ta đặt trên main()

    // Khai báo hàm 2
    int soSinhVien();




Định nghĩa hàm, chúng ta sẽ đặt dưới main()

    // Định nghĩa hàm 2
    int soSinhVien(){

    printf("Xin chào Dat Viet Lap Trinh! Day la tong so Sinh Vien\n");
    return 1;

    }

Sau khi thêm vào, chương trình sẽ như sau

#include <stdio.h>

// Khai báo hàm
void danhSach(char *sinhVien[]);
 // Khai báo hàm 2
int soSinhVien();


int main()
{
    char *sinhVien[] = {"Nguyen Van A",
                     "Tran Van B", "Le Thi C"};
                 
    danhSach(sinhVien);
    
    return 0;
}

// Định nghĩa hàm
void danhSach(char *sinhVien[]) {

  printf("Xin chào Dat Viet Lap Trinh! Danh sach Sinh Vien:\n");
 
  for (int i = 0; i < 3; i++)
  {
    printf("%s\n", sinhVien[i]);
  }
}

// Định nghĩa hàm 2
    int soSinhVien(){

    printf("Xin chào Dat Viet Lap Trinh! Day la tong so Sinh Vien\n");
    return 1;

    }


Chúng ta sẽ chạy thử chương trình. Mọi thứ đều ổn



Bây giờ ta sẽ định nghĩa hàm. 

  • Chúng ta khai báo một biến int tongSo;
  • Sau đó dùng lệnh scanf để nhận dữ liệu người dùng nhập vào.
  • Cuối cùng dùng lệnh return tongSo; 

Đến lúc này, chương trình sẽ như sau:

 

#include <stdio.h>

// Khai báo hàm
void danhSach(char *sinhVien[]);
 // Khai báo hàm 2
int soSinhVien();

int main()
{
    char *sinhVien[] = {"Nguyen Van A",
                     "Tran Van B", "Le Thi C"};
                 
    danhSach(sinhVien);
    
    return 0;
}

// Định nghĩa hàm
void danhSach(char *sinhVien[]) {

  printf("Xin chào Dat Viet Lap Trinh! Danh sach Sinh Vien:\n");
 
  for (int i = 0; i < 3; i++)
  {
    printf("%s\n", sinhVien[i]);
  }
}

// Định nghĩa hàm 2
    int soSinhVien(){
    int tongSo;
    printf("Xin chào Dat Viet Lap Trinh! Day la tong so Sinh Vien\n");
    printf("Hãy nhập tổng số sinh viên:  ");
    scanf("%d", &tongSo);
    return tongSo;

    }


Bấm Run để chạy chương trình, mọi thứ ổn. Tuy nhiên ta cần kiểm tra xem hàm có hoạt động đúng không. Ta cần thêm một lệnh printf và lời gọi hàm.

Chương trình của chúng ta sẽ như thế này:


#include <stdio.h>

// Khai báo hàm
void danhSach(char *sinhVien[]);
 // Khai báo hàm 2
int soSinhVien();

int main()
{
    char *sinhVien[] = {"Nguyen Van A",
                     "Tran Van B", "Le Thi C"};
                 
    danhSach(sinhVien);
    soSinhVien();
    
    return 0;
}

// Định nghĩa hàm
void danhSach(char *sinhVien[]) {

  printf("Xin chào Dat Viet Lap Trinh! Danh sach Sinh Vien:\n");
 
  for (int i = 0; i < 3; i++)
  {
    printf("%s\n", sinhVien[i]);
  }
}

// Định nghĩa hàm 2
    int soSinhVien(){
    int tongSo;
    printf("Xin chào Dat Viet Lap Trinh! Day la tong so Sinh Vien\n");
    printf("Hãy nhập tổng số sinh viên:  ");
    scanf("%d", &tongSo);
    printf("Tong so sinh vien là : %d\n", tongSo);
    return tongSo;

    }


Bấm Run để chạy chương trình. Mọi thứ rất ổn.



Vì chương trình của chúng ta có tên là...tào lao, nên ta cứ giữ nguyên mọi thứ, chỉ đắp thêm các nguyên vật liệu khác mà thôi!

Tiếp theo, ta sẽ khai báo một biến số nguyên int size;
Sau đó ta sẽ gán giá trị trả về từ hàm tongSo() cho biến size.
Chúng ta cũng xóa bỏ lời gọi hàm soSinhVien() trong chương trình cũ. Giờ đây chương trình của chúng ta sẽ thế này

#include <stdio.h>

// Khai báo hàm
void danhSach(char *sinhVien[]);
 // Khai báo hàm 2
int soSinhVien();

int main()
{
    char *sinhVien[] = {"Nguyen Van A",
                     "Tran Van B", "Le Thi C"};
    int size;
    size = soSinhVien();

                 
    danhSach(sinhVien);
    //soSinhVien();
    
    return 0;
}

// Định nghĩa hàm
void danhSach(char *sinhVien[]) {

  printf("Xin chào Dat Viet Lap Trinh! Danh sach Sinh Vien:\n");
 
  for (int i = 0; i < 3; i++)
  {
    printf("%s\n", sinhVien[i]);
  }
}

// Định nghĩa hàm 2
    int soSinhVien(){
    int tongSo;
    printf("Xin chào Dat Viet Lap Trinh! Day la tong so Sinh Vien\n");
    printf("Hãy nhập tổng số sinh viên:  ");
    scanf("%d", &tongSo);
    printf("Tong so sinh vien là : %d\n", tongSo);
    return tongSo;

    }


Bấm Run để chạy chương trình




Ta sẽ khai báo một mảng con trỏ sinhVien2[] với kích thước là size; 

char *sinhVien2[size];
 
Lý do chúng ta đặt tên sinhVien2[] vì bản chất chương trình là...tào lao, chúng tôi muốn giữ nguyên mang sinhVien[] khai sẵn giờ đây đã thừa vì không còn sử dụng để bạn hình dung từng bước. Nếu muốn bạn hoàn toàn có thể xóa nó.
 
Tiếp theo, chúng ta cần một biến mảng ký tự để lưu giữ Họ Tên các sinh viên:
 
char hoTen[30];
 
Chúng ta sẽ khởi tạo các phần tử của  mảng con trỏ sinhVien2[] sau đó sẽ truyền tham số cho hàm danhSach().

Tuy nhiên, chúng ta cần thử trước.
 
Chúng ta sử dụng vòng lặp For và lệnh fgets để thêm các phần tử (Tên sinh viên) cho mảng hoTen[].
 
Đây là chương trình. 
 
#include <stdio.h>


// Khai báo hàm
void danhSach(char *sinhVien[]);
 // Khai báo hàm 2
int soSinhVien();

int main()
{
    char *sinhVien[] = {"Nguyen Van A",
                     "Tran Van B", "Le Thi C"};
    int size;
    size = soSinhVien();
    char *sinhVien2[size];
    char hoTen[30];
    
    for(int i = 0; i < size; i++){
       
        printf("Nhap Ho Va Ten Sinh Vien[%d] :\n ", i);
        
        
        fgets(hoTen, sizeof(hoTen), stdin);
        printf("Xin chao: %s", hoTen);
       

    }



    danhSach(sinhVien);
   
    
    return 0;
}

// Định nghĩa hàm
void danhSach(char *sinhVien[]) {

  printf("Xin chào Dat Viet Lap Trinh! Danh sach Sinh Vien:\n");
 
  for (int i = 0; i < 3; i++)
  {
    printf("%s\n", sinhVien[i]);
  }
}

// Định nghĩa hàm 2
    int soSinhVien(){
    int tongSo;
    printf("Xin chào Dat Viet Lap Trinh! Day la tong so Sinh Vien\n");
    printf("Hãy nhập tổng số sinh viên:  ");
    scanf("%d", &tongSo);
    printf("Tong so sinh vien là : %d\n", tongSo);
    return tongSo;

    }


 
 
Khi chạy thử bạn sẽ thấy bị lỗi.
 

 
Đây gọi là lỗi Trôi Lệnh.
Xử Lý Trôi Lệnh

Khi nhập chuỗi ký tự có dấu cách bằng hàm gets() hoặc fgets() thì đôi khi bạn sẽ xảy ra trường hợp bạn chưa kịp nhập nội dung cho chuỗi thì câu lệnh đã được bỏ qua.
Nguyên nhân xảy ra trôi lệnh : hàm gets() (hay fgets()) chỉ dừng nhập khi gặp kí tự xuống dòng. Coi lại chương trình: Trước khi lệnh fgets() thực hiện thì hàm soSinhVien(); đã được gọi và gán giá trị cho biến size. Khi người dùng nhập tổng số sinh viên và nhấn Enter.
Khi hàm fgets() đọc dữ liệu từ luồng vào sẽ gặp phải phím enter dư thừa này và nó sẽ dừng nhập vì bản chất nó gặp enter là dừng. Vì thế nội dung của mảng ký tự bạn định nhập chưa hề có ký tự nào mà câu lệnh fgets() đầu tiên trong vòng lặp For bị trôi qua luôn.

Cách xử lý : Có nhiều cách xử lý tình trạng trôi lệnh, nhưng về bản chất thì ta cần loại bỏ phím enter dư thừa trước khi nhập chuỗi.

Cách 1 : Dùng hàm getchar() để đọc 1 ký tự từ bàn phím, hàm này sẽ đọc phím enter thừa.
Cách 2 : Sử dụng câu lệnh scanf("\n"). Câu lệnh này sẽ bỏ qua mọi ký tự là dấu cách và enter cho tới khi bạn nhập kí tự khác dấu cách và enter.

Chúng ta sẽ sử dụng cách 2 trong chương trình của chúng ta. Bạn thêm lệnh hàm scanf("\n"); vào trước fgets(). Đây là chương trình

#include <stdio.h>


// Khai báo hàm
void danhSach(char *sinhVien[]);
 // Khai báo hàm 2
int soSinhVien();

int main()
{
    char *sinhVien[] = {"Nguyen Van A",
                     "Tran Van B", "Le Thi C"};
    int size;
    size = soSinhVien();
    char *sinhVien2[size];
    char hoTen[30];
    
    for(int i = 0; i < size; i++){
       
        printf("Nhap Ho Va Ten Sinh Vien[%d] :\n ", i);
      
        scanf("\n");
        
        fgets(hoTen, sizeof(hoTen), stdin);
        printf("Xin chao: %s", hoTen);
       

    }


    danhSach(sinhVien);
   
    
    return 0;
}

// Định nghĩa hàm
void danhSach(char *sinhVien[]) {

  printf("Xin chào Dat Viet Lap Trinh! Danh sach Sinh Vien:\n");
 
  for (int i = 0; i < 3; i++)
  {
    printf("%s\n", sinhVien[i]);
  }
}

// Định nghĩa hàm 2
    int soSinhVien(){
    int tongSo;
    printf("Xin chào Dat Viet Lap Trinh! Day la tong so Sinh Vien\n");
    printf("Hãy nhập tổng số sinh viên:  ");
    scanf("%d", &tongSo);
    printf("Tong so sinh vien là : %d\n", tongSo);
    return tongSo;

    }




Chạy thử chương trình, bây giờ mọi thứ đã ổn. 




Tưởng chừng như đã gần xong. Tuy nhiên tới đây chúng ta gặp một vấn đề. Thấy chương trình in ra họ tên sinh viên nhập vào "ngon lành" quá, ta cứ ngỡ chỉ việc "thả vào" mảng sinhVien2[] là xong. Đơn giản mà, cứ thế mà làm thôi:
Để chắc ăn ta thử in luôn kết quả ra, và đây là chương trình
 
#include <stdio.h>

// Khai báo hàm
void danhSach(char *sinhVien[]);
 // Khai báo hàm 2
int soSinhVien();

int main()
{
    char *sinhVien[] = {"Nguyen Van A",
                     "Tran Van B", "Le Thi C"};
    int size;
    size = soSinhVien();
    char *sinhVien2[size];
    char hoTen[30];
    
    for(int i = 0; i < size; i++){
       
        printf("Nhap Ho Va Ten Sinh Vien[%d] :\n ", i);
      
        scanf("\n");
        
        fgets(hoTen, sizeof(hoTen), stdin);
        printf("Xin chao: %s", hoTen);
        
        sinhVien2[i] = hoTen;
        
     
    }

    for(int i = size - 1; i >= 0; i--){
        printf("%s ", sinhVien2[i]);
    }

    danhSach(sinhVien);
   
    
    return 0;
}

// Định nghĩa hàm
void danhSach(char *sinhVien[]) {

  printf("Xin chào Dat Viet Lap Trinh! Danh sach Sinh Vien:\n");
 
  for (int i = 0; i < 3; i++)
  {
    printf("%s\n", sinhVien[i]);
  }
}

// Định nghĩa hàm 2
    int soSinhVien(){
    int tongSo;
    printf("Xin chào Dat Viet Lap Trinh! Day la tong so Sinh Vien\n");
    printf("Hãy nhập tổng số sinh viên:  ");
    scanf("%d", &tongSo);
    printf("Tong so sinh vien là : %d\n", tongSo);
    return tongSo;

    }


 
Bấm Run để chạy thử, kết quả là các phần tử trong sinhVien[] đều giống nhau!



Chúng ta không thể làm như vậy vì cứ sau mỗi vòng lặp giá trị của hoTen không giữ lại. Chúng ta cần sử dụng một hàm trong thư viện <string.h> để như một bộ đệm lưu giữ và gán giá trị cho từng phần tử của mảng.
 

Trước hết chúng ta xóa dòng lệnh sinhVien2[i] = hoTen; thêm thư viện <string.h> vào đầu chương trình. Sau đó ta dùng hàm strndup như sau:

sinhVien2[i] = strndup(hoTen,30);

Đây là chương trình đã sửa lại.

 

#include <stdio.h>
#include <string.h>

// Khai báo hàm
void danhSach(char *sinhVien[]);
 // Khai báo hàm 2
int soSinhVien();

int main()
{
    char *sinhVien[] = {"Nguyen Van A",
                     "Tran Van B", "Le Thi C"};
    int size;
    size = soSinhVien();
    char *sinhVien2[size];
    char hoTen[30];
    
    for(int i = 0; i < size; i++){
       
        printf("Nhap Ho Va Ten Sinh Vien[%d] :\n ", i);
      
        scanf("\n");
        
        fgets(hoTen, sizeof(hoTen), stdin);
        printf("Xin chao: %s", hoTen);
       
        
        sinhVien2[i] = strndup(hoTen,30);

    }

    for(int i = size - 1; i >= 0; i--){
        printf("%s ", sinhVien2[i]);
    }
    danhSach(sinhVien);
   
    
    return 0;
}

// Định nghĩa hàm
void danhSach(char *sinhVien[]) {

  printf("Xin chào Dat Viet Lap Trinh! Danh sach Sinh Vien:\n");
 
  for (int i = 0; i < 3; i++)
  {
    printf("%s\n", sinhVien[i]);
  }
}

// Định nghĩa hàm 2
    int soSinhVien(){
    int tongSo;
    printf("Xin chào Dat Viet Lap Trinh! Day la tong so Sinh Vien\n");
    printf("Hãy nhập tổng số sinh viên:  ");
    scanf("%d", &tongSo);
    printf("Tong so sinh vien là : %d\n", tongSo);
    return tongSo;

    }

Bấm Run để chạy chương trình, ta sẽ thấy mọi thứ đã đâu vào đó theo ý định của chúng ta.



 

 

Chúng ta sẽ tạm dừng ở đây, phần sau ta sẽ hoàn thiện chương trình.

Phần tiếp theo

Phần trước

Không có nhận xét nào:

Đăng nhận xét