[bài tập lập trình C] Tạo một menu cho người dùng nhập lệnh vào

Đề practical 1 môn PFC Part 1 
(FPT University, Summer 2014)
 
Đây là bài tập ví dụ mẫu cho một đề practical test trong môn học PFC – Nhập môn lập trình với ngôn ngữ C của Đại học FPT trong học kỳ Summer 2014. Tất nhiên, có rất nhiều đề, đầy là đề tôi bốc ngẫu nhiên. Nhìn chung đề phù hợp cho sinh viên mới tiếp cận lập trình C như tôi và các bạn. Vì đây là môn học nhập môn nên yêu cầu đề cũng như kiến thức không quá cao, hy vọng nó sẽ giúp ích cho bạn. Vì do cũng lần đầu học về C nên kiến thức còn khá hạn hẹp cũng như kinh nghiệm chưa nhiều. Mục đích bài viết này chỉ để chia sẻ một dạng yêu cầu khi học lập trình C rất phổ biến. Rất mong nhận được ý kiến đóng góp của mọi người.

 
Đây là yêu cầu đề bài số 1 có tính ứng dụng tương đối cao cho các chương trình kiểu console (chương trình chạy trên màn hình đen thui :”> của DOS). Đó là xuất ra một menu, chờ người dùng nhập một giá trị, đọc giá trị người dùng nhập vào, xử lý và chạy hàm phù hợp với giá trị đó.
Yêu cầu số 1: In ra một menu có nội dung như sau:
 
***********************************************************
* Date of Examination:        17/07/2014
* Student Name:                 Nguyen Viet Cong Hau
* Student Number:             SE61546
* Batch:                              06
*
* Plese select the number for appropritate tasks
* 1. Quit program.
* 2. Question 2.
* 3. Question 3.
*
***********************************************************
Input a number to run its procedure:
 
Yêu cầu khá đơn giản, bạn chỉ cần dùng các tổ hợp lệnh gọi hàm printf để xuất ra các nội dung theo cú pháp. prinft(“nội_dung_xuất”) ra màn hình.
 
Sau khi nội dung menu chọn trên được in ra, người dùng khi nhấn phím lệnh 1, 2, 3 thì một tiến trình sẽ được thực thi. Ở đây đề yêu cầu chỉ cần nhập phím 1, hoặc 2, hoặc 3 từ bàn phím là chương trình thực thi không cần phải dùng tới phím enter.
 
Cách giải quyết yêu cầu đề bài:
 
Đầu tiên ta nên viết trước hàm main() cùng các thư viện cần đưa vào chương trình trước. Hãy tạo một file baitapPractical1C.c và nhập vào nội dung sau:
#include <stdio.h>
#include <stdlib.h>
int main()
{
        return 0;
}
 
Chúng ta nên viết một hàm riêng để in menu chính của chương trình ra. Hàm này sẽ dùng lệnh gọi hàm getch() để nhận giá trị nhập vào từ bàn phím, sau đó trả giá trị này về cho nơi gọi hàm để xử lý tiếp. Viết như vậy sẽ dễ quản lý khi chia nhiệm vụ rõ ràng ra cho từng module. Module này chỉ chịu trách nhiệm in nội dung menu và trả về giá trị là lệnh người dùng muốn thực thi (1, 2, 3).
 
Đầu tiên ta nên tạo một phần prototype để định nghĩa về hàm, prototype đó có nội dung như sau: char printMenu(). Vì hàm này sẽ thực hiện yêu cầu là trả về giá trị mà người dùng đã nhập vào bàn phím bên ta thiết lập kiểu giá trị trả về của nó là char. Chúng ta nên đưa câu prototypes này dưới các lệnh include thư viện và trên hàm main. Chủ yếu là cho dễ nhìn 🙂 .
/*
* FPT University
* Course: PFC
* PRACTICAL TEST 1 – PE6
* Professor: Nguyen Trong Tai
* Class: SE0966
* Student: Nguyen Viet Cong Hau
* Student ID: SE61546
*
*/
 
/* inlcude libs */
#include <stdio.h>
#include <stdlib.h>
/* printMenu() function’s protyotype */
char printMenu();
int main()
{
        return 0;
}
 
Sau đó ta đặt phần định nghĩa của hàm ở bất cứ nơi nào của file baitapPractical1C.c , ta nên đặt ở cuối file.

char printMenu()
{
printf(“***********************************************************\n”);

printf(“* Date of Examination: 17/07/2014 *\n”); printf(“* Student Name: Nguyen Viet Cong Hau *\n”);
printf(“* Student Number: SE61546 *\n”);
printf(“* Batch: 06 *\n”);
printf(“* *\n”);
printf(“* Plese select the number for appropritate tasks *\n”);
printf(“* 1. Quit program. *\n”);
printf(“* 2. Question 2. *\n”);
printf(“* 3. Question 3. *\n”);
printf(“* *\n”);
printf(“***********************************************************\n”);printf(“\nInput a number to run its procedure: “);
fflush(stdin);
return getch();
}
 
Giải thích câu lệnh:
Nhóm lệnh gọi hàm printf sẽ in ra các chuỗi là nội dung menu theo đúng yêu cầu đề. Một hàm fflush(stdin) được gọi để làm rỗng bộ nhớ buffer để đảm bảo không còn gì trong buffer có thể truyền vào giá trị trả về của hàm gây sai lệch cho hoạt động của chương trình. Sau đó hàm trả về giá trị là MỘT KÝ TỰ mà người dùng nhập vào từ bàn phím thông qua hàm getch(). Ví dụ người dùng nhập vào 1 thì giá trị ‘1’ của kiểu char sẽ được trả về.
Tại sao lại dùng hàm getch()? Vì đề yêu cầu nhập vào một ký tự từ bàn phím là thực thi ngay nên ta chỉ quan tâm đến việc lấy giá trị của chỉ một ký tự từ bàn phím. Hàm getch() có chức năng lấy một giá trị kiểu char do người dùng nhập vào từ bàn phím và ngay lập tức kết thúc lệnh nhập.
 
Để chạy hàm này thực sự, ta cần gọi nó trong hàm main() – entry point của chương trình.
 
Ta tiến tới vị trí của hàm main() và nhập vào nội dung sau cho hàm main().int main(){char userChoice;

do {
/* clear the screen */
system(“cls”);
/* MS-DOS command for clear the screen */

userChoice = printMenu();

switch(userChoice)
{


case ‘1’: printf(“\n\nThe program is exiting…\n\n”);
break;
case ‘2’: printf(“\nThis is question 2…\n\n”);
break;
case ‘3’: printf(“\nThis is question 3…\n\n”);
break;
case ‘0’:
default:
printf(“\n\nERROR! Your input is invalid!\nPlease, try again…\n\n”);
system(“pause”);

}

} while(userChoice!=’1′);

system(“pause”);
return 0;
}

 
Đầu tiên ta tạo biến userChoice có kiểu dữ liệu là char để lưu vào ký tự mà người dùng nhập vào từ bàn phím. Sau đó một vòng lặp do…while(); được viết để cho sau khi kết thúc mỗi tiến trình, người dùng có thể quyết định có nên chạy tiếp các chức năng khác của chương trình hay kết thúc chương trình.
 
Điều kiện để vòng lặp này chạy là userChoice!=’1′. Vì theo yêu cầu của đề bài, khi người dùng nhập vào 1 sẽ kết thúc chương trình, vì vậy, ta dùng userChoice==’1′ để làm điều kiện kết thúc vòng lặp while này để đồng thời kết thúc chương trình.
 

TẠI SAO LẠI PHẢI DÙNG VÒNG LẶP DO…WHILE
Do ở đây chúng ta chưa cài đặt giá trị mặc định cho biến userChoice nên nếu chỉ dùng vòng lặp while, có thể chương trình của chúng ta sẽ kết thúc mà chưa bao giờ được chạy. Do đó ta dùng cấu trúc do…while(); để đảm bảo nội  dung của vòng lặp, trong đó có việc gán giá trị mới cho userChoice, được thực thi ít nhất một lần.
Thật sự bạn vẫn có thể chỉ dùng vòng lặp while để đáp ứng yêu cầu. Do đó ta nên gán userChoice một giá trị nào đó bất kỳ khác ‘1’ ngay ở nơi tạo nó (char userChoice = ‘0’ chẳng hạn). Tất nhiên, điều này sẽ khó khăn cho việc hiểu đúng mục đích và yêu cầu của đề bài và cũng khó khăn cho người khác khi nhìn mã của bạn.

 
 
Trong vòng lặp do…while(); câu lệnh đầu tiên được thực hiện là lệnh gọi hàm system(“cls”); . Đây là lệnh giúp bạn xóa nội dung của màn hình DOS.
 

** Lưu ý quan trọng: system thật chất gửi một câu lệnh của hệ điều hành để hệ điều hành thực thi. Ví dụ system(“dir C:\”) có thể giúp bạn hiện danh sách thư mục và files con của ổ đĩa C. Do đó câu lệnh system(“cls”); chỉ có tác dụng xóa màn hình trên MS-DOS, nếu bạn dùng Linux, có thể đổi lại thành system(“clear”); – tôi chưa thử, nhưng hy vọng nó chạy được.

 
Sau đó ta thực hiện phép gán cho biến userChoice. Ta gán biến userChoice giá trị trả về của hàm printMenu() đã được trình bày ở trên. Dù ở đây là một phép gán, nhưng nó vẫn không ảnh hưởng tới hoạt động của hàm printMenu(), do vậy các lệnh gọi hàm printf() để in nội dung ra màn hình vẫn được thực thi trước khi thực hiện phép gán giá trị trả về cho biến userChoice ở hàm main().
 
Tiếp theo đó, một cấu trúc rẽ nhánh switch() được viết để phân tích giá trị của biến userChoice sau khi được gán giá trị nhập từ bàn phím của người dùng. Nếu người dùng nhập giá trị ‘1’ từ bàn phím, tiến trình sẽ in ra thông báo The program is exiting… ra màn hình để báo chuẩn bị thoát chương trình và dừng lại cấu trúc switch này bằng lệnh break;.
 

** Lưu ý: Nếu cấu trúc switch sau mỗi lệnh case giá_trị: thực thi lệnh; mà không có break; tiến trình sẽ thực hiện tất cả các lệnh còn lại trong cấu trúc switch cho tới khi gặp một lệnh break hay kết thúc cấu trúc switch.

Sau khi kết thúc cấu trúc switch ở đây, do lúc này userChoice == ‘1’ nên cấu trúc lặp do…while() sẽ dừng lại, và chương trình sẽ kết thúc.
 
Trong trường hợp người dùng nhập vào giá trị ‘2’ và ‘3’, thì tiến trình sẽ nhảy tới hàm của chức năng số 2, 3 mà ta đã giới thiệu ở phần Menu. Nhưng vì ta chỉ mới ở phần một nên tôi chưa giới thiệu sẽ làm gì tiếp ở đây, tôi sẽ giới thiệu bạn trong bài viết tiếp theo. Để kiểm tra sự đúng đắng của mã mà ta viết, ta nên gọi hàm printf() để in nội dung như “this is question 2 solution” hay “this is question 3 solution” để đảm bảo khi người dùng nhập ‘2’, hoặc ‘3’ thì mã của ta sẽ xử lý đúng. Ở nơi case ‘2’: hoặc case ‘3’: ta vẫn phải đưa lệnh break vào để đảm bảo sau khi thực hiện các lệnh của chức năng số 2, 3 sẽ không bị nhảy qua các lệnh khác trong cấu trúc switch.
 

 
Cuối cùng, trong phần default: của cấu trúc switch, ta sẽ thông báo lỗi tới người dùng. Tại sao phải có thông báo lỗi này? Vì theo yêu cầu đề bài, khi người dùng nhập vào ‘1’, ‘2’, hay ‘3’ thì chương trình sẽ làm một điều gì đó. Nhưng nếu người dùng nhập một cái gì đó khác các số ‘1’, ‘2’, hay ‘3’ trên bàn phím ví dụ như nhập vào ‘0’ hay ký tự ‘a’ chẳng hạn thì điều gì sẽ xảy ra? Do đó ta nên để một lệnh default: trong cấu trúc switch để khi nếu userChoice – tức giá trị người dùng nhập vào khác ‘1’, ‘2’ hay ‘3’ thì chương trình không gây lỗi nghiêm trọng mà chỉ in ra thông báo người dùng đã nhập một lệnh không phù hợp.
 

 
Tại sao ở phần default: của switch lại có system(“pause”)? Do ta đang ở trong vòng lặp do…while() tiến trình sẽ kết thúc rất nhanh, nếu người dùng nhập một cái gì đó sai và lệnh sau default: được thực thi thì người dùng cũng sẽ không có cơ hội được thấy nó vì lúc này do userChoice vẫn khác ‘1’, vòng lặp lại tiếp tực thực thi và đầu vòng lặp đã có lệnh xóa màn hình. Vì vậy ta dùng lệnh gọi hàm system(“pause”); để in ra câu “Press any key to continue…”, khi người dùng thấy câu này họ sẽ có thời gian đọc điều gì đang xảy ra và nhấn phím bất kì để quay về nhập lại lệnh.
 

Sau khi vòng lặp do…while() kết thúc (do userChoice==1), tiến trình cũng đi đến lệnh system(“pause”) khác. Điều này chỉ để người dùng kịp nhìn lại chương trình trước khi nó thoát mà trong hđh Windows, cửa sổ DOS sẽ vụt tắt trước khi bạn kịp thấy gì. Một lệnh return 0 sẽ trả về giá trị 0 cho hđh, ta không quan tâm nhiều tới nó lắm.
 
Sau khi viết mã hoàn tất, bạn thử bấm nút biến dịch để dịch mã nguồn của bạn thành chương trình có thể thực thi ( file .exe). Có thể bạn sẽ nhận được thông báo lỗi ở ngay dòng có getch(); . Đó là vì chúng ta chưa #include thư viện <conio.h> vào mã chương trình. Do đó ta đặt lệnh inlcude thư viện này bên dưới lệnh include các thư viện khác.
 

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

Sau đó ta bấm biên dịch lại mã nguồn một lần nữa. Nếu không còn vấn đề gì, chương trình sẽ thực thi. Bạn tự kiểm tra kết quả và sửa lỗi (nếu có). Theo kinh nghiệm cá nhân, lúc viết mã không đem lại kiến thức và kinh nghiệm cần thiết trong lặp trình bằng lúc phát hiện lỗi và sửa lỗi. 🙂

Trong phần tiếp theo của bài này, chúng ta sẽ quan tâm đến “question 2” và “question 3” để viết mã thực thi cho chúng. Cảm ơn sự quan tâm theo dõi của bạn.

Nguyễn Viết Công Hậu.

One thought on “[bài tập lập trình C] Tạo một menu cho người dùng nhập lệnh vào”

Leave a reply to dầu nhớt idemitsu Cancel reply