Bài giảng học phần Lập trình nâng cao - Chương 2: Vào/ra dữ liệu và các cấu trúc điều khiển chương trình

2.1. Vào/ra dữ liệu 2.2. Các cấu trúc điều khiển chương trình trong ngôn ngữ C 2.1. Vào/ra dữ liệu • Khai báo thư viện chương trình • Lệnh đưa dữ liệu ra màn hình • Lệnh nhập dữ liệu vào từ bàn phím 3Khai báo thư viện chương trình • Để có thể sử dụng các lệnh vào/ra dữ liệu, cần khai báo tệp tiêu đề stdio.h (standard input/output) #include hoặc #include "stdio.h"

pdf66 trang | Chia sẻ: candy98 | Lượt xem: 446 | Lượt tải: 0download
Bạn đang xem trước 20 trang tài liệu Bài giảng học phần Lập trình nâng cao - Chương 2: Vào/ra dữ liệu và các cấu trúc điều khiển chương trình, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
CHƯƠNG 2: VÀO/RA DỮ LIỆU VÀ CÁC CẤU TRÚC ĐIỀU KHIỂN CHƯƠNG TRÌNH BÀI GIẢNG HỌC PHẦN LẬP TRÌNH NÂNG CAO Nội dung 2.1. Vào/ra dữ liệu 2.2. Các cấu trúc điều khiển chương trình trong ngôn ngữ C 2 2.1. Vào/ra dữ liệu • Khai báo thư viện chương trình • Lệnh đưa dữ liệu ra màn hình • Lệnh nhập dữ liệu vào từ bàn phím 3 Khai báo thư viện chương trình • Để có thể sử dụng các lệnh vào/ra dữ liệu, cần khai báo tệp tiêu đề stdio.h (standard input/output) #include hoặc #include "stdio.h" 4 Lệnh đưa dữ liệu ra màn hình (1) • Cú pháp: printf(xâu_định_dạng, danh_sách_tham_số); Trong đó: - xâu_định_dạng: đặt trong cặp dấu " ", quy định cách thức hiển thị dữ liệu ra màn hình máy tính, bao gồm 3 loại ký tự: + Ký tự văn bản thông thường + Ký tự điều khiển + Đặc tả - danh_sách_tham_số: danh sách các giá trị được hiển thị theo quy định trong xâu định dạng, được viết ngăn cách nhau bởi dấu phẩy, gồm: tên biến, tên hằng, biểu thức, hàm, các giá trị cụ thể 5 Lệnh đưa dữ liệu ra màn hình (2) • Lưu ý: danh_sách_tham_số phải phù hợp với các đặc tả trong xâu_định_dạng cả về số lượng, kiểu dữ liệu và thứ tự 6 xâu_định_dạng (1) • Ký tự văn bản thông thường: Được đưa ra màn hình nguyên vẹn như trong xâu_định_dạng Ví dụ: - printf("Xin chao!");  Kết quả hiển thị trên màn hình: Xin chao! - printf("a+b");  Kết quả hiển thị trên màn hình: a+b • Một số ký tự đặc biệt cần đặt liền sau ký hiệu \ \"  in ra ký tự " \\  in ra ký tự \ 7 xâu_định_dạng (2) • Ký tự điều khiển: tạo các hiệu ứng hiển thị đặc biệt \n xuống dòng \t tab \b backspace \r carriage return – đưa con trỏ màn hình về đầu dòng \f line feed – sang trang Ví dụ: printf("\n"); printf("\t"); printf("\n\t"); 8 xâu_định_dạng (3) • Đặc tả: Xác định kiểu dữ liệu của giá trị muốn xuất 9 Đặc tả Ý nghĩa Kiểu dữ liệu %c Kiểu ký tự đơn char %i, %d Số nguyên hệ 10 có dấu int, char %u Số nguyên hệ 10 không dấu unsigned int/char %o Số hệ 8 không dấu (không có số 0 đứng trước) int, char %x, %X Số hệ 16 không dấu chữ thường/chữ hoa(không có 0x đứng trước) int, char xâu_định_dạng (4) • Đặc tả: (tiếp) 10 Đặc tả Ý nghĩa Kiểu dữ liệu %li,%ld Số nguyên hệ 10 có dấu long %lu Số nguyên hệ 10 không dấu unsigned long %lo Số hệ 8 không dấu (không có số 0 đứng trước) long %lx, %lX Số hệ 16 không dấu chữ thường/chữ hoa(không có 0x đứng trước) long xâu_định_dạng (5) • Đặc tả: (tiếp) 11 Đặc tả Ý nghĩa Kiểu dữ liệu %s Hiển thị xâu ký tự kết thúc bởi '\0' char[] %f Số thực dấu phẩy tĩnh float, double %e, %E Số thực dấu phẩy động float, double %g Dùng %f hoặc %e tùy thuộc loại nào ngắn hơn float, double xâu_định_dạng (6) • Ví dụ: int a,b; float x; a=5; b=10; x=12.75; printf("%d",a);  in ra 5 printf("\n%d",b);  in ra 10 printf("\n%d, %d",a,b);  in ra 5, 10 printf("\n%f",x);  in ra 12.750000 printf("\n%f",10.0/3);  in ra 3.333333 12 Độ rộng hiển thị - đối với số nguyên • Định dạng: %nd trong đó n là số nguyên không âm, cho biết số chỗ dùng để viết số nguyên • Ví dụ: int a,b; a=123; b=45; printf("%4d",a); printf ("\n%5d",b); Kết quả hiển thị trên màn hình: ˽123 (˽ là ký hiệu dấu cách) ˽˽˽45 13 Độ rộng hiển thị - đối với số thực (1) • Định dạng: %n.mf Trong đó n, m là 2 số nguyên không âm: n - số chỗ dùng để viết số thực m - số chỗ trong n dùng để viết phần thập phân 14 Độ rộng hiển thị - đối với số thực (2) • Ví dụ: float x; x=9.125; printf("\n%f",x); printf("\n%.2f",x); printf("\n%6.2f",x); Kết quả hiển thị trên màn hình: 9.125000 9.12 ˽˽9.12 15 Độ rộng hiển thị - Lưu ý (1) • Đối với các ký tự và xâu ký tự, định dạng độ rộng hiển thị tương tự như đối với số nguyên • Ví dụ: printf("\n%3d%15s%3c",1,"Tran Ngoc Anh",'A'); printf("\n%3d%15s%3c",2,"Nguyen Sao Mai",'B'); Kết quả hiển thị trên màn hình: ˽˽1˽˽Tran Ngoc Anh˽˽A ˽˽2˽Nguyen Sao Mai˽˽B 16 Độ rộng hiển thị - Lưu ý (2) • Khi số chỗ cần thiết để hiển thị nội dung dữ liệu lớn hơn trong định dạng  hệ thống tự cung cấp thêm chỗ để hiển thị dữ liệu trong các trường hợp: số nguyên, phần nguyên của số thực, ký tự, xâu ký tự • Ví dụ: a=1024; x=10.875; printf("\n%1d",a); printf("\n%3.2f",x); printf("\n%0c",'A'); printf("\n%1s","ABC"); Kết quả: 1024 10.88 A ABC 17 Căn lề trái/phải • Khi hiển thị dữ liệu, mặc định C căn lề phải • Để căn lề trái: thêm dấu - ngay sau ký hiệu % • Ví dụ: printf("\n%-3d%-15s%-3c",1,"Tran Ngoc Anh",'A'); printf("\n%-3d%-15s%-3c",2,"Nguyen Sao Mai",'B'); Kết quả hiển thị trên màn hình: 1˽˽Tran Ngoc Anh˽˽A˽˽ 2˽˽Nguyen Sao Mai˽B˽˽ 18 Lệnh nhập dữ liệu vào từ bàn phím (1) • Cú pháp: scanf(xâu_định_dạng, danh_sách_tham_số); Trong đó: - xâu_định_dạng: chỉ bao gồm các đặc tả quy định cho từng loại dữ liệu được nhập vào - danh_sách_tham_số: bao gồm các địa chỉ của các biến được viết ngăn cách nhau bởi dấu phẩy (&tên_biến_1, &tên_biến_2, ) 19 Lệnh nhập dữ liệu vào từ bàn phím (2) • Lưu ý: danh_sách_tham_số phải phù hợp với các đặc tả trong xâu_định_dạng cả về số lượng, kiểu dữ liệu, thứ tự 20 xâu_định_dạng (1) • Đặc tả: 21 Đặc tả Ý nghĩa Kiểu dữ liệu %c Kiểu ký tự đơn char %d Số nguyên kiểu int int %u Số nguyên không dấu unsigned int %o Số hệ 8 int %x Số hệ 16 int %ld Số nguyên kiểu long long %lo Số kiểu long hệ 8 long %lx, Số kiểu long hệ long xâu_định_dạng (2) • Đặc tả: (tiếp) 22 Đặc tả Ý nghĩa Kiểu dữ liệu %s Xâu ký tự kết thúc bởi '\0' char[] %f Số thực dấu phẩy tĩnh float %lf Số thực dấu phẩy tĩnh double Ví dụ (1) • Chương trình nhập các dữ liệu nguyên, thực, ký tự từ bàn phím rồi hiển thị lại các kết quả đã nhập: #include #include void main() { int a; float x; char c; printf("Nhap so nguyen a: "); scanf("%d",&a); 23 Ví dụ (2) • Chương trình (tiếp): printf("Nhap so thuc x: "); scanf("%f",&x); printf("Nhap ky tu c: "); fflush(stdin); scanf("%c",&c); printf("Cac du lieu da nhap la: a = %d, x = %f, c = '%c'",a,x,c); getch(); } 24 Ví dụ (3) • Chạy thử chương trình: 25 Lưu ý • Khi nhập dữ liệu số: - Hàm scanf coi mọi ký tự số và ký tự dấu . được nhập vào đều là hợp lệ - Để kết thúc việc nhập dữ liệu cần nhấn phím Enter, Tab, hoặc Backspace • Khi nhập dữ liệu dạng ký tự: - Hàm scanf coi mọi ký tự có trong bộ đệm của thiết bị vào chuẩn đều là hợp lệ, kể cả các ký tự Enter, Tab, hay Backspace - Trước khi nhập dữ liệu dạng ký tự/xâu ký tự, cần dùng lệnh fflush(stdin); để xóa bộ đệm 26 Ví dụ (1) • Chương trình tính diện tích, chu vi hình tròn: #include #include const float PI=3.14; int main(void) { float r,dt,cv; printf("Nhap ban kinh r: "); scanf("%f",&r); dt=PI*r*r; cv=2*PI*r; 27 Ví dụ (2) • Chương trình tính diện tích, chu vi hình tròn: (tiếp) printf("Dien tich = %6.2f\n",dt); printf("Chu vi = %6.2f",cv); getch(); return 0; } 28 Ví dụ (3) • Chương trình tính tổng, hiệu, tích, thương của 2 số a,b: #include #include void main() { float a,b,tong,hieu,tich,thuong; printf("Nhap a: "); scanf("%f",&a); printf("Nhap b: "); scanf("%f",&b); tong=a+b; hieu=a-b; 29 Ví dụ (4) • Chương trình tính tổng, hiệu, tích, thương của 2 số a,b: (tiếp) tich=a*b; thuong=a/b; printf("Tong = %6.2f\n",tong); printf("Hieu = %6.2f\n",hieu); printf("Tich = %6.2f\n",tich); printf("Thuong = %6.2f\n",thuong); getch(); } 30 2.2. Các cấu trúc điều khiển chương trình trong ngôn ngữ C • Cấu trúc rẽ nhánh • Cấu trúc lặp 31 Cấu trúc rẽ nhánh • Cấu trúc if • Cấu trúc switch 32 Cấu trúc if • Dạng 1: if (biểu_thức) câu_lệnh; • Hoạt động: Máy tính xác định giá trị của biểu_thức. Nếu biểu_thức nhận giá trị đúng (khác 0) thì thực hiện câu_lệnh, nếu biểu_thức nhận giá trị sai (bằng 0) thì bỏ qua câu_lệnh 33 • Dạng 2: if (biểu_thức) câu_lệnh_1; else câu_lệnh_2; • Hoạt động: Máy tính xác định giá trị của biểu_thức. Nếu biểu_thức nhận giá trị đúng (khác 0) thì thực hiện câu_lệnh_1, nếu biểu_thức nhận giá trị sai (bằng 0) thì thực hiện câu_lệnh_2 • Lưu ý: Biểu_thức có thể nhận giá trị nguyên/thực Ví dụ về cấu trúc if (1) - if (a==0) printf("a la so 0"); - if (diem>=4) printf("Do"); - if (b>0) printf("b la so duong"); 34 - if (a==0) printf("a la so 0"); else printf("a khac 0"); - if (diem>=4) printf("Do!"); else printf("Truot!"); - if (b>0) printf("b la so duong"); else printf("b khong phai la so duong"); Ví dụ về cấu trúc if (2) • Chương trình tính căn bậc hai của một số thực: #include #include #include void main() { float a; printf("Nhap so thuc a = "); scanf("%f",&a); if (a>=0) printf("Can bac hai cua %f = %f",a,sqrt(a)); else printf("%f la so am, khong co can bac hai",a); getch(); } 35 Cấu trúc if lồng nhau (1) • Ví dụ 1: if (a!=0) if (a>0) printf("a la so duong"); else printf("a la so am"); else printf("a la so 0"); 36 • Ví dụ 2: if (a>0) printf("a la so duong"); else if (a<0) printf("a la so am"); else printf("a la so 0"); • Các cấu trúc if có thể viết lồng nhau, khi đó else đi với if gần nhất trước nó Cấu trúc if lồng nhau (2) • Ví dụ 1: if (a!=0) if (a>0) printf("a la so duong"); else printf("a la so am"); 37 • Khi số từ khóa else ít hơn từ khóa if, để tránh nhầm lẫn, nên đưa các cấu trúc if được lồng bên trong vào trong cặp dấu ngoặc {} • Ví dụ 2: if (a!=0) { if (a>0) printf("a la so duong"); else printf("a la so am"); } Cấu trúc if rẽ nhiều nhánh (1) • Khi muốn thực hiện 1 trong n quyết định, có thể sử dụng cú pháp sau: if (biểu_thức_1) câu_lệnh_1; else if (biểu_thức_2) câu_lệnh_2; else if (biểu_thức_n) câu_lệnh_n; else câu_lệnh_n+1; 38 Cấu trúc if rẽ nhiều nhánh (2) • Ví dụ: if (diem>10 || diem<0) printf("Diem nhap vao khong hop le!"); else if (diem>=9) printf("Xuat sac"); else if (diem>=8) printf("Gioi"); else if (diem>=6.5) printf("Kha"); else if (diem>=5) printf("Trung binh"); else if (diem>=3.5) printf("Yeu"); else printf("Kem"); 39 Cấu trúc switch (1) • Dạng 1: switch (biểu_thức) { case hằng_1: các_câu_lệnh; break; case hằng_2: các_câu_lệnh; break; case hằng_n: các_câu_lệnh; } 40 • Dạng 2: switch (biểu_thức) { case hằng_1: các_câu_lệnh; break; case hằng_2: các_câu_lệnh; break; case hằng_n: các_câu_lệnh; break; default: các_câu_lệnh; } Cấu trúc switch (2) • Hoạt động: - Máy tính xác định giá trị của biểu_thức, giả sử bằng ni, khi đó nó sẽ nhảy đến câu lệnh có nhãn case ni và thực hiện tất cả các lệnh sau đó cho đến khi nào gặp break hoặc dấu } kết thúc switch - Khi giá trị biểu_thức khác với mọi ni, máy sẽ nhảy đến câu lệnh có nhãn default (dạng 2) hoặc ra khỏi switch (dạng 1) 41 Cấu trúc switch (3) • Lưu ý: - biểu_thức, hằng_1, hằng_2, , hằng_n phải cùng kiểu và là các kiểu số nguyên hoặc kiểu ký tự (không được là kiểu số thực) - Từ khóa case là bắt buộc phải có trước một hằng. Nếu các_câu_lệnh là giống nhau với một số hằng (giả sử là hằng_i, j, k) thì có thể viết gọn theo cách: case hằng_i: case hằng_j: case hằng_k: các_câu_lệnh; break; 42 Cấu trúc switch (4) • Lưu ý: (tiếp) - hằng_1, hằng_2, , hằng_n phải là các giá trị khác nhau - Cần đưa break (có thể thay thế bằng goto hoặc return tùy từng trường hợp) vào cuối mỗi case, trừ trường hợp case đó là case cuối cùng trong switch (dạng 1, không có default) - Các cấu trúc switch có thể viết lồng nhau 43 Cấu trúc switch (5) • Ví dụ 1: switch (thu) { case 1: printf("Sunday"); break; case 2: printf("Monday"); break; case 3: printf("Tuesday"); break; case 4: printf("Wednesday"); break; case 5: printf("Thursday"); break; case 6: printf("Friday"); break; case 7: printf("Saturday"); } 44 Cấu trúc switch (6) • Ví dụ 2: switch (thu) { case 1: printf("Sunday"); break; case 2: printf("Monday"); break; case 3: printf("Tuesday"); break; case 4: printf("Wednesday"); break; case 5: printf("Thursday"); break; case 6: printf("Friday"); break; case 7: printf("Saturday"); break; default: printf("Nhap sai thu"); } 45 Cấu trúc switch (7) • Chương trình cho người dùng nhập vào tháng và năm, sau đó in ra số ngày tương ứng: #include #include void main() { int thang,nam; printf("Nhap thang = "); scanf("%d",&thang); printf("Nhap nam = "); scanf("%d",&nam); 46 Cấu trúc switch (8) switch (thang) { case 2: if ((nam%4==0 && nam%100!=0) || (nam%400==0)) printf("29 ngay"); else printf("28 ngay"); break; case 4: case 6: case 9: case 11: printf("30 ngay"); break; default: printf("31 ngay"); } getch(); } 47 Cấu trúc lặp • Cấu trúc for • Cấu trúc while • Cấu trúc do while • Lặp vô hạn • break, continue • Nhãn và goto 48 Cấu trúc for (1) • Cú pháp: for (biểu_thức_1;biểu_thức_2;biểu_thức_3) câu_lệnh; trong đó: - biểu_thức_1: dùng để khởi tạo giá trị cho biến điều khiển - biểu_thức_2: dùng để kiểm tra giá trị của biến điều khiển xem vòng lặp có thể tiếp tục hay kết thúc (nhận giá trị đúng tiếp tục, sai kết thúc) - biểu_thức_3: dùng để thay đổi giá trị của biến điều khiển 49 Cấu trúc for (2) • Hoạt động: - Bước 1: Xác định biểu_thức_1 - Bước 2: Xác định biểu_thức_2 - Bước 3: Nếu biểu_thức_2 nhận giá trị sai, thoát khỏi cấu trúc for; ngược lại, nếu biểu_thức_2 nhận giá trị đúng, thực hiện câu_lệnh - Bước 4: Xác định biểu_thức_3, quay lại bước 2 để bắt đầu một vòng lặp mới 50 Cấu trúc for (3) • Lưu ý: - Các biểu_thức_1,2,3 có thể vắng mặt nhưng phải giữ dấu ; - Mỗi biểu_thức_1,2,3 không nhất thiết phải là một biểu thức đơn mà có thể là một dãy các biểu thức được viết ngăn cách nhau bởi dấu , (khi đó các biểu thức được xác định theo chiều từ trái sang phải, tính đúng/sai của dãy biểu thức được xác định là tính đúng/sai của biểu thức cuối cùng trong dãy) - biểu_thức_2 vắng mặt tương đương với việc nó luôn nhận giá trị đúng vòng lặp vô hạn - Các cấu trúc for có thể viết lồng nhau 51 Cấu trúc for (4) • Một số ví dụ: - for(i=0;i<10;i++) printf("%d\n",i); - j=10; for(;j<=99;j+=2) printf("%d\n",j); - k=11; for(;k<99;) { printf("%d\n",k); k+=2; } 52 - for(i=0;;i++) { if (i>=10) break; printf("%d\n",i); } - for(i=0;i<=9;i++) { for(j=0;j<=9;j++) printf("%5d",i+j); printf("\n"); } Cấu trúc for (5) • Chương trình tính tổng s = 1 + 2 + + n #include #include void main() { int i,n,s; printf("Nhap n = "); scanf("%d",&n); for(s=0,i=1;i<=n;i++) s+=i; printf("Tong s = %d",s); getch(); } 53 Cấu trúc while (1) • Cú pháp: while (biểu_thức) câu_lệnh; • Hoạt động: - Bước 1: Xác định giá trị của biểu_thức - Bước 2: Nếu biểu_thức nhận giá trị sai, thoát khỏi while; nếu biểu_thức nhận giá trị đúng, thực hiện câu_lệnh rồi quay lại bước 1 • Ví dụ: i=0; while (i<10) { printf("%d\n",i); i++; } 54 Cấu trúc while (2) • Lưu ý: - Biểu_thức có thể là một dãy các biểu thức được viết ngăn cách nhau bởi dấu , - Nếu ngay từ đầu, biểu_thức nhận giá trị sai thì không có bất kỳ lệnh nào được thực hiện - Các cấu trúc while có thể viết lồng nhau 55 Cấu trúc while (3) • Chương trình tìm ước số chung lớn nhất của 2 số nguyên dương a, b: #include #include void main() { int a,b; printf("Nhap 2 so nguyen duong a, b:"); printf("\na = "); scanf("%d",&a); printf("b = "); scanf("%d",&b); 56 Cấu trúc while (4) • Chương trình: (tiếp) while (a!=b) { if (a>b) a=a-b; else b=b-a; } printf("UCLN = %d",a); getch(); } 57 Cấu trúc do while (1) • Cú pháp: do các_câu_lệnh; while (biểu_thức) • Hoạt động: - Bước 1: Thực hiện các_câu_lệnh - Bước 2: Xác định giá trị của biểu_thức. Nếu biểu_thức nhận giá trị sai, thoát khỏi do while; nếu biểu_thức nhận giá trị đúng, quay lại bước 1 • Ví dụ: i=0; do { printf("%d\n",i); i++; } while (i<10); 58 Cấu trúc do while (2) • Lưu ý: - Biểu_thức có thể là một dãy các biểu thức được viết ngăn cách nhau bởi dấu , - Nếu ngay từ đầu, biểu_thức nhận giá trị sai thì các_câu_lệnh vẫn được thực hiện 1 lần trước đó - Các cấu trúc do while có thể viết lồng nhau 59 Cấu trúc do while (3) • Chương trình tìm ước số chung lớn nhất của 2 số nguyên dương a, b: #include #include void main() { int a,b; printf("Nhap 2 so nguyen duong a, b:"); printf("\na = "); scanf("%d",&a); printf("b = "); scanf("%d",&b); 60 Cấu trúc do while (4) • Chương trình: (tiếp) do { if (a>b) a=a-b; else if (b>a) b=b-a; } while (a!=b); printf("UCLN = %d",a); getch(); } 61 Lặp vô hạn • Xét các ví dụ: - for (i=0;;i++) printf("%d\n",i); - for (i=0;i>=0;i++) printf("%d\n",i); - i=5;n=10; while (i<n) printf("%d\n",i); - i=5;n=10; while (i<n) { printf("%d\n",i); i--; } 62 - i=5; do { printf("%d\n",i); i--; } while (i<10); -  Khi sử dụng for, while, do while cần chú ý đến tính dừng break, continue • break: - Được sử dụng trong các cấu trúc switch, for, while, do while - Cho phép thoát ra khỏi cấu trúc chứa nó 63 • continue: - Được sử dụng trong các cấu trúc for, while, do while, không sử dụng trong switch - Cho phép bỏ qua lần lặp hiện tại: + Chuyển tới bước xác định biểu_thức_3 rồi quay lại vòng lặp mới đối với for + Chuyển tới bước xác định giá trị của biểu thức đối với while và do while Nhãn và goto (1) • Nhãn: - Đánh dấu câu lệnh để chương trình có thể nhảy tới câu lệnh đó - Cú pháp: nhãn: câu_lệnh; • goto: - Điều khiển chương trình nhảy đến câu lệnh được gán nhãn tương ứng - Cú pháp: goto nhãn; 64 Nhãn và goto (2) • Ví dụ: #include #include void main() { int i,n,s; printf("Nhap n = "); scanf("%d",&n); s=0; i=1; lap: s+=i; 65 if (i<n) { i++; goto lap; } printf("Tong s = %d",s); getch(); } Nhãn và goto (3) • Lưu ý: - go to và nhãn cần nằm trong một hàm - goto cho phép nhảy từ vị trí câu lệnh này tới câu lệnh khác trong thân một hàm nhưng không cho phép nhảy từ hàm này sang hàm khác - goto không cho phép nhảy từ ngoài vào trong một khối lệnh nhưng cho phép nhảy từ trong ra ngoài khối lệnh 66
Tài liệu liên quan