Giáo trình Lập trình cơ bản - Chuyên ngành: Kỹ thuật lắp ráp, sửa chữa máy tính - Trường Cao đẳng công nghiệp Hải Phòng
Bạn đang xem 20 trang mẫu của tài liệu "Giáo trình Lập trình cơ bản - Chuyên ngành: Kỹ thuật lắp ráp, sửa chữa máy tính - Trường Cao đẳng công nghiệp Hải Phòng", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
Tài liệu đính kèm:
- giao_trinh_lap_trinh_co_ban_chuyen_nganh_ky_thuat_lap_rap_su.pdf
Nội dung text: Giáo trình Lập trình cơ bản - Chuyên ngành: Kỹ thuật lắp ráp, sửa chữa máy tính - Trường Cao đẳng công nghiệp Hải Phòng
- UBND TỈNH HẢI PHÒNG TRƯỜNG CAO ĐẲNG CÔNG NGHIỆP HẢI PHÒNG GIÁO TRÌNH LẬP TRÌNH CƠ BẢN Chuyên ngành: Kỹ thuật lắp ráp, sửa chữa máy tính (Lưu hành nội bộ) HẢI PHÒNG
- LỜI GIỚI THIỆU Ngôn ngữ lập trình C là một ngôn ngữ mệnh lệnh được phát triển từ đầu thập niên 1970 bởi Ken Thompson và Dennis Ritchie để dùng trong hệ điều hành UNIX. Từ đó, ngôn ngữ này đã lan rộng ra nhiều hệ điều hành khác và trở thành một những ngôn ngữ phổ dụng nhất. C là ngôn ngữ rất có hiệu quả và được ưa chuộng nhất để viết các phần mềm hệ thống , mặc dù nó cũng được dùng cho việc viết các ứng dụng. Ngoài ra, C cũng thường được dùng làm phương tiện giảng dạy trong khoa học máy tính. Giáo trình Ngôn ngữ lập trình dùng cho học sinh, sinh viên hệ Cao Đẳng và Trung cấp của tất cả các nghề chuyên về Công Nghệ thông tin trong trường Cao đẳng nghề Công nghệ Việt - Hàn Bắc Giang nhằm cung cấp cho học sinh, sinh viên các thuật toán tổng quát, từ đó sinh viên sẽ từng bước cải tiến thuật toán để xây dựng được những chương trình hiệu quả và có tính ứng dụng cao. Ngoài ra,còn trang bị cho sinh viên những kiến thức làm tiền đề để chuẩn bị cho một số môn học tiếp theo, như: cấu trúc dữ liệu, lập trình hướng đối tượng, C#, Mục đích của giáo trình là trang bị cho học viên những kiến thức và kỹ năng: Phân tích được thuật toán. Có được kỹ năng lập trình cấu trúc thông qua một số thuật toán quan trọng gồm: các kỹ năng về lập trình cấu trúc, mảng, chuỗi ký tự, sắp xếp và tìm kiếm, . Phân tích được một bài toán và thiết kế thuật giải cho bài toán đó. Sử dụng các kiểu dữ liệu, các cấu trúc điều khiển, các giải thuật, khai báo và sử dụng hàm Trong qua trình biên soạn giáo trình, tác giả đã cố gắng cập nhật thông tin mới, đồng thời tham khảo nhiều giáo trình khác, nhưng chắc chắn sẽ không tránh khỏi những hạn chế nhất định. Rất mong nhận được ý kiến đóng góp của các nhà chuyên môn, các anh chị đồng nghiệp và các bạn đọc để giáo trình được hoàn thiện hơn. TỔ BỘ MÔN TIN HỌC Trang 3
- MỤC LỤC TUYÊN BỐ BẢN QUYỀN 2 LỜI GIỚI THIỆU 3 MỤC LỤC 4 CHƯƠNG 1: TỔNG QUAN VỀ NGÔN NGỮ LẬP TRÌNH C 6 1.1. Giới thiệu sơ lược về ngôn ngữ lập trình C 6 1.1.1. Lịch sử phát triển ngôn ngữ C 6 1.1.2. Các bước cơ bản khi viết chương trình trên C 7 1.2. Các phần tử cơ bản của C 9 1.2.1. Tập ký tự cơ bản 9 1.2.3. Định danh (Tên) - Identifier 9 1.2.4. Kiểu dữ liệu 10 1.2.5. Hằng 11 1.2.6. Biến 12 1.2.7. Hàm 12 1.2.8. Biểu thức 13 1.2.9. Câu lệnh 13 1.2.10. Chú thích 13 1.3. Cấu trúc cơ bản của chương trình C 14 1.4. Cài đặt và sử dụng turbo C 15 1.4.1. Cài đặt 15 1.4.2. Sử dụng môi trường Turbo 16 CHƯƠNG 2: KHỞI ĐẦU VỚI LẬP TRÌNH C 17 2.1. Một số khai báo cơ bản 17 2.1.1. Khai báo tệp tiêu đề 17 2.1.2. Khai báo hằng 17 2.3.2. Lệnh nhập dữ liệu 28 2.3.3. Lệnh gán 29 2.3.4. Các lệnh nhập xuất khác 30 CHƯƠNG 3: CÁC LỆNH CẤU TRÚC 31 3.1. Lệnh khối 31 3.2. Lệnh lựa chọn 32 3.2.1. Lệnh lựa chọn theo điều kiện if 32 3.2.2. Lệnh switch 35 3.3. Lệnh lặp 38 Trang 4
- 3.3.1. Lệnh for 38 3.3.2. Lệnh while 40 3.3.3. Lệnh do while 42 CHƯƠNG 4: HÀM TRONG C 45 4.1. Khái niệm về hàm và thư viện 45 4.1.1. Chương trình con (Subprogram) 45 4.1.2. Một số đặc tính của hàm trong C 46 4.1.3. Thư viện hàm chuẩn 46 4.2. Quy trình xây dựng hàm 47 4.2.1. Khai báo hàm 47 4.2.2. Sử dụng hàm 49 4.2.3. Quy tắc hoạt động của hàm 51 4.2.4. Tính tổng thể và cục bộ của biến 51 CHƯƠNG 5: DỮ LIỆU KIỂU MẢNG 54 5.1. Khái niệm và khai báo mảng 54 5.1.1. Khái niệm về mảng 55 5.1.2. Khai báo và sử dụng mảng 55 5.2. Một số bài toán với mảng 1 chiều 57 5.2.1. Nhập dữ liệu cho mảng 57 5.2.2. Hiện mảng dữ liệu 58 5.2.3. Tìm giá trị lớn nhất/nhỏ nhất (Max/Min) 59 5.2.4. Sắp xếp mảng 60 5.2.5. Tìm kiếm trong mảng 62 5.3. Một số bài toán với mảng 2 chiều 63 5.3.1. Nhập dữ liệu cho mảng 2 chiều 63 5.3.2. Hiện dữ liệu từ mảng 2 chiều 64 5.3.3. Các bài toán về ma trận 64 CHƯƠNG 6: XÂU KÝ TỰ (STRING) 66 6.1. Khái niệm và khai báo xâu 67 6.1.1. Khái niệm về xâu ký tự 67 6.1.2. Khai báo và sử dụng biến xâu 67 6.2. Một số hàm xử lý ký tự và xâu 68 6.2.1. Hàm xử lý ký tự 68 6.2.2. Hàm xử lý xâu 69 6.3. Một số chương trình xử lý dữ liệu kiểu xâu 72 Trang 5
- THỰC HÀNH 73 TH bài 1: CÀI ĐẶT, SỬ DỤNG MÔI TRƯỜNG LẬP TRÌNH C 73 TH BÀI 2: LẬP TRÌNH VỚI LỆNH KHỐI VÀ LỆNH LỰA CHỌN 75 TH BÀI 3: LẬP TRÌNH VỚI LỆNH LẶP 77 TH BÀI 4: LẬP TRÌNH VỚI LỆNH LẶP (tiếp) 79 TH BÀI 5: XÂY DỰNG HÀM VÀ XỬ LÝ MẢNG 2 CHIỀU 83 TH BÀI 6: MẢNG VÀ XỬ LÝ XÂU KÝ TỰ 85 Trang 6
- CHƯƠNG 1: TỔNG QUAN VỀ NGÔN NGỮ LẬP TRÌNH C Kiến thức: Trình bày được lịch sử phát triển của ngôn ngữ C, các phần tử cơ bản trong ngôn ngữ C và cấu trúc cơ bản của một chương trình C. Kĩ năng Thực hiện được cài đặt trình biên dịch và viết được chương trình C cơ bản. Thái độ Vận dụng tích cực, linh hoạt các kiến thức đã học vào các bài học tiếp theo, áp dụng viết các chương trình cơ bản. 1.1. Giới thiệu sơ lược về ngôn ngữ lập trình C 1.1.1. Lịch sử phát triển ngôn ngữ C Ngôn ngữ C ra đời tại phòng thí nghiệm BELL của tập đoàn AT&T (Hoa Kỳ) do Brian W. Kernighan và Dennis Ritchie phát triển vào đầu những năm 1970 và hoàn thành vào năm 1972. C được phát triển dựa trên nền các ngôn ngữ BCPL (Basic Combined Programming Language) và ngôn ngữ B. Cũng vì được phát triển dựa trên nền ngôn ngữ B nên ngôn ngữ mới được Brian W. Kernighan và Dennis Ritchie đặt tên là ngôn ngữ C như là sự tiếp nối ngôn ngữ B. C có các đặc điểm là một ngôn ngữ lập trình hệ thống mạnh, khả chuyển, có tính linh hoạt cao và có thế mạnh trong xử lí các dạng dữ liệu số, văn bản, cơ sở dữ liệu. Vì vậy C thường được dùng để viết các chương trình hệ thống như hệ điều hành (ví dụ hệ điều hành Unix có 90% mã nguồn được viết bằng C, 10% còn lại viết bằng hợp ngữ) và các chương trình ứng dụng chuyên nghiệp có can thiệp tới dữ liệu ở mức thấp như xử lí văn bản, cơ sở dữ liệu, xử lí ảnh W. Kernighan và Dennis Ritchie công bố ngôn ngữ C trong lần xuất bản đầu của cuốn sách "The C programming language" (1978). Sau đó người ta đã bổ sung thêm những yếu tố và khả năng mới vào trong ngôn ngữ C (ví dụ như đưa thêm kiểu liệt kê enum, cho phép kiểu dữ liệu trả về bởi hàm là kiểu void, struct hoặc union và đặc biệt là sự bổ sung các thư viện cho ngôn ngữ. Lúc đó đồng thời tồn tại nhiều phiên bản khác nhau của ngôn ngữ C nhưng không tương thích với nhau. Điều này gây khó khăn cho việc trao đổi mã nguồn chương trình C viết trên các phiên bản ngôn ngữ C khác nhau (bạn sẽ rất khó đọc và hiểu chương trình của người khác, và khi bạn muốn sửa nó thành chương trình của mình dịch trên bộ dịch của mình thì sẽ tốn rất nhiều công sức) và dẫn đến nhu cầu chuẩn hóa ngôn ngữ C. Hiện nay cũng có nhiều phiên bản của ngôn ngữ C khác nhau và mỗi phiên bản này gắn liền với một bộ chương trình dịch cụ thể của ngôn ngữ C. Các bộ chương trình dịch phổ biến của ngôn ngữ C có thể kể tên như: Turbo C++ và Borland C++ của Borland Inc. MSC và VC của Microsoft Corp. GCC của GNU project. Free C Trang 7
- 1.1.2. Các bước cơ bản khi viết chương trình trên C Để giải bài toán bằng chương trình thực hiện theo các bước sau: 1. Xác định đối tượng của chương trình 2. Xác định phương pháp và thuật giải 3. Viết chương trình (lập trình) 4. Chạy chương trình và kiểm tra kết quả. Như vậy ta thấy chu trình phát triển một chương trình như sau: 1. Soạn thảo chương trình nguồn Chúng ta có thể sử dụng một trình soạn thảo văn bản chuẩn (ASCII) nào đó để soạn thảo chương trình, sau đó ghi vào file chương trình nguồn (ngầm định với phần mở rộng là .C). Do C cũng như hầu hết các ngôn ngữ lập trình phổ biến đều sử dụng bảng chữ cái ASCII nên bạn có thể sử dụng bất kỳ một hệ soạn thảo văn bản chuẩn để viết chương trình, tuy nhiên hầu hết các trình biên dịch của C trên môi trường MS-DOS hoặc WINDOWS đều có tích hợp trình soạn thảo và bạn nên sử dụng trình soạn thảo tích hợp này sẽ thuận lợi hơn. 2. Biên dịch chương trình nguồn Hiện nay có rất nhiều chương trình dịch cho C như: Turbo C, BC, Microsoft C, mục đích của bước này là chuyển chương trình nguồn thành chương trình mã đối tượng (object). Sau bước này (nếu thành công) chúng ta thu được file chương trình đối tượng (có phần mở rộng là .OBJ) 3. Liên kết chương trình Sau bước biên dịch hoàn thành ta có chương trình đối tượng, đây chưa phải là chương trình có thể chạy được trên máy tính, bước này chúng ta phải sử dụng một trình liên kết để liên kết các hàm thư viện với chương trình đối tượng để tạo ra chương trình đích. Bạn có thể sử dụng trình liên kết độc lập nào đó, nhưng với các trình biên dịch của C trên môi trường DOS hay WINDOWS đều có sẵn trình liên kết. 4. Chạy và kiểm tra kết quả chương trình Khi đã có chương trình đích, chúng ta cần phải kiểm tra tính đúng đắn của nó.Bạn chạy chương trình với các bộ dữ liệu mẫu và kiểm tra kết quả có như dự kiến hay không, nếu có sai sót thì phải xác định nguyên nhân gây lỗi và quay lại bước 1 để hiệu chỉnh. Và chúng ta lặp lại quá trình này cho tới khi được chương trình giải đúng bài toán mong đợi. Trang 8
- Các bước phát triển chương trình Hiện nay có rất nhiều chương trình dịch cho C và hầu hết (trên nền DOS hoặc Windows) trong đó được tích hợp cả trình soạn thảo, biên dịch, liên kết - gọi là môi trường tích hợp. Trong giáo trình này chúng ta sử dụng BC (Borland C) hoặc turbo C làm môi trường lập trình. 1.2. Các phần tử cơ bản của C 1.2.1. Tập ký tự cơ bản Tập kí tự sử dụng trong ngôn ngữ lập trình C gồm có: 26 chữ cái hoa: ABC X Y Z 26 chữ cái thường: a b c x y z. 10 chữ số: 0 1 2 3456789. Các kí hiệu toán +-*/=<> Các dấu ngăn cách: . ; , : space tab Các dấu ngoặc: ( ) [ ] { } Các kí hiệu đặc _ ? $ & # ^ \ ! ‘ “ ~ .v.v. 1.2.2. Từ khóa (Keyword) Từ khóa (Keyword) là những từ có sẵn của ngôn ngữ và được sử dụng dành riêng cho những mục đích xác định. Một số từ khóa hay dùng trong Turbo C Trang 9
- break case char const continue default do double else enum float for goto if int interrupt long return short signed sizeof static struct switch typedef union unsigned void while Chú ý: Tất cả các từ khóa trong C đều viết bằng chữ thường. Các từ khóa trong C được sử dụng để - Đặt tên cho các kiểu dữ liệu: int, float, double, char, struct, union - Mô tả các lệnh, các cấu trúc điều khiển: for, do, while, switch, case, if, else, break, continue 1.2.3. Định danh (Tên) - Identifier Định danh (Identifier – hoặc còn gọi là Tên) là một dãy các kí tự dùng để gọi tên các đối tượng trong chương trình. Các đối tượng trong chương trình gồm có biến, hằng, hàm, kiểu dữ liệu ta sẽ làm quen ở những mục tiếp theo. Khi đặt tên cho định danh trong C, người lập trình cần tuân thủ các quy tắc sau : 1. Các kí tự được sử dụng trong các định danh của ngôn ngữ C chỉ được gồm có: chữ cái, chữ số và dấu gạch dưới “_” (underscore). 2. Bắt đầu của định danh phải là chữ cái hoặc dấu gạch dưới, không được bắt đầu định danh bằng chữ số. 3. Định danh do người lập trình đặt không được trùng với từ khóa. 4. Turbo C++ không giới hạn độ dài của định danh, nhưng chỉ 32 kí tự đầu của định danh được chương trình biên dịch sử dụng (khi định danh có độ dài lớn hơn 32 kí tự thì Turbo C++ sẽ tự động cắt bỏ, không xem xét các kí tự cuối bắt đầu từ kí tự thứ 33). Một số ví dụ về định danh: i, x, y, a, b, _function, _MY_CONSTANT, PI, gia_tri_1 Ví dụ về định danh không hợp lệ 1_a, 3d, 55x bắt đầu bằng chữ số so luong, ti le có kí tự không hợp lệ (dấu cách – space) trong tên int, char trùng với từ khóa của ngôn ngữ C 1.2.4. Kiểu dữ liệu Dữ liệu là đối tượng được lưu trữ và xử lý trong máy tính. Dữ liệu trong máy tính lại không phải tất cả đều giống nhau. Có dữ liệu là chữ viết, có dữ liệu là con số, lại có dữ liệu khác là hình ảnh, âm thanh Ta nói rằng các dữ liệu đó thuộc các kiểu dữ liệu khác nhau. Kiểu dữ liệu trong C có thể phân loại thành 3 nhóm: - Kiểu đơn giản (cơ bản/cơ sở) gồm kiểu số nguyên,số thực,ký tự - Kiểu dữ liệu có cấu trúc gồm kiểu mảng, xâu (chuỗi), struct (bản ghi),tệp. - Kiểu con trỏ Một cách hình thức, kiểu dữ liệu có thể được định nghĩa gồm 2 điểm như sau: Một kiểu dữ liệu là một tập hợp các giá trị mà một dữ liệu thuộc kiểu dữ liệu đó có thể nhận được (dải giá trị/miền giá trị). Trang 10
- Trên một kiểu dữ liệu ta xác định một số phép toán đối với các dữ liệu thuộc kiểu dữ liệu đó (tập thao tác/ phép toán trên tập giá trị). Bảng sau liệt kê các kiểu dữ liệu đơn giản trong C Ví dụ: Trong ngôn ngữ C có kiểu dữ liệu int. Một dữ liệu thuộc kiểu dữ liệu int thì nó sẽ là một số nguyên (integer) và nó có thể nhận giá trị từ - 32,768 (- 215) đến 32,767 (215 - 1). Trên kiểu dữ liệu int ngôn ngữ C định nghĩa các phép toán số học đối với số nguyên như Tên phép toán Kí hiệu Đảo dấu - Cộng + Trừ - Nhân * Chia lấy phần / nguyên Chia lấy phần dư % So sánh bằng = = So sánh lớn hơn > So sánh nhỏ hơn < Trong máy tính, việc phân biệt kiểu dữ liệu là cần thiết vì qua kiểu dữ liệu máy tính biết được đối tượng mà nó đang xử lí thuộc dạng nào, có cấu trúc ra sao, có thể thực hiện các phép xử lí nào đối với đối tượng đó, hay là cần phải lưu trữ đối tượng đó như thế nào 1.2.5. Hằng Hằng (constant) là đại lượng có giá trị xác định và không thay đổi trong chương trình. Để giúp chương trình dịch nhận biết hằng ta cần nắm được cách biểu diễn hằng trong một chương trình C. Biểu diễn hằng số nguyên Trong ngôn ngữ C, một hằng số nguyên có thể được biểu diễn dưới những dạng sau Trang 11
- - Dạng thập phân: đó chính là cách viết giá trị số đó dưới hệ đếm cơ số 10 thông thường. - Dạng thập lục phân: ta viết giá trị số đó dưới dạng hệ đếm cơ số 16 và thêm tiền tố 0x ở đầu. - Dạng bát phân: ta viết giá trị số đó dưới dạng hệ đếm cơ số 8 và thêm tiền tố 0 ở đầu. Ví dụ Giá trị thập phân Giá trị hệ bát phân Giá trị hệ thập lục phân 2007 03727 0x7D7 396 0614 0x18C Biểu diễn hằng số thực Có 2 cách biểu diễn hằng số thực: - Dưới dạng số thực dấu phẩy tĩnh. - Dưới dạng số thực dấu phẩy động. Ví dụ: Số thực dấu phẩy tĩnh Số thực dấu phẩy động 3.14159 31.4159 E-1 123.456 12.3456 E+1 hoặc 1.23456 E+2 Biểu diễn hằng kí tự Có 2 cách biểu diễn hằng kí tự: - Bằng kí hiệu của kí tự đó đặt giữa 2 dấu nháy đơn. - Bằng số thứ tự của kí tự đó trong bảng mã ASCII (và lưu ý số thứ tự của một kí tự trong bảng mã ASCII là một số nguyên nên có một số cách biểu diễn). Ví dụ Kí tự cần biểu diễn Cách 1 Cách 2 Chữ cái A ‘A’ 65 hoặc 0101 hoặc 0x41 Dấu nháy đơn ‘ ‘\’’ 39 hoặc 047 hoặc 0x27 Dấu nháy kép “ ‘\”’ 34 hoặc 042 hoặc 0x22 Dấu gạch chéo ngược \ ‘\\’ 92 hoặc 0134 hoặc 0x5c Kí tự xuống dòng ‘\n’ Kí tự NUL ‘\0’ 0 hoặc 00 hoặc 0x0 Kí tự Tab ‘\t’ 9 hoặc 09 hoặc 0x9 Biểu diễn hằng xâu kí tự Hằng xâu kí tự được biểu diễn bởi dãy các kí tự thành phần có trong xâu đó và được đặt trong cặp dấu nháy kép. Ví dụ: “ngon ngu lap trinh C”, “tin hoc dai cuong” Để sử dụng, hằng phải được đặt tên. Tên hằng đặt theo quy tắc định danh 1.2.6. Biến Biến (variable) là đặc trưng cho một đại lượng được xử lý trong bài toán, đại lượng này có giá trị có thể thay đổi trong chương trình. Tại một thời điểm xác định, giá trị của biến là một hằng. Trang 12
- Trong chương trình, hằng và biến được sử dụng để lưu trữ dữ liệu, và dữ liệu lưu trữ trong biến, hằng phải thuộc một kiểu dữ liệu nào đó. Biến và hằng đều phải được đặt tên để khi cần thì có thể gọi đến. Tên biến và hằng được đặt theo quy tắc đặt tên cho định danh. 1.2.7. Hàm Trong lập trình chúng ta rất hay phải tính toán giá trị của một số đại lượng thường gặp như sin(x), cos(x), căn bậc hai, lũy thừa, logarithm Ngôn ngữ C cung cấp cho người lập trình một công cụ dùng để tính toán giá trị các đại lượng đó mỗi khi cần trong chương trình, đó là các hàm. Một số hàm toán học hay được sử dụng trong C Kí hiệu Hàm Ý nghĩa Ví dụ toán học sqrt(x) Căn bậc 2 của x √ x sqrt(16.0) bằng 4.0 pow(x,y) x mũ y xy pow(2,3) bằng 8 exp(x) e mũ x ex exp(1.0) bằng 2.718282 log(x) logarithm tự nhiên (cơ số e) lnx log(2.718282) bằng 1.0 của x log10(x) logarithm cơ số 10 của x logx log10(100) bằng 2 sin(x) sin của x sinx sin(0.0) bằng 0.0 cos(x) cosin của x cosx cos(0.0) bằng 1.0 tan(x) tang của x tgx tan(0.0) bằng 0.0 ceil(x) phần nguyên già của x, tức là ⌈ x⌉ ceil(2.5) bằng 3 số nguyên nhỏ nhất không ceil(-2.5) bằng –2 nhỏ hơn x floor(x) phần nguyên non của x, tức ⌊ x⌋ floor(2.5) bằng 2 là số nguyên lớn nhất không floor(-2.5) bằng –3 lớn hơn x 1.2.8. Biểu thức Biểu thức là sự ghép nối các toán tử (operator) và các toán hạng (operand) theo một quy tắc xác định. Các toán hạng trong biểu thức có thể là biến, hằng, hàm hoặc một biểu thức khác. Bản thân một biến, hằng, hàm đứng độc lập cũng được coi là một biểu thức. Các toán tử trong biểu thức rất đa dạng như cộng, trừ, nhân, chia, so sánh Biểu thức thường là sự thể hiện công thức tính toán giá trị một đại lượng nào đó. Ví dụ về biểu thức: chieu_dai * chieu_rong * chieu_cao Trong biểu thức trên chieu_dai, chieu_rong, chieu_cao là các biến hoặc hằng, * là kí hiệu của toán tử nhân. Nếu chieu_dai, chieu_rong, chieu_cao là các biến (hoặc hằng) lưu trữ giá trị chiều dài, chiều rộng và chiều cao của một khối hộp chữ nhật thì biểu thức trên sẽ tính giá trị thể tích của khối hộp chữ nhật đó. 1.2.9. Câu lệnh Câu lệnh (statement) diễn tả một hoặc một nhóm các thao tác trong giải thuật. Chương trình được tạo thành từ dãy các câu lệnh. Trang 13
- Cuối mỗi câu lệnh đều có dấu chấm phẩy ‘;’ để đánh dấu kết thúc câu lệnh cũng như để phân tách các câu lệnh với nhau. Câu lệnh được chia thành 2 nhóm chính: Nhóm các câu lệnh đơn: là những câu lệnh không chứa câu lệnh khác. Ví dụ: phép gán, phép cộng, phép trừ Nhóm các câu lệnh phức: là những câu lệnh chứa câu lệnh khác trong nó. Ví dụ: lệnh khối, các cấu trúc lệnh rẽ nhánh, cấu trúc lệnh lặp Lệnh khối là một số các lệnh đơn được nhóm lại với nhau và đặt trong cặp dấu ngoặc nhọn { } để phân tách với các lệnh khác trong chương trình. 1.2.10. Chú thích Để giúp việc đọc và hiểu chương trình viết ra được dễ dàng hơn, chúng ta cần đưa vào các lời chú thích (comment). Lời chú thích là lời mô tả, giải thích vắn tắt cho một câu lệnh, một đoạn chương trình hoặc cả chương trình, nhờ đó người đọc có thể hiểu được ý đồ của người lập trình và công việc mà chương trình đang thực hiện. Khi gặp kí hiệu lời chú thích trong chương trình, trình biên dịch sẽ tự động bỏ qua không dịch phần nội dung nằm trong phạm vi của vùng chú thích đó. Trong C, có 2 cách để viết lời chú thích Dùng 2 dấu sổ chéo liên tiếp // để kí hiệu toàn bộ vùng bắt đầu từ 2 dấu sổ chéo liên tiếp đó đến cuối dòng là vùng chú thích. Ví dụ: // khai bao 2 bien nguyen int a, b; a = 5; b = 3; // khoi tao gia tri cho cac bien nay Cách này thường dùng nếu đoạn chú thích ngắn, có thể viết đủ trên một dòng. Dùng 2 cặp kí hiệu /* và */ để kí hiệu rằng toàn bộ vùng bắt đầu từ cặp kí hiệu /* kéo dài đến cặp kí hiệu */ là vùng chú thích. Ví dụ: /* doan chuong trinh sau khai bao 2 bien nguyen va khoi tao gia tri cho 2 bien nguyen nay */ int a, b; a = 5; b = 3; Cách này thường dùng khi đoạn chú thích dài, phải viết trên nhiều dòng. 1.3. Cấu trúc cơ bản của chương trình C Về cơ bản, mọi chương trình viết bằng ngôn ngữ C sẽ có cấu trúc gồm các phần có thứ tự như sau: Phần 1: Khai báo tệp tiêu đề #include Phần 2: Định nghĩa hằng #define hay const Phần 3: Định nghĩa kiểu dữ liệu typedef Phần 4: Khai báo các nguyên mẫu hàm Trang 14
- Phần 5: Khai báo các biến toàn cục Phần 6: Định nghĩa các hàm Phần 7: Định nghĩa hàm main() main() { } Phần 8: Định nghĩa các hàm đã khai báo nguyên mẫu Phần 1: Phần khai báo các tệp tiêu đề. Phần này có chức năng thông báo cho chương trình dịch biết là chương trình có sử dụng những thư viện nào (mỗi tệp tiêu đề tương ứng với một thư viện). Phần 2: Định nghĩa các hằng mới dùng cho cả chương trình. Phần 3: Định nghĩa các kiểu dữ liệu mới dùng cho cả chương trình Phần 4: Phần khai báo các hàm nguyên mẫu. Phần này giúp cho chương trình dịch biết được những thông tin cơ bản (gồm tên hàm, dach sách các tham số và kiểu dữ liệu trả về) của các hàm sử dụng trong chương trình. Phần 5: Phần khai báo các biến toàn cục. Phần 6: Định nghĩa các hàm. Phần 7: Phần định nghĩa hàm main( ). Hàm main( ) là một hàm đặc biệt trong C. Khi thực hiện, chương trình sẽ gọi hàm main( ), hay nói cách khác chương trình sẽ bắt đầu bằng việc thực hiện các lệnh trong hàm main( ). Trong hàm main( ) ta mới gọi tới các hàm khác. Phần 8: Phần định nghĩa các hàm đã khai báo nguyên mẫu. Ở phần 3 ta đã khai báo nguyên mẫu (prototype) của các hàm, trong đó chỉ giới thiệu các thông tin cơ bản về hàm như tên hàm, danh sách các tham số và kiểu dữ liệu trả về. Nguyên mẫu hàm không cho ta biết cách thức cài đặt và hoạt động của các hàm. Ta sẽ làm việc đó ở phần định nghĩa các hàm. Trong các phần trên, thì phần 7 định nghĩa hàm main( ) bắt buộc phải có trong mọi chương trình C. Các phần khác có thể có hoặc không. 1.4. Cài đặt và sử dụng turbo C 1.4.1. Cài đặt Để sử dụng Turbo C++ 3.0 ta cần phải cài đặt nó lên máy. Quá trình cài đặt thực hiện theo các bước sau: Bước 1: Bạn cần chuẩn bị đĩa chứa bộ cài của Turbo C++ 3.0, kích thước của bộ cài khoảng 4 MB. Hãy copy bộ cài này vào máy của bạn, giả sử vào thư mục C:\ TC_Setup. Bước 2: Tìm đến thư mục chứa bộ cài Turbo C++ 3.0 (như giả sử ở trên là C:\ TC_Setup) và kích hoạt file INSTALL.EXE để chạy chương trình cài đặt Turbo C++ 3.0. Chương trình cài đặt Turbo C++ 3.0 ban đầu sẽ yêu cầu bạn chỉ ra ổ đĩa trên đó chứa bộ cài Turbo C++ 3.0 Enter the SOURCE drive to use: Trang 15
- Hãy nhập vào tên ổ đĩa, chẳng hạn C (ta để bộ cài Turbo C++ 3.0 ở thư mục C:\ TC_Setup). Bước 3: Sau đó chương trình yêu cầu bạn nhập vào đường dẫn tới thư mục chứa các file của Turbo C++ 3.0 Enter the SOURCE Path: Thông thường chương trình sẽ tự tìm cho bạn, và bạn chỉ cần ấn Enter để chuyển sang bước tiếp theo. Bước 4: Ở bước 4, bạn cần xác định thư mục cài đặt. Thư mục này sẽ chứa các file của Turbo C++ 3.0 để bạn sử dụng sau này. Directories [C:\TC] Option [IDE CMD LIB CLASS BGI HELP EXMPL] Start Installation Thư mục cài đặt mặc định sẽ là \TC nằm trên thư mục gốc của ổ đĩa chứa bộ cài. Nếu bạn muốn thay đổi thư mục cài đặt thì hãy dùng các phím và để di chuyển hộp sáng đến phần Directories, gõ Enter và nhập vào đường dẫn mới, sau đó ấn phím Esc để trở về. Dùng các phím và để di chuyển hộp sáng đến phần Start Installation và ấn Enter. 1.4.2. Sử dụng môi trường Turbo Sau khi cài đặt xong, bạn có thể tìm đến thư mục BIN trong thư mục cài đặt và chạy file TC.EXE để khởi động Turbo C++ 3.0. Sau khi khởi động Turbo C++ 3.0 sẽ xuất hiện màn hình làm việc của Turbo C++ 3.0. Bạn dùng chuột di chuyển đến menu File (hoặc ấn Alt-F), sau đó chọn mục New để mở cửa sổ soạn thảo mới. Giờ hãy gõ vào toàn bộ chương trình viết bằng ngôn ngữ C của bạn lên cửa sổ soạn thảo này. Ấn phím F2 để lưu trữ tệp chương trình nguồn trên máy, một cửa sổ cất giữa tệp sẽ hiện ra yêu cầu bạn nhập vào tên mới cho tệp chương trình nguồn (tên mặc định sẽ là NONAME.CPP). Hãy đặt một tên cho tệp rồi chọn OK để lưu tệp chương trình nguồn lại. Cuối cùng là ấn phím F9 để biên dịch chương trình viết ra. Nếu chương trình của bạn có lỗi thì Turbo C++ 3.0 sẽ báo lỗi và bạn phải sửa lại đến khi không còn lỗi. Nếu chương trình bạn không có lỗi thì Turbo C++ 3.0 sẽ thông báo biên dịch thành công và bạn có thể ấn Ctrl-F9 để chạy chương trình đã biên dịch. Trang 16
- CHƯƠNG 2: KHỞI ĐẦU VỚI LẬP TRÌNH C MỤC TIÊU CỦA BÀI Kiến thức: - Trình bày được các phép toán trong C; - Trình bày được các lệnh vào ra dữ liệu trong C. Kĩ năng Tạo được một chương trình đơn giản nhập và xuất ra màn hình. Thái độ Vận dụng tích cực, linh hoạt các kiến thức đã học vào các bài học tiếp theo, áp dụng viết các chương trình cơ bản. 2.1. Một số khai báo cơ bản 2.1.1. Khai báo tệp tiêu đề Trong chương trình C (trong hàm main cũng như các hàm khác do người lập trình viết) có thể sử dụng các hàm, hằng, kiểu dữ liệu, (gọi chung là các thành phần) đã được định nghĩa trong thư viện của C. Để sử dụng các thành phần này chúng ta phải chỉ dẫn cho chương trình dịch biết các thông tin về các thành cần sử dụng, các thông tin đó được khai báo trong tệp gọi là tệp tiêu đề (có phần mở rộng là H – viết tắt của header). Cú pháp của một dòng bao hàm tệp: #include hoặc #include “tên_tệp” trong đó tên_tệp là tên có thể có cả đường dẫn của tệp tiêu đề (.H) mà chúng ta cần sử dụng, mỗi lệnh bao hàm tệp trên một dòng. Ví dụ: #include #include #include “phanso.h” 2.1.2. Khai báo hằng Có 2 cách để khai báo hằng trong C là dùng chỉ thị #define hoặc khai báo với từ khóa const. Dùng chỉ thị #define Cú pháp khai báo: # define tên_hằng giá_trị Lưu ý không có dấu chấm phẩy ở cuối dòng chỉ thị. #define MAX_SINH_VIEN 50 // hằng kiểu số nguyên Dùng từ khóa const để khai báo với cú pháp: const kiểu_dữ_liệu tên_hằng = giá_trị; Khai báo này giống với khai báo biến có khởi tạo giá trị đầu, tuy nhiên cần lưu ý: Do có từ khóa const ở đầu cho nên giá trị của đối tượng tên_hằng sẽ không được phép thay đổi trong chương trình. Những lệnh nhằm làm thay đổi giá trị của tên_hằng trong chương trình sẽ dẫn tới lỗi biên dịch. Trang 17
- Trong khai báo biến thông thường, người lập trình có thể khởi tạo giá trị cho biến ngay từ khi khai báo hoặc không khởi tạo cũng được. Nhưng trong khai báo hằng, giá trị của tất cả các hằng cần được xác định ngay trong lệnh khai báo. Các khai báo hằng ở ví dụ trước giờ có thể viết lại theo cách khác như sau: const int MAX_SINH_VIEN = 50; 2.1.3. Khai báo biến Một biến trước khi sử dụng phải được khai báo. Cú pháp khai báo: kiểu_dữ_liệutên_biến; Ví dụ: float x; // biến kiểu thực float y; // biến kiểu thực double z; // biến kiểu thực int i; // biến kiểu nguyên int j; // biến kiểu nguyên Nếu các biến thuộc cùng kiểu dữ liệu thì C cho phép khai báo chúng trên cùng một dòng: kiểu_dữ_liệu danh_sách_tên_biến; Ví dụ: float x, y; double z; int i, j; Sau khi khai báo, biến có thể nhận giá trị thuộc kiểu dữ liệu đã khai báo. Chúng ta có thể khởi tạo giá trị đầu cho biến nếu muốn với cú pháp: kiểu_dữ_liệu tên_biến = giá_trị_đầu; Ví dụ: int a = 3; // sau lenh nay bien a se co gia tri bang 3 2.1.4. Khai báo hàm main() Một chương trình C bao hàm nhiều hàm. Trong đó hàm main() là hàm chính,luôn xuất hiện và chỉ xuất hiện 1 lần. Khái niệm về hàm của C như sau: Hàm main() cũng không có tham số, vì vậy ta có thể gặp cách viết void main() hoặc void main(void). Phần thân của hàm main() gồm: - Các khai báo hằng,biến, - Các chỉ thị thực hiện các thao tác theo nội dung hàm. Ví dụ: Void main (void) { Int in; Printf(“\n Go vao 1 so nguyen la nam sinh cua ban”); Scanf(“%d”,&n)’ Printf(“\n ban sinh nam %d”,n); Getch(); } Trang 18
- 2.2. Các phép toán và biểu thức trong C 2.2.1. Các phép toán (operators) Các phép toán trong C được chia thành các nhóm phép toán cơ bản sau: nhóm các phép toán số học,nhóm các phép toán thao tác trên bit,nhóm các phép toán quan hệ,nhóm các phép toán logic. 2.2.1.1. Các phép toán cơ bản Các phép toán số học (Arithmetic operators ) gồm có: Toán Ý nghĩa Kiểu dữ liệu của Ví dụ tử toán hạng - Phép đổi dấu Số thực hoặc số int a, b; nguyên -12; -a; -25.6; + Phép toán cộng Số thực hoặc số float x, y; nguyên 5 + 8; a + x; 3.6 + 2.9; - Phép toán trừ Số thực hoặc số 3 – 1.6; a – 5; nguyên * Phép toán nhân Số thực hoặc số a * b; b * y; nguyên 2.6 * 1.7; / Phép toán chia Số thực hoặc số 10.0/3.0; (bằng 3.33 ) nguyên 10/3.0; (bằng 3.33 ) 10.0/3; (bằng 3.33 ) / Phép chia lấy phần Giữa 2 số nguyên 10/3; (bằng 3) nguyên % Phép chia lấy phần Giữa 2 số nguyên 10%3; (bằng 1) dư Các phép toán quan hệ (Comparison operators) gồm có: Toán tử Ý nghĩa Ví dụ So sánh lớn hơn giữa 2 số nguyên hoặc 2 > 3 (có giá trị 0) thực. 6 > 4 (có giá trị 1) a > b So sánh lớn hơn hoặc bằng giữa 2 số 6 4 (có giá trị 1) nguyên hoặc thực. x a So sánh nhỏ hơn giữa 2 số nguyên hoặc 5 3 (có giá trị 0), thực. So sánh nhỏ hơn hoặc bằng giữa 2 số 5 5 (có giá trị 1) nguyên hoặc thực. 2 9 (có giá trị 1) So sánh bằng nhau giữa 2 số nguyên hoặc 3 4 (có giá trị 0) thực. a b So sánh không bằng (so sánh khác) giữa 2 5 6 (có giá trị 1) số nguyên hoặc thực. 6 6 (có giá trị 0) Các phép toán logic (Logical operators) gồm có Trang 19
- Toán Ý nghĩa Kiểu dữ liệu của Ví dụ tử toán hạng && Phép VÀ LOGIC. Hai biểu thức logic 3 b && c 5) (có giá trị 1) ĐỊNH LOGIC có giá trị bằng 1 nếu toán hạng bằng 0 và có giá trị bằng 0 nếu toán hạng bằng 1 Phép toán gán Phép toán gán có dạng tên_biến = biểu_thức; Phép toán gán có chức năng lấy giá trị của biểu_thức gán cho tên_biến. Dấu = là kí hiệu cho toán tử gán. Ví dụ: int a, b, c; a = 3; b = a + 5; c = a * b; Sau đoạn lệnh trên, biến a có giá trị là 3, b có giá trị là 8 và c có giá trị là 24. Trong phép toán gán nếu ta bỏ dấu ; ở cuối đi thì ta sẽ thu được biểu thức gán. Biểu thức gán là biểu thức có dạng tên_biến = biểu_thức; 2.2.1.2. Các phép toán đặc trưng của C Phép toán gán thu gọn Xét lệnh gán sau x = x + y; Lệnh gán này sẽ tăng giá trị của biến x thêm một lượng có giá trị bằng giá trị của y. Trong C ta có thể viết lại lệnh này một cách gọn hơn mà thu được kết quả tương đương x += y; Trang 20
- Dạng lệnh gán thu gọn này còn áp dụng được với các phép toán khác nữa. Lệnh gán thông thường Lệnh gán thu gọn x = x + y x + = y x = x - y x - = y x = x * y x * = y x = x / y x / = y x = x % y x % = y x = x >> y x >> = y x = x = + 1; = - 1; Ta cũng có thể tăng (hoặc giảm) giá trị của một biến bằng cách sử dụng hai phép toán đặc biệt của C là phép toán ++ và phép toán . Phép toán ++ sẽ tăng giá trị của biến thêm 1 đơn vị, phép toán sẽ giảm giá trị của biến đi 1 đơn vị. Ví dụ int a = 5; float x = 10; a ++; // lệnh này tương đương với a = a+1 ; x ; // tương đương với x = x – 1; Phép toán tăng, giảm một đơn vị ở ví dụ trên là dạng hậu tố (vì phép toán đứng sau toán hạng). Ngoài ra còn có dạng tiền tố của phép toán tăng, giảm một đơn vị. Trong dạng tiền tố, ta thay đổi giá trị của biến trước khi sử dụng biến đó để tính toán giá trị của biểu thức. Trong dạng hậu tố, ta tính toán giá trị của biểu thức bằng giá trị ban đầu của biến, sau đó mới thay đổi giá trị của biến. Ví dụ int a, b, c; a = 3; // a bằng 3 b = a++; // dạng hậu tố. b bằng 3; a bằng 4 c = ++b; // dạng tiền tố. b bằng 4, c bằng 4; Sau khi thực hiện đoạn chương trình trên, ta có a, b và c đều có giá trị bằng 4; Phép toán lấy địa chỉ biến (&) Địa chỉ của một biến được định nghĩa là địa chỉ của ô nhớ đầu tiên trong vùng nhớ dành cho biến đó. Hình dưới đây minh họa một biến tên là a, kiểu dữ liệu int được lưu trữ trong bộ nhớ tại 2 ô nhớ có địa chỉ lần lượt là 158 và 159. Giá trị của biến a là 2006 = 0x07D6. Khi đó địa chỉ của biến a sẽ là 158 hay 0000:9E (vì địa chỉ được mã hóa bởi 2 byte). Trang 21
- 1 . . . 157 a D6 158 07 159 160 . . . Trong C để xác đinh địa chỉ của một biến ta sử dụng toán tử một ngôi & đặt trước tên biến, cú pháp là & ; Ví dụ &a; // có giá trị là 158 hay 9E Phép toán chuyển đổi kiểu bắt buộc Chuyển đổi kiểu là chuyển kiểu dữ liệu của một biến từ kiểu dữ liệu này sang kiểu dữ liệu khác. Cú pháp của lệnh chuyển kiểu dữ liệu là như sau: ( ) ; Có những sự chuyển đổi được thực hiện hết sức tự nhiên, không có khó khăn gì, thậm chí đôi khi chương trình dịch sẽ tự động chuyển đổi kiểu hộ cho ta, ví dụ chuyển một dữ liệu kiểu số nguyên int sang một số nguyên kiểu long int, hay từ một số long int sang một số thực float Đó là vì một số nguyên kiểu int thực ra cũng là một số nguyên kiểu long int, một số nguyên kiểu long int cũng chính là một số thực kiểu float, một số thực kiểu float cũng là một số thực kiểu double. Tuy nhiên điều ngược lại thì chưa chắc, ví dụ số nguyên long int 50,000 không phải là một số nguyên kiểu int vì phạm vi biểu diễn của kiểu int là từ (-32,768 đến 32,767). Khi đó nếu phải chuyển kiểu dữ liệu thì ta phải cẩn thận nếu không sẽ bị mất dữ liệu. C hỗ trợ chuyển kiểu tự động trong những trường hợp sau char int long int float double long double 2.2.2. Biểu thức 2.2.2.1. Các loại biểu thức Biểu thức số học Biểu thức số học là biểu thức mà giá trị của nó là cái đại lượng số học (số nguyên, số thực). Trong biểu thức số học, các toán tử là các phép toán số học (cộng, trừ, nhân, chia ), các toán hạng là các đại lượng số học. Ví dụ (giả sử a, b, c là các số thực) 3 * 3.7, 8 + 6/3, a + b – c Biểu thức logic Biểu thức logic là biểu thức mà giá trị của nó là các giá trị logic, tức là một trong hai giá trị: Đúng (TRUE) hoặc Sai (FALSE). Ngôn ngữ C coi các giá trị nguyên khác 0 (ví dụ 1, -2, -5) là giá trị logic Đúng (TRUE), giá trị 0 là giá trị logic Sai (FALSE). Các phép toán logic gồm có - AND (VÀ logic, trong ngôn ngữ C được kí hiệu là &&) Trang 22
- - OR (HOẶC logic, trong ngôn ngữ C được kí hiệu là ||) - NOT (PHỦ ĐỊNH, trong ngôn ngữ C kí hiệu là !) Biểu thức quan hệ Biểu thức quan hệ là những biểu thức trong đó có sử dụng các toán tử quan hệ so sánh như lớn hơn, nhỏ hơn, bằng nhau, khác nhau Biểu thức quan hệ cũng chỉ có thể nhận giá trị là một trong 2 giá trị Đúng (TRUE) hoặc Sai (FALSE), vì vậy biểu thức quan hệ là một trường hợp riêng của biểu thức logic. Ví dụ về biểu thức quan hệ 5 > 7 // có giá trị logic là sai, FALSE 9!=10 // có giá trị logic là đúng, TRUE 2 2 // có giá trị logic là đúng, TRUE a > b // giả sử a, b là 2 biến kiểu int a+1 > a // có giá trị đúng, TRUE Ví dụ về biểu thức logic (5 > 7)&&(9!=10) // có giá trị logic là sai, FALSE 0||1 // có giá trị logic là đúng, TRUE (5 > 7)||(9!=10) // có giá trị logic là đúng, TRUE 0 // có giá trị logic là sai, FALSE !0 // phủ định của 0, có giá trị logic là đúng, TRUE 3 // có giá trị logic là đúng, TRUE !3 // phủ định của 3, có giá trị logic là sai, FALSE (a > b)&&( a < b) // Có giá trị sai, FALSE. Giả sử a, b là 2 biến kiểu int Biểu thức điều kiện Là biểu thức có dạng biểu_thức_1 ? biểu_thức_2 : biểu_thức_3 Giá trị của biểu thức điều kiện sẽ là giá trị của biểu_thức_2 nếu biểu_thức _1 có giá trị khác 0 (tương ứng với giá trị logic ĐÚNG), và trái lại giá trị của biểu thức điều kiện sẽ là giá trị của biểu_thức_3 nếu biểu_thức_1 có giá trị bằng 0 (tương ứng với giá trị logic SAI). Ví dụ sau sẽ cho ta xác định được giá trị nhỏ nhất của 2 số nhờ sử dụng biểu thức điều kiện float x, y, z; // khai báo biến x = 3.8; y = 2.6; // gán giá trị cho các biến x, y z = (x<y) ? x : y; // z sẽ có giá trị bằng giá trị nhỏ nhất trong 2 số x và y Biểu thức dãy Lệnh dãy là lệnh gồm một dãy các biểu thức phân cách nhau bằng dấu phẩy và kết thúc lệnh là dấu chấm phẩy. Nó có dạng biểu_thức_1, biểu_thức_2, , biểu_thức_n; Trong lệnh dãy các biểu thức được tính toán độc lập với nhau 2.2.2.2. Thứ tự ưu tiên của các phép toán trong biểu thức Trong biểu thức có thể có nhiều toán tử, vậy điều gì giúp cho chương trình dịch thực hiện các toán tử một cách đúng đắn?. Trong các biểu thức nếu có các cặp (), thì nó sẽ quyết định thứ tự thực hiện các phép toán: trong ngoặc trước, ngoài ngoặc sau. Nhưng Trang 23
- có những khả năng dấu ngoặc không có hoặc không đủ để quyết định tất cả các trường hợp thì khi đó C thực hiện các toán tử căn cứ vào độ ưu tiên của chúng và sử dụng một số quy tắc về các toán tử (ví dụ như khi chúng cùng độ ưu tiên thì thực hiện từ trái qua phải hay từ phải qua trái). Ví dụ với các phép toán số học +, - có cùng độ ưu tiên, nên nếu trong biểu thức có nhiều phép toán +, - và không có các dấu ngoặc quy định thứ tự thực hiện thì chúng sẽ được thực hiện từ trái qua phải. Nhưng với phép toán ++, hay các phép gán, chẳng hạn như ++++ a; hoặc a=b=c=d trình tự kết hợp lại từ phải qua trái. Sau đây là bảng các toán tử và độ ưu tiên của chúng, các phép toán trên cùng dòng (thứ tự) có cùng độ ưu tiên, các toán tử trên dòng có thứ tự nhỏ hơn sẽ có độ ưu tiên cao hơn, trong bảng này có một số toán tử không được mô tả trong phần các phép toán như [], (), ., -> chúng sẽ được mô tả trong các phần thích hợp. Bảng thứ tự ưu tiên của các phép toán trong C Mức Các toán tử Trật tự kết hợp 1 () [] . -> ++ (hậu tố) hậu tố 2 ! ~ ++ (tiền tố) (tiền tố) - * & sizeof 3 * / % 4 + - 5 > 6 >= 7 == != 8 & 9 ^ 10 | 11 && 12 || 13 ?: 14 =+=-= Ghi chú: trật tự kết hợp từ trái qua phải trật tự kết hợp từ phải qua trái. Nguyên tắc xác định trật tự thực hiện các phép toán i. Biểu thức con trong ngoặc được tính toán trước các phép toán khác ii. Phép toán một ngôi đứng bên trái toán hạng được kết hợp với toán hạng đi liền nó. iii. Nếu toán hạng đứng cạnh hai toán tử thì có 2 khả năng là: a. Nếu hai toán tử có độ ưu tiên khác nhau thì toán tử nào có độ ưu tiên cao hơn sẽ kết hợp với toán hạng b. Nếu hai toán tử cùng độ ưu tiên thì dựa vào trật tự kết hợp của các toán tử để xác định toán tử được kết hợp với toán hạng. 2.2.2.3. Sử dụng biểu thức Trong chương trình, biểu thức được sử dụng cho các mục đích sau: Trang 24
- - Làm vế phải, của lệnh gán như công thức tính toán một đại lượng nào đó. - Làm toán hạng trong các biểu thức khác. - Làm tham số thực trong lời gọi hàm. - Làm chỉ số trong các cấu trúc lặp for, while, do while. - Làm biểu thức kiểm tra trong các cấu trúc rẽ nhánh if, switch. 2.3. Các lệnh vào ra Các công cụ lập trình nói chung và ngôn ngữ lập trình nói riêng đều cung cấp cho người dùng các chỉ thị tương ứng để thực hiện vào ra dữ liệu. Để vào ra dữ liệu, ngôn ngữ C cung cấp 2 hàm vào ra cơ bản là printf() và sacnf(). Muốn sử dụng 2 hàm printf() và scanf() ta cần khai báo tệp tiêu đề stdio.h #include 2.3.1. Lệnh hiển thị dữ liệu Cú pháp sử dụng hàm printf ( ) printf(xâu_định_dạng, [danh_sách_tham_số]); Hàm printf() được dùng để hiển thị ra màn hình các loại dữ liệu cơ bản như số, kí tự và xâu kí tự cùng một số hiệu ứng hiển thị đặc biệt. xâu_định_dạng là xâu điều khiển cách thức hiển thị dữ liệu trên thiết bị ra chuẩn(màn hình máy tính). Trong xâu_định_dạng có chứa: - Các kí tự thông thường, chúng sẽ được hiển thị ra màn hình bình thường. - Các nhóm kí tự định dạng dùng để xác định quy cách hiển thị các tham số trong phần danh_sách_tham_số. - Các kí tự điều khiển dùng để tạo các hiệu ứng hiển thị đặc biệt như xuống dòng (‘\n’) hay sang trang (‘\f’) Phần danh_sách_tham_số là các giá trị biến, hằng, biểu thức mà ta muốn hiển thị ra màn hình. Nhóm kí tự định dạng thứ k trong xâu_định_dạng dùng để xác định quy cách hiển thị tham số thứ k trong danh_sách_tham_số. Do đó danh_sách_tham_số phải phù hợp về số lượng, thứ tự và kiểu với các nhóm kí tự định dạng trong xâu_định_dạng. Số lượng tham số trong danh_sách_tham_số bằng số lượng nhóm các kí tự định dạng trong xâu_định_dạng. Ví dụ #include #include void main() { int a = 5; float x = 1.234; printf(“Hien thi mot so nguyen %d và mot so thuc %f”,a,x); getch(); } Kết quả: Hien thi mot so nguyen 5 va mot so thuc 1.234000 Trang 25
- Trong ví dụ trên “Hien thi mot so nguyen %d và mot so thuc %f” là xâu định dạng, còn a và x là các tham số của hàm printf(). Trong xâu định dạng trên có 2 nhóm kí tự định dạng là %d và %f, với %d dùng để báo cho máy biết rằng cần phải hiển thị tham số tương ứng (biến a) theo định dạng số nguyên và %f dùng để báo cho máy cần hiển thị tham số tương ứng (biến x) theo định dạng số thực. Một số nhóm kí tự định dạng hay dùng trong C và ý nghĩa: Nhóm kí tự Áp dụng cho kiểu Ghi chú định dạng dữ liệu %d int, long, char Hiển thị tham số tương ứng dưới dạng số nguyên có dấu hệ đếm thập phân %i int, long, char Hiển thị tham số tương ứng dưới dạng số nguyên có dấu hệ đếm thập phân %o int, long, char Hiển thị tham số tương ứng dưới dạng số nguyên không dấu trong hệ đếm cơ số 8. %u int, long, char Hiển thị tham số tương ứng dưới dạng số nguyên không dấu. %x int, long, char Hiển thị tham số tương ứng dưới dạng số nguyên hệ đếm 16 (không có 0x đứng trước), sử dụng các chữ cái a b c d e f %X int, long, char Hiển thị tham số tương ứng dưới dạng số nguyên hệ đếm 16 (không có 0x đứng trước), sử dụng các chữ cái A B C D E F %e float, double Hiển thị tham số tương ứng dưới dạng số thực dấu phẩy động %f float, double Hiển thị tham số tương ứng dưới dạng số thực dấu phẩy tĩnh %g float, double Hiển thị tham số tương ứng số thực dưới dạng ngắn gọn hơn trong 2 dạng dấu phẩy tĩnh và dấu phẩy động %c int, long, char Hiển thị tham số tương ứng dưới dạng kí tự %s char * Hiển thị tham số tương ứng dưới dạng xâu kí tự (xâu kí tự) Để trình bày dữ liệu được đẹp hơn, C cho phép đưa thêm một số thuộc tính định dạng dữ liệu khác vào trong xâu định dạng như độ rộng tối thiểu, căn lề trái, căn lề phải. Độ rộng tối thiểu Thông thường khi hiển thị dữ liệu, C tự động xác định số chỗ cần thiết sao cho hiển thị vừa đủ nội dung dữ liệu. Nếu ta muốn C hiển thị dữ liệu của ta trên một số lượng vị trí xác định bất kể nội dung dữ liệu đó có điền đầy số chỗ được cung cấp hay không, ta có thể chèn một số nguyên vào trong nhóm kí tự định dạng, ngay sau dấu %. Ví dụ khi hiển thị số nguyên a = 1234; printf("\n%5d",a); // dành 5 chỗ để hiển thị số nguyên a Trang 26
- printf("\n%5d",34);// dành 5 chỗ để hiển thị số nguyên 34 Kết quả 1234 34 Ở đây kí hiệu thay cho dấu trắng (space). Như vậy với nhóm kí tự định dạng %md, m dùng để báo số chỗ cần dành để hiển thị dữ liệu, còn d báo rằng hãy hiển thị dữ liệu đó dưới dạng một số nguyên. Tương tự với các nhóm kí tự định dạng %mc khi hiển thị kí tự, và %ms khi hiển thị xâu kí tự. Ví dụ printf("\n%3d %15s %3c", 1, "nguyen van a", 'g'); printf("\n%3d %15s %3c", 2, "tran van b", 'k'); Kết quả 1 nguyen van a g 2 tran van b k Nếu nội dung dữ liệu không điền đầy số chỗ được cấp thì những chỗ không dùng đến sẽ được điền bởi dấu trắng. Khi số chỗ cần thiết để hiển thị nội dung dữ liệu lớn hơn m thì C tự động cung cấp thêm chỗ mới để hiển thị chứ không cắt bớt nội dung của dữ liệu để cho vừa m vị trí. Với dữ liệu là số thực ta sử dụng mẫu nhóm kí tự định dạng %m.nf để báo rằng cần dành m vị trí để hiển thị số thực, và trong m vị trí đó dành n vị trí để hiển thị phần thập phân. Ví dụ: printf("\n%f",12.345); printf("\n%.2f",12.345); printf("\n%8.2f",12.345); Kết quả 12.345000 12.35 12.35 Căn lề trái Khi hiển thị dữ liệu, mặc định C căn lề phải. Nếu muốn căn lề trái khi hiển thị dữ liệu ta chỉ cần thêm dấu trừ - vào ngay sau dấu %. Ví dụ printf("\n%-3d %-15s %-4.2f %-3c", 1, "nguyen van a", 8.5, 'g'); printf("\n%-3d %-15s %-4.2f %-3c", 2, "tran van b", 6.75, 'k'); Kết quả 1 nguyen van a 8.50 g 2 tran van b 6.75 k Trang 27
- 2.3.2. Lệnh nhập dữ liệu Cú pháp: scanf(xâu_định_dạng, [danh_sách_địa_chỉ]); Hàm scanf() dùng để nhập dữ liệu từ bàn phím. Cụ thể nó sẽ đọc các kí tự được nhập từ bàn phím, sau đó căn cứ theo xâu_định_dạng sẽ chuyển những thông tin đã nhập được sang kiểu dữ liệu phù hợp. Cuối cùng sẽ gán những giá trị vừa nhập được vào các biến tương ứng trong danh_sách_địa_chỉ. xâu_định_dạng trong hàm scanf() xác định khuôn dạng của các dữ liệu được nhập vào. Trong xâu_định_dạng có chứa các nhóm kí tự định dạng xác định khuôn dạng dữ liệu nhập vào. Địa chỉ của một biến được viết bằng cách đặt dấu & trước tên biến. Ví dụ giả sử ta có các biến có tên là a, x, ten_bien thì địa chỉ của chúng lần lượt sẽ là &a, &x, &ten_bien. danh_sách_địa_chỉ phải phù hợp với các nhóm kí tự định dạng trong xâu_định_dạng về số lượng, kiểu dữ liệu và thứ tự. Số nhóm kí tự định dạng bằng số địa chỉ của các biến trong danh_sách_địa_chỉ. Dưới đây là một số nhóm kí tự định dạng hay dùng và ý nghĩa Nhóm kí tự Ghi chú định dạng %d Định khuôn dạng dữ liệu nhập vào dưới dạng số nguyên kiểu int %o Định khuôn dạng dữ liệu nhập vào dưới dạng số nguyên kiểu int hệ cơ số 8 %x Định khuôn dạng dữ liệu nhập vào dưới dạng số nguyên kiểu int hệ cơ số 16 %c Định khuôn dạng dữ liệu nhập vào dưới dạng kí tự kiểu char %s Định khuôn dạng dữ liệu nhập vào dưới dạng xâu kí tự %f Định khuôn dạng dữ liệu nhập vào dưới dạng số thực kiểu float %ld Định khuôn dạng dữ liệu nhập vào dưới dạng số nguyên kiểu long %lf Định khuôn dạng dữ liệu nhập vào dưới dạng số thực kiểu double Ví dụ: #include #include void main() { // khai bao bien int a; float x; char ch; char* str; // Nhap du lieu printf(“Nhap vao mot so nguyen”); scanf(“%d”,&a); printf(“\n Nhap vao mot so thuc”); scanf(“%f”,&x); Trang 28
- printf(“\n Nhap vao mot ki tu”); fflush(stdin); scanf(“%c”,&ch); printf(“\n Nhap vao mot xau ki tu”); fflush(stdin); scanf(“%s”,str); // Hien thi du lieu vua nhap vao printf(“\n Nhung du lieu vua nhap vao”); printf(“\n So nguyen: %d”,a); printf(“\n So thuc : %.2f”,x); printf(“\n Ki tu: %c:,ch); printf(“\n Xau ki tu: %s”,str); } Kết quả: Nhap vao mot so nguyen: 2007 Nhap vao mot so thuc: 18.1625 Nhap vao mot ki tu: b Nhap vao mot xau ki tu: ngon ngu lap trinh C Nhung du lieu vua nhap vao So nguyen: 2007 So thuc: 18.16 Ki tu: b Xau ki tu: ngon Một số quy tắc cần lưu ý khi sử dụng hàm scanf() Quy tắc 1: Khi đọc số, hàm scanf() quan niệm rằng mọi kí tự số, dấu chấm (‘.’) đều là kí tự hợp lệ. Khi gặp các dấu phân cách như tab, xuống dòng hay dấu cách (space bar) thì scanf() sẽ hiểu là kết thúc nhập dữ liệu cho một số. Quy tắc 2: Khi đọc kí tự, hàm scanf() cho rằng 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ự tab, xuống dòng hay dấu cách. Quy tắc 3: Khi đọc xâu kí tự, hàm scanf() nếu gặp các kí tự dấu trắng, dấu tab hay dấu xuống dòng thì nó sẽ hiểu là kết thúc nhập dữ liệu cho một xâu kí tự. Vì vậy trước khi nhập dữ liệu kí tự hay xâu kí tự ta nên dùng lệnh fflush(stdin). 2.3.3. Lệnh gán Biến có thể nhận một giá trị hằng hoặc giá trị của 1 biểu thức thông qua phép gán hay còn gọi là lệnh gán sau: Tên biến=giá trị; Hoặc Tên biến= Biểu thức; Ví dụ: x=1.234; y=2*x+sin(x); 2.3.4. Các lệnh nhập xuất khác Hàm gets(), có cú pháp gets(xâu_kí_tự); Trang 29
- Hàm gets() dùng để nhập vào từ bàn phím một xâu kí tự bao gồm cả dấu cách, điều mà hàm scanf() không làm được. Hàm puts(), có cú pháp puts(xâu_kí_tự); Hàm puts() sẽ hiển thị ra màn hình nội dung xâu_kí_tự và sau đó đưa con trỏ xuống dòng mới. Vì vậy nó tương đương với lệnh printf(“%s\n”,xâu_kí_tự). Hàm getch(), có cú pháp getch(); Hàm getch() là hàm không có tham số. Nó đọc một kí tự bất kì nhập vào từ bàn phím nhưng không hiển thị kí tự đó lên màn hình. Lệnh getch() thường dùng để chờ người sử dụng ấn một phím bất kì rồi sẽ kết thúc chương trình. Để sử dụng các hàm gets(), puts(), getch() ta cần khai báo tệp tiêu đề conio.h. Ví dụ: #include #include void main() { // khai bao bien char* str; // Nhap du lieu puts(“Nhap vao mot xau ki tu:”); fflush(stdin); gets(str); // Hien thi du lieu vua nhap vao puts(“Xau vua nhap vao: ”); puts(str); puts(“An phim bat ki de ket thuc ”); getch(); } Kết quả: Nhap vao mot xau ki tu: ngon ngu lap trinh C Xau vua nhap vao: ngon ngu lap trinh C An phim bat ki de ket thuc CHƯƠNG 3: CÁC LỆNH CẤU TRÚC MỤC TIÊU CỦA BÀI Sau khi học xong bài này người học có khả năng Kiến thức Trang 30
- - Trình bày được cấu trúc lệnh khối, cách sử dụng lệnh lựa chọn theo điều kiện; - Nêu được cấu trúc của các lệnh lặp for, while, do while. Kĩ năng Viết được chương trình đơn giản sử dụng các câu lệnh lựa chọn, lệnh khối, lệnh lặp. Thái độ Vận dụng tích cực, linh hoạt các kiến thức đã học vào các bài học tiếp theo, áp dụng viết các chương trình cơ bản. 3.1. Lệnh khối Một cách hình thức ta có thể định nghĩa một lệnh khối là dãy các câu lệnh được đặt trong cặp dấu ngoặc nhọn { }. { lệnh_1; lệnh_2; . lệnh_n; } Trong lệnh khối có thể chứa lệnh khối khác, ta gọi đó là các lệnh khối lồng nhau. Sự lồng nhau của các lệnh khối là không hạn chế. Các lệnh trong lệnh khối được thực hiện tuần tự theo trật tự xuất hiện. { lệnh; { lệnh; } } C cho phép khai báo biến trong lệnh khối. Ràng buộc duy nhất là phần khai báo phải nằm trước phần câu lệnh. Ví dụ: #include #include void main() // Noi dung cua ham main() cung la mot khoi lenh { // khai bao bien int c; c = 10; printf(“ Gia tri cua c = %d day la c ngoai”,c); // bat dau mot khoi lenh khac { Trang 31
- int c; c = 10; printf(“\n Gia tri cua c = %d day la c trong”,c); printf(“\n Tang gia tri cua c them 10 don vi”); c = c + 10; printf(“\n Gia tri cua c = %d day la c trong”,c); } printf(“\n Gia tri cua c = %d day la c ngoai”,c); getch(); } Kết quả: Gia tri cua c = 10 day la c ngoai Gia tri cua c = 10 day la c trong Tang gia tri cua c them 10 don vi Gia tri cua c = 20 day la c trong Gia tri cua c = 10 day la c ngoai 3.2. Lệnh lựa chọn 3.2.1. Lệnh lựa chọn theo điều kiện if Câu lệnh if cho phép lựa chọn một trong hai nhánh tùy thuộc vào giá trị của biểu thức luận lý là đúng (true) hay sai (false) hoặc khác không hay bằng không. Lệnh if có 2 dạng: - Dạng 1(còn gọi là dạng if khuyết) Cú pháp: if (biểu_thức_điều_kiện) lệnh; Công dụng: nếu biểu thức luận lý đúng thì thực hiện khối lệnh và thoát khỏi if, ngược lại không làm gì cả và thoát khỏi if - Dạng 2(if đầy đủ) Cú pháp: if (biểu_thức_điều_kiện) lệnh_1; else lệnh_2; Công dụng: nếu biểu thức luận lý đúng thì thực hiện khối lệnh 1 và thoát khỏi if ngược lại thực hiện khối lệnh 2 và thoát khỏi if Lưu ý: là lệnh, lệnh_1 và lệnh_2 có thể là lệnh khối. Trang 32
- if (biểu_thức_điều_kiện) if (biểu_thức_điều_kiện) lệnh_1; lệnh_1; lệnh_kế_tiếp else lệnh_2; lệnh_kế_tiếp 0 1 0 1 biểu_thức_điều_kiện == 0 ? biểu_thức_điều_kiện = = 0 ? lệnh_1 lệnh_1 lệnh_2 lệnh_kế_tiếp lệnh_kế_tiếp V í dụ 1: Viết chương trình nhập vào 2 số nguyên a, b. Tìm và in ra số lớn nhất. a. Phác họa lời giải Trước tiên ta cho giá trị a là giá trị lớn nhất bằng cách gán a cho max (max là biến được khai báo cùng kiểu dữ liệu với a, b). Sau đó so sánh b với a, nếu b lớn hơn a ta gán b cho max và cuối cùng ta được kết quả max là giá trị lớn nhất. b. Mô tả quy trình xử lý (giải thuật) Ngôn ngữ tự nhiên Ngôn ngữ C - Khai báo 3 biến a, b, max kiểu số - int ia, ib, imax; nguyên - printf("Nhap vao so a: "); - Nhập vào giá trị a scanf("%d", &ia); - Nhập vào giá trị b - printf("Nhap vao so b: "); - Gán a cho max scanf("%d", &ib); - Nếu b > a thì gán b cho max - imax = ia; - In ra kết quả max - if (ib > ia) imax = ib; - printf("So lon nhat = %d.\n", imax); c. Mô tả bằng lưu đồ Trang 33
- d. Viết chương trình Kết quả in ra màn hình Nhap vao so a : 10 Cho chạy lại chương trình và thử lại với: Nhap vao so b : 8 a = 7, b = 9 So lon nhat = 10. a = 5, b = 5 Quan sát và nhận xét kết quả Ví dụ 2: Viết chương trình nhập vào 2 số nguyên a, b. In ra thông báo "a bằng b" nếu a = b, ngược lại in ra thông báo "a khác b". a. Phác họa lời giải So sánh a với b, nếu a bằng b thì in ra câu thông báo "a bằng b", ngược lại in ra thông báo "a khác b". b. Mô tả quy trình xử lý (giải thuật) Ngôn ngữ tự nhiên Ngôn ngữ C - Khai báo 2 biến a, b kiểu số nguyên - int ia, ib; - Nhập vào giá trị a - printf("Nhap vao so a: "); - Nhập vào giá trị b scanf("%d", &ia); - Nếu a = b thì in ra thông báo "a bằng b" - printf("Nhap vao so b: "); Ngược lại (còn không thì) in ra thông scanf("%d", &ib); báo "a khác b" - if (ia == ib) printf("a bang b\n"); else printf("a khac b\n"); c. Mô tả bằng lưu đồ Trang 34
- d. Viết chương trình Kết quả in ra màn hình Nhap vao so a : 10 Cho chạy lại chương trình và thử lại với: Nhap vao so b : 8 a = 6, b = 6 a khac b a = 1, b = 5 Quan sát và nhận xét kết quả Lưu ý: Các lệnh if else lồng nhau thì else sẽ luôn luôn kết hợp với if nào chưa có else gần nhất. Vì vậy khi gặp những lệnh if không có else, Bạn phải đặt chúng trong những khối lệnh rõ ràng để tránh bị hiểu sai câu lệnh 3.2.2. Lệnh switch Lệnh switch cũng giống cấu trúc else if, nhưng nó mềm dẻo hơn và linh động hơn nhiều so với sử dụng if. Tuy nhiên, nó cũng có mặt hạn chế là kết quả của biểu thức phải là giá trị hằng nguyên (có giá trị cụ thể). Một bài toán sử dụng lệnh switch thì cũng có thể sử dụng if, nhưng ngược lại còn tùy thuộc vào giải thuật của bài toán Trang 35
- Cú pháp cấu trúc switch switch (biểu_thức) { case giá_trị_1: lệnh_1; [break;] case giá_trị_2: lệnh_2; [break;] case giá_trị_n: lệnh_n; [break;] [default: lệnh_n+1; [break;]] } Cơ chế hoạt động: câu lệnh switch ban đầu sẽ tính giá trị của biểu_thức, sau đó so sánh với các giá_trị_k với k = 1, 2, , n đứng sau case. Xảy ra 2 trường hợp Nếu trong dãy các giá trị giá_trị_1, giá_trị_2, tồn tại giá trị bằng biểu_thức. Gọi i là chỉ số của giá trị đầu tiên trong dãy thỏa mãn giá_trị_i bằng biểu_thức, khi đó lệnh_i sẽ được thực hiện. Sau khi thực hiện xong lệnh_i, nếu có lệnh break thì chương trình sẽ chuyển sang thực hiện lệnh tiếp sau cấu trúc switch. Nếu không có lệnh break thì chương trình sẽ chuyển sang thực hiện các lệnh sau lệnh_i nằm trong switch (tức là lệnh_i+1, lệnh_i+2 ) cho đến khi gặp lệnh break đầu tiên hoặc sau khi thực hiện xong lệnh n. Sau đó chương trình sẽ chuyển sang thực hiện lệnh tiếp theo sau cấu trúc switch. Nếu không tồn tại giá_trị_k (với k = 1, 2, n) nào bằng giá trị của biểu_thức thì sẽ có 2 khả năng: o Nếu có nhãn default: chương trình sẽ thực hiện lệnh_n+1 rồi chuyển sang thực hiện lệnh tiếp theo sau cấu trúc switch. o Nếu không có nhãn default: chương trình chuyển sang thực hiện lệnh tiếp theo sau cấu trúc switch. Sơ đồ: 0 0 0 biểu_thức == giá_trị_1 ? biểu_thức == giá_trị_2 ? . . . biểu_thức == giá_trị_n ? 1 1 1 lệnh_1 lệnh_2 lệnh_n 0 0 0 break ? break ? break ? 1 1 1 Ví dụ: /* Ví dụ sau yêu cầu người dùng nhập vào một số nguyên không âm và đưa ra ngày trong tuần tương ứng với số nguyên đó. Và ở đây ta quy ước những số chia hết Trang 36
- cho 7 ứng với Chủ nhật, chia 7 dư 1 ứng với thứ Hai, , chia 7 dư 6 ứng với thứ Bảy.*/ #include #include void main() { // khai bao bien int a; do { printf(“\n Nhap mot gia tri so nguyen khong am: “); scanf(“%d”,&a); if(a #include int main () { Trang 37
- int thang; clrscr(); printf("\n Nhap vao thangs trong nam "); scanf("%d",&thang); switch(thang) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: printf("\n Thang %d co 31 ngay ",thang); break; case 4: case 6: case 9: case 11: printf("\n Thang %d co 30 ngay ",thang); break; case 2: printf ("\ Thang 2 co 28 hoac 29 ngay"); break; default : printf("\n Khong co thang %d", thang); break; } getch(); return 0; } Lưu ý: giá trị của biểu thức kiểm tra phải là số nguyên tức là phải có kiểu dữ liệu là char, int, long. Một cách tương ứng các giá trị sau case cũng phải nguyên. Đây là một trong những điểm phân biệt giữa cấu trúc switch và if else. 3.3. Lệnh lặp Trong thuật toán có 3 cấu trúc điều khiển cơ bản là tuần tự, rẽ nhánh và lặp. Để thể hiện cấu trúc lặp, C cung cấp các cấu trúc lặp sau: vòng lặp for, vòng lặp while, vòng lặp do while. 3.3.1. Lệnh for Dạng thường gặp của vòng lặp for là for([biểu_thức_1];[biểu_thức_2];[biểu_thức_3]) lệnh; Trang 38
- Sơ đồ for([biểu_thức_1];[biểu_thức_2];[biểu_thức_3]) lệnh Thực hiện tính giá trị biểu_thức_1 Thực hiện tính giá trị Thực hiện tính giá trị biểu_thức_2 biểu_thức_3 0 biểu_thức_2 == 0 lệnh 1 Câu lệnh for thường dùng để thực hiện lặp đi lặp lại một công việc nào đó với số lần lặp xác định. Giải thích: + biểu_thức_1: khởi tạo giá trị ban đầu cho biến điều khiển. + biểu_thức_2: là quan hệ logic thể hiện điều kiện tiếp tục vòng lặp. + biểu_thức_3: phép gán dùng thay đổi giá trị biến điều khiển. Nhận xét: + biểu_thức_1 bao giờ cũng chỉ được tính toán một lần khi gọi thực hiện for. + biểu_thức_2,3 và thân for có thể thực hiện lặp lại nhiều lần. Lưu ý: + biểu_thức_1,2,3 phải phân cách bằng dấu chấm phẩy (;) + Nếu biểu_thức_2 không có, vòng for được xem là luôn luôn đúng. Muốn thoát khỏi vòng lặp for phải dùng một trong 3 lệnh break, goto hoặc return. + Với mỗi biểu thức có thể viết thành một dãy biểu thức con phân cách nhau bởi dấu phẩy. Khi đó các biểu thức con được xác định từ trái sang phải. Tính đúng sai của dãy biểu thức con trong biểu thức thứ 2 được xác định bởi biểu thức con cuối cùng. + Trong thân for (khối lệnh) có thể chứa một hoặc nhiều cấu trúc điều khiển khác. + Khi gặp lệnh break, cấu trúc lặp sâu nhất sẽ thoát ra. + Trong thân for có thể dùng lệnh goto để thoát khỏi vòng lặp đến vị trí mong muốn. + Trong thân for có thể sử dụng return để trở về một hàm nào đó. + Trong thân for có thể sử dụng lệnh continue để chuyển đến đầu vòng lặp (bỏ qua các câu lệnh còn lại trong thân). Ví dụ 1: Viết chương trình nhập vào số nguyên n. Tính tổng các giá trị lẻ từ 0 đến n. Trang 39
- Kết quả in ra màn hình Nhap vao so n : 5 Bạn thay các dòng từ 11 đến 16 bằng câu lệnh: Tong: 9. for(is=0, i=1; i = 1; i ) - Thay đổi biến điều khiển từ 7 đến 77, mỗi lần tăng 7: for(i = 7; i = 2; i –= 2) 3.3.2. Lệnh while Vòng lặp thực hiện lặp lại trong khi biểu thức còn đúng. Cú pháp: while (biểu thức) khối lệnh; Sơ đồ Trang 40
- while (biểu_thức) lệnh; Tính toán giá trị của biểu_thức lệnh 0 biểu_thức == 0? 1 Công dụng: Trước tiên biểu thức được kiểm tra nếu sai thì kết thúc vòng lặp while (khối lệnh không được thi hành 1 lần nào) nếu đúng thực hiện khối lệnh; lặp lại kiểm tra biểu thức - Biểu thức: có thể là một biểu thức hoặc nhiều biểu thức con. Nếu là nhiều biểu thức con thì cách nhau bởi dấu phẩy (,) và tính đúng sai của biểu thức được quyết định bởi biểu thức con cuối cùng. - Trong thân while (khối lệnh) có thể chứa một hoặc nhiều cấu trúc điều khiển khác. - Trong thân while có thể sử dụng lệnh continue để chuyển đến đầu vòng lặp (bỏ qua các câu lệnh còn lại trong thân). - Muốn thoát khỏi vòng lặp while tùy ý có thể dùng các lệnh break, goto, return như lệnh for. Ví dụ: Viết chương trình tính tổng các số nguyên từ 1 đến n, với n được nhập vào từ bàn phím. Trang 41
- Kết quả in ra màn hình Nhap vao so n : 5 Bạn thay các dòng từ 11 đến 12 bằng câu lệnh: Tong: 15. while(is = is+i, i++ < in); Chạy lại chương trình, quan sát và nhận xét kết quả 3.3.3. Lệnh do while Vòng lặp thực hiện lặp lại cho đến khi biểu thức sai. Cú pháp: do { lệnh; }while (biểu_thức); Sơ đồ do { lệnh; } while (biểu_thức) lệnh Tính toán giá trị của biểu_thức 0 biểu_thức == 0? 1 Công dụng: Thực hiện khối lệnh Kiểm tra biểu thức Nếu đúng thì lặp lại thực hiện khối lệnh Nếu sai thì kết thúc vòng lặp (khối lệnh được thi hành 1 lần) - Biểu thức: có thể là một biểu thức hoặc nhiều biểu thức con. Nếu là nhiều biểu thức con thì cách nhau bởi dấu phẩy (,) và tính đúng sai của biểu thức được quyết định bởi biểu thức con cuối cùng. - Trong thân do while (khối lệnh) có thể chứa một hoặc nhiều cấu trúc điều khiển khác. - Trong thân do while có thể sử dụng lệnh continue để chuyển đến đầu vòng lặp (bỏ qua các câu lệnh còn lại trong thân). - Muốn thoát khỏi vòng lặp do while tùy ý có thể dùng các lệnh break, goto, return. Ví dụ: Viết chương trình kiểm tra password. Trang 42
- Kết quả in ra màn hình Nhap vao password: 1123 Bạn thay các dòng từ 10 đến 14 bằng câu lệnh: Nhap vao password: 12346 do{}while(printf("Nhap vao password: "), scanf("%d", Nhap vao password: 12345 &in), in != PASSWORD); Chạy lại chương trình và quan sát kết quả - Các vòng lặp while, do{ }while, hay for sẽ kết thúc quá trình lặp khi biểu thức điều kiện của vòng lặp không còn được thỏa mãn. Tuy nhiên trong lập trình đôi khi ta cũng cần thoát khỏi vòng lặp ngay cả khi biểu thức điều kiện của vòng lặp vẫn còn được thỏa mãn. Để hỗ trợ người lập trình làm việc đó, ngôn ngữ C cung cấp 2 câu lệnh là continue và break Vòng lặp với các lệnh break và continue Điều kiện lặp còn thỏa mãn break continue Lệnh tiếp theo Lệnh tiếp theo Lệnh tiếp theo Lệnh continue Khi gặp lệnh continue trong thân vòng lặp, chương trình sẽ chuyển sang thực hiện một vòng lặp mới và bỏ qua việc thực hiện các câu lệnh nằm sau lệnh continue trong thân vòng lặp. Trang 43
- Ví dụ sau đây sẽ in ra màn hình các số tự nhiên lẻ và nhỏ hơn 100 #include #include void main() { int i; for(i = 1;i #include void main() { char ch; clrscr(); do { printf(“\n Nhap vao mot ki tu: “); fflush(stdin); scanf(“%c”,&ch); printf(“\n Ki tu vua nhap vao la %c”,ch); if((ch ==’T’)||(ch ==’t’)) break; }while(1); Trang 44
- printf(“\n An phim bat ki de ket thuc chuong trinh ”); getch(); } Kết quả thực hiện chương trình Nhap vao mot ki tu: a Ki tu vua nhap vao la a Nhap vao mot ki tu: 5 Ki tu vua nhap vao la 5 Nhap vao mot ki tu: t Ki tu vua nhap vao la t An phim bat ki de ket thuc chuong trinh CHƯƠNG 4: HÀM TRONG C MỤC TIÊU CỦA BÀI Sau khi học xong bài này người học có khả năng Kiến thức: Trình bày về các loại chương trình con, cách khai báo hàm, các hàm thư viện thường gặp. Kĩ năng: Viết được một chương trình con đơn giản và gọi chương trình con trong hàm main. Thái độ: Vận dụng tích cực, linh hoạt các kiến thức đã học vào các bài học tiếp theo, áp dụng viết các chương trình cơ bản. 4.1. Khái niệm về hàm và thư viện 4.1.1. Chương trình con (Subprogram) Trong khi lập trình chúng ta thường gặp những đoạn chương trình lặp đi lặp lại nhiều lần ở những chỗ khác nhau. Để tránh rườm rà và tiết kiệm công sức, những đoạn chương trình đó được thay thế bởi các chương trình con tương ứng và khi cần ta chỉ việc gọi những chương trình con đó ra mà không phải viết lại cả đoạn chương trình đó. Lấy ví dụ khi giải các bài toán lượng giác ta thường xuyên cần phải tính giá trị sin của đại lượng lượng giác x nào đó. Như vậy ta nên lập một chương trình con tên là sin và tham số là x để tính giá trị sin(x). Mỗi khi cần tính toán giá trị sin của một đại lượng y nào đó thì ta chỉ cần gọi chương trình con sin đã lập sẵn và truyền đại lượng y làm tham số cho chương trình con sin đó thì ta vẫn thu được kết quả mong muốn mà không phải viết lại cả đoạn chương trình tính giá trị sin(y). Bên cạnh chương trình con sin còn có rất nhiều chương trình con khác được tạo sẵn như cos, exp (dùng để tính lũy thừa cơ số e), pow (tính lũy thừa), sqrt (tính căn bậc 2), giúp người lập trình tính toán giá trị của các đại lượng thông dụng. Những chương trình con này nằm trong thư viện các chương trình con mẫu và được trình biên dịch C quản lý, vì vậy chúng còn được gọi là các chương trình con chuẩn. Trình biên dịch Turbo C++ phân loại và đặt các chương trình con chuẩn này trong các đơn vị chương trình khác nhau dưới dạng các tệp tiêu đề như stdio.h, conio.h, math.h, string.h Trang 45
- Ngoài ra còn có một lý do khác cần đến chương trình con. Khi ta giải quyết một bài toán lớn thì chương trình của ta có thể rất lớn và dài, điều này làm cho việc sửa chữa, gỡ rối, hiệu chỉnh chương trình gặp nhiều khó khăn. Nhưng nếu ta chia bài toán lớn, phức tạp ban đầu thành các bài toán con nhỏ hơn và tương đối độc lập với nhau, rồi lập các chương trình con giải quyết từng bài toán con, cuối cùng ghép các chương trình con đó lại thành một chương trình giải quyết bài toán ban đầu thì sẽ rất tiện lợi cho việc phát triển, kiểm tra và sửa chữa cả chương trình. Việc này tương tự như trong dây chuyền sản xuất công nghiệp khi ta lắp ráp sản phẩm hoàn thiện từ các bán thành phẩm, các module được chế tạo ở những nơi khác nhau. Vì các bán thành phẩm này được chế tạo độc lập nên khi phát hiện lỗi ở module nào ta chỉ việc tìm đến nơi sản xuất ra nó để sửa chữa. Việc chia nhỏ một chương trình thành các chương trình con đảm nhận những công việc nhỏ khác nhau chính là tư tưởng chính cho phương pháp lập trình có cấu trúc (structured programming). Cần lưu ý là có khi một chương trình con chỉ sử dụng đúng một lần nhưng nó vẫn làm cho chương trình trở nên sáng sủa và dễ đọc, dễ hiểu hơn. Có 2 loại chương trình con là hàm ( function) và thủ tục (procedure). Sự khác nhau giữa hàm và thủ tục là ở chỗ hàm sau khi thực hiện xong thì sẽ trả về giá trị, còn thủ tục không trả về giá trị gì cả. Mặc dù vậy hàm và thủ tục là tương đương nhau, tức là có thể xây dựng được thủ tục có chức năng tương đương với một hàm bất kì và có thể xây dựng được hàm có chức năng tương đương với một thủ tục bất kì. Vì thế có những ngôn ngữ lập trình cho phép chương trình con có thể là hàm và thủ tục (Pascal) và có những ngôn ngữ chỉ cho phép chương trình con là hàm mà thôi (như C, Java). Lưu ý là nếu chương trình con là hàm thì nó luôn có giá trị trả về. Nếu thực sự không có giá trị gì để trả về (nghĩa là nó hoạt động giống thủ tục) thì ta phải khai báo hàm đó có kiểu giá trị trả về là “không là kiểu giá trị nào cả” (kiểu void trong C). 4.1.2. Một số đặc tính của hàm trong C Một chương trình C có thể bao gồm nhiều hàm và trong đó nhất thiết phải có hàm main(). Hàm trong C có một số đặc tính khác biệt sau: - Giá trị của hàm có thể không dùng đến. Đó là trường hợp hàm printf() và scanf() mà chúng ta đã dùng nhiều trong các chương trước với công dụng là lệnh hiển thị và nhập dữ liệu. Hai hàm này có giá trị là số, song chúng ta hầu như không sử dụng đến. - Hàm có thể không có giá trị trả lại qua tên hàm (giá trị gán cho tên hàm). Ví dụ với các hàm có kiểu void như hàm main() - Hàm có thể không có đối số. Ví dụ hàm main(). 4.1.3. Thư viện hàm chuẩn Cũng như các ngôn ngữ khác, C có nhiều hàm được lập sẵn. Để thuận lợi cho việc sử dụng, các hàm này được chia thành các nhóm theo chức năng và được lưu trong các tệp tiêu đề header có tên *.h. Sau đây là một số tệp header (tệp thư viện mẫu): Trang 46
- stdio.h Thư viện các hàm input/output (printf,scanf, ) math.h Thư viện các hàm toán học (sin,có,pow, ) string.h Thư viện các hàm về xâu ký tự (strlen,strcpy,strcmp, ) ctype.h Thư viện các hàm xử lý ký tự (toupper, ) conio.h Thư viện các hàm về màn hình,bàn phím (clrscr(),getch(), ) Trước khi sử dụng các hàm, phải khai báo các tệp header chứa chúng bằng lệnh khai báo sau: #include Ví dụ: Trước khi dùng hàm sin phải có khai báo tệp header math.h như sau: #include Mặc dù C cung cấp khá nhiều hàm, song không thể đáp ứng mọi yêu cầu của tất cả người dùng ở các lĩnh vực xử lý khác nhau. Như các ngôn ngữ khác, C cung cấp cơ chế xây dựng hàm cho người dùng. Hàm do người dùng tự xây dựng gọi là hàm do người sử dụng định nghĩa (User Defined Function - UDF) 4.2. Quy trình xây dựng hàm 4.2.1. Khai báo hàm Cú pháp khai báo một hàm trong C là như sau [ ] ([ , ]) Thân hàm Khai báo của một hàm được chia làm 2 phần: - Dòng đầu hàm: [ ] ([ , ]) - Thân hàm: là tập hợp các khai báo và câu lệnh đặt trong cặp dấu ngoặc nhọn { } Trong thân hàm có ít nhất một lệnh return. Ví dụ sau là khai báo và định nghĩa hàm tính giai thừa của một số nguyên dương. Ta quy ước rằng giai thừa của một số âm thì bằng –1, của 0 bằng 0, của một số nguyên dương a là a! = a (a-1) 1. int giai_thua(int a) Dòng đầu hàm { int ket_qua; Các khai báo int i; ket_qua = 1; for(i = 0;i<a;i++) ket_qua = ket_qua * i; if(a < 0) ket_qua = -1; if(a == 0) ket_qua = 0; Các câu lệnh return ket_qua; Trang 47
- } Các thành phần của dòng đầu hàm Dòng đầu hàm là các thông tin được trao đổi giữa bên trong và bên ngoài hàm. Khi nói tới dòng đầu hàm là ta nói tới tên của hàm, hàm đó cần những thông tin gì từ môi trường để hoạt động (các tham số đầu vào), hàm đó cung cấp những thông tin gì cho môi trường (những tham số đầu ra và giá trị trả về). Dòng đầu hàm phân biệt các hàm với nhau, hay nói cách khác không được có 2 hàm có dòng đầu hàm giống nhau. Kiểu dữ liệu trả về của hàm Thông thường hàm sau khi được thực hiện sẽ trả về một giá trị kết quả tính toán nào đó. Để sử dụng được giá trị đó ta cần phải biết nó thuộc kiểu dữ liệu gì. Kiểu dữ liệu của đối tượng tính toán được hàm trả về được gọi là kiểu dữ liệu trả về của hàm. Trong C, kiểu dữ liệu trả về của hàm có thể là kiểu dữ liệu bất kì (kiểu dữ liệu có sẵn hoặc kiểu dữ liệu do người dùng tự định nghĩa) nhưng không được là kiểu dữ liệu mảng. Nếu kiểu dữ liệu trả về là kiểu void thì hàm không trả về giá trị nào cả. Trường hợp ta không khai báo kiểu dữ liệu trả về thì chương trình dịch của C sẽ ngầm hiểu rằng kiểu dữ liệu trả về của hàm là kiểu int. Tên hàm Tên hàm là có thể là bất kì một định danh hợp lệ nào. Tuy nhiên tên hàm nên mang nghĩa gợi ý chức năng công việc mà hàm thực hiện. Ví dụ một hàm có chức năng tính và trả về bình phương của một số thực x thì nên có tên là binh_phuong. Trong C, các hàm không được đặt tên trùng nhau. Tham số của hàm Tham số của hàm là các thông tin cần cho hoạt động của hàm và các thông tin, kết quả tính toán được hàm trả lại. Tức là có những tham số chứa dữ liệu vào cung cấp cho hàm, có những tham số chứa dữ liệu ra mà hàm tính toán được. Các tham số sử dụng trong lời khai báo hàm được gọi là tham số hình thức. Nó là tham số giả định của hàm. Khi khai báo tham số hình thức của hàm phải chỉ ra tên của tham số và kiểu dữ liệu của tham số. Các tham số được cung cấp cho hàm trong quá trình thực hiện của hàm được gọi là tham số thực. Kiểu dữ liệu của tham số thực cung cấp cho hàm trong chương trình phải giống kiểu dữ liệu của tham số hình thức tương ứng với tham số thực đó, nếu không sẽ có báo lỗi biên dịch. Một hàm có thể có một, nhiều hoặc không có tham số nào cả. Nếu có nhiều tham số thì chúng phải được phân cách với nhau bằng dấu phẩy. Lưu ý là nếu hàm không có tham số nào cả thì vẫn phải có cặp dấu ngoặc đơn sau tên hàm, ví dụ main(). Lệnh return Trong chương trình, một hàm được thực hiện khi ta gặp lời gọi hàm của hàm đó trong chương trình. Một lời gọi hàm là tên hàm theo sau bởi các tham số thực trong chương trình. Sau khi hàm thực hiện xong, nó sẽ trở về chương trình đã gọi nó. Có 2 cách để từ hàm trở về chương trình đã gọi hàm: Trang 48
- - Sau khi thực hiện tất cả các câu lệnh có trong thân hàm. - Khi gặp lệnh return. Cú pháp chung của lệnh return là returnbiểu_thức; Khi gặp lệnh này, chương trình sẽ tính toán giá trị của biểu_thức, lấy kết quả tính toán được làm giá trị trả về cho lời gọi hàm rồi kết thúc việc thực hiện hàm, trở về chương trình đã gọi nó. Trong lệnh return cũng có thể không có phần biểu_thức, khi đó ta sẽ kết thúc thực hiện hàm mà không trả về giá trị nào cả. Ví dụ và phân tích. #include #include int max(int x, int y, int z) { int max; max = x>y?x:y; max = max>z?max:z; return max; } void main() { int a,b,c; clrscr(); printf("\n Nhap gia tri cho 3 so nguyen a, b, c: "); scanf("%d %d %d",&a,&b,&c); printf("\n Gia tri cac so vua nhap: "); printf(" a = %-5d b = %-5d c = %-5d"); printf("\n Gia tri lon nhat trong 3 so la %d",max(a,b,c)); getch(); } 4.2.2. Sử dụng hàm Một hàm sau khi khai báo thì có thể sử dụng. Để sử dụng một hàm (hay còn nói là gọi hàm) ta sử dụng cú pháp sau: ([danh sách các tham số]) Ví dụ: chương trình dưới đây sẽ khai báo và định nghĩa một hàm có tên là Uscln với 2 tham số đều có kiểu unsigned int. Hàm Uscln tìm ước số chung lớn nhất của 2 tham số này theo thuật toán Euclid và trả về ước số chung tìm được. Sau đó ta sẽ gọi hàm Uscln trong hàm main để tìm ước số chung lớn nhất của 2 số nguyên được nhập từ bàn phím. #include #include unsigned int Uscln(unsigned int a, unsigned int b) Trang 49
- { unsigned int u; if (a<b) { u = a; a = b; b = u; } do { u = a%b; a = b; b = u; }while (u!=0); return a; } int main() { unsigned int a, b; do { printf("\n Nhap vao 2 so nguyen duong a va b "); printf("\n a = "); scanf("%d",&a); printf("\n b = "); scanf("%d",&b); if(a*b == 0) { printf("\n Khong hop le"); continue; } printf("\n Uoc chung lon nhat cua %d va %d la: %d", a, b, Uscln(a, b)); }while ((a != 0)||(b != 0)); printf("\n An phim bat ki de ket thuc chuong trinh "); getch(); return 0; } Kết quả khi thực hiện: Nhap vao 2 so nguyen duong a va b a = 6 b = 9 Uoc chung lon nhat cua 6 va 9 la: 3 Nhap vao 2 so nguyen duong a va b a = 15 Trang 50
- b = 26 Uoc chung lon nhat cua 15 va 26 la: 1 Nhap vao 2 so nguyen duong a va b a = 3 b = 0 Khong hop le Nhap vao 2 so nguyen duong a va b a = 0 b = 0 Khong hop le An phim bat ki de ket thuc chuong trinh Lưu ý: - Nếu có nhiều tham số trong danh sách tham số thì các tham số được phân cách với nhau bằng dấu phẩy - Cho dù hàm có một, nhiều hay không có tham số thì vẫn luôn luôn cần cặp dấu ngoặc đơn đứng sau tên hàm 4.2.3. Quy tắc hoạt động của hàm Trong chương trình, khi gặp một lời gọi hàm thì chuyển thi hành đến hàm được gọi. Quá trình diễn ra như sau: - Nếu hàm có tham số, trước tiên các tham số sẽ được gán giá trị thực tương ứng - Chương chình sẽ thực hiện tiếp các câu lệnh trong thân hàm bắt đầu từ lệnh đầu tiên đến câu lệnh cuối cùng - Khi gặp lệnh return hoặc dấu } cuối cùng trong thân hàm, chuwogn trình sẽ thoát khỏi hàm để trở về chương trình gọi nó và thực hiện tiếp tục những câu lệnh của chương trình này. Ví dụ: 4.2.4. Tính tổng thể và cục bộ của biến Phạm vi của các biến Một biến sau khi khai báo thì có thể được sử dụng trong chương trình. Tuy nhiên tùy vào vị trí khai báo biến mà phạm vi sử dụng các biến sẽ khác nhau. Nguyên tắc sử dụng biến là biến khai báo trong phạm vi nào thì được sử dụng trong phạm vi đó. Trang 51
- Một biến có thể được khai báo trong chương trình chính hoặc trong các chương trình con hoặc thậm chí trong một lệnh khối. Nếu biến được khai báo trong một lệnh khối thì nó chỉ có thể được gọi trong lệnh khối đó thôi, không thể gọi từ bên ngoài lệnh khối được. Một biến được khai báo trong một chương trình con chỉ có thể được sử dụng trong phạm vi chương trình con đó. Một biến được khai báo trong chương trình chính thì có thể được sử dụng trong toàn bộ chương trình, trong tất cả các chương trình con cũng như trong các lệnh khối của chương trình. Lưu ý Một số ngôn ngữ lập trình như Pascal cho phép khai báo một chương trình con nằm trong một chương trình con khác, nhưng ngôn ngữ C không cho phép khai báo một chương trình con nằm trong một chương trình con khác. Bên trong một lệnh khối thì có thể có chứa lệnh khối khác. Khi đó biến được khai báo ở lệnh khối bên ngoài có thể được sử dụng ở lệnh khối bên trong. Việc trùng tên của các biến: Trong cùng một phạm vi ta không được phép khai báo 2 biến có cùng tên nhưng ta có thể khai báo 2 biến trùng tên thuộc 2 phạm vi khác nhau. Nếu có 2 biến trùng tên khai báo ở 2 phạm vi khác nhau thì xảy ra 2 trường hợp: o Hai phạm vi này tách rời nhau: khi đó các biến sẽ có tác dụng ở phạm vi riêng của nó, không ảnh hưởng đến nhau. o Phạm vi này nằm trong phạm vi kia: khi đó nếu chương trình đang ở phạm vi ngoài (tức là đang thực hiện câu lệnh nằm ở phạm vi ngoài) thì biến khai báo ở phạm vi ngoài có tác dụng, còn nếu chương trình đang ở phạm vi trong (đang thực hiện câu lệnh nằm ở phạm vi trong) thì biến khai báo ở phạm vi trong sẽ có tác dụng và nó che lấp biến trùng tên ở bên ngoài. Ví dụ: #include void main() { { int a = 1; printf(“\n a = %d”,a); { int a = 2; printf(“\n a = %d”,a); } printf(“\n a = %d”,a); } { int a = 3; printf(“\n a = %d”,a); } } Kết quả thực hiện chương trình Trang 52
- a = 1 a = 2 a = 1 a = 3 Phân loại biến Theo phạm vi sử dụng, biến chia làm 2 loại: biến cục bộ (biến địa phương – local variable) và biến toàn cục (global variable). Biến địa phương Là các biến được khai báo trong lệnh khối hoặc trong thân chương trình con. Việc khai báo các biến cục bộ phải được đặt trước phần câu lệnh trong lệnh khối hoặc trong chương trình con. Biến toàn cục Các biến toàn cục là biến được thấy bởi toàn bộ chương trình, và có thể được sử dụng bởi một mã lệnh bất kỳ. Chúng được khai báo bên ngoài các hàm của chương trình và lưu giá trị của chúng trong suốt sự thực thi của chương trình. Các biến này có thể được khai báo bên ngoài main() hoặc khai báo bất kỳ nơi đâu trước lần sử dụng đầu tiên. Tuy nhiên, tốt nhất để khai báo các biến toàn cục là tại đầu chương trình, nghĩa là trước hàm main(). int ctr; /* ctr is global */ void blk1(void); void blk2(void); void main(void) { ctr = 10; blk1 (); . . } void blk1(void) { int rtc; if (ctr > 8) { rtc = rtc + 1; blk2(); } } void blk2(void) { int ctr; ctr = 0; Trang 53
- } Trong đoạn mã lệnh trên, ctr là một biến toàn cục và được khai báo bên ngoài hàm main() và blk1(), nó có thể được tham chiếu đến trong các hàm. Biến ctr trong blk2 (), là một biến cục bộ và không có liên quan với biến toàn cục ctr. Nếu một biến toàn cục và cục bộ có cùng tên, tất cả các tham chiếu đến tên đó bên trong khối chứa định nghĩa biến cục bộ sẽ được kết hợp với biến cục bộ mà không phải là biến toàn cục. Các biến toàn cục được lưu trữ trong các vùng cố định của bộ nhớ. Các biến toàn cục hữu dụng khi nhiều hàm trong chương trình sử dụng cùng dữ liệu. Tuy nhiên, nên tránh sử dụng biến toàn cục nếu không cần thiết, vì chúng giữ bộ nhớ trong suốt thời gian thực hiện chương trình. Vì vậy việc sử dụng một biến toàn cục ở nơi mà một biến cục bộ có khả năng đáp ứng cho hàm sử dụng là không hiệu quả. Ví dụ sau sẽ giúp làm rõ hơn điều này: void addgen(int i, int j) { return(i + j); } int i, j; void addspe(void) { return(i + j); } Cả hai hàm addgen() và addspe() đều trả về tổng của các biến i và j. Tuy nhiên, hàm addgen() được sử dụng để trả về tổng của hai số bất kỳ; trong khi hàm addspe() chỉ trả về tổng của các biến toàn cục i và j. Lưu ý: - Hàm main() cũng chỉ là một chương trình con, nhưng nó là chương trình con đặc biệt ở chỗ chương trình được bắt đầu thực hiện từ hàm main(). - Biến khai báo trong hàm main() không phải là biến toàn cục mà là biến cục bộ của hàm main(). CHƯƠNG 5: DỮ LIỆU KIỂU MẢNG MỤC TIÊU CỦA BÀI Sau khi học xong bài này người học có khả năng Kiến thức: Trình bày được khái niệm về Mảng, cách khai báo và sử dụng mảng 1 chiều và mảng 2 chiều Kĩ năng - Phân biệt được mảng 1 chiều và mảng 2 chiều; - Lựa chọn loại khai báo mảng cho từng bài tập; - Khai báo, hiển thị mảng 1 chiều, 2 chiều. Thái độ: Vận dụng tích cực, linh hoạt các kiến thức đã học vào các bài học tiếp theo, áp dụng viết các chương trình cơ bản. Trang 54
- 5.1. Khái niệm và khai báo mảng 5.1.1. Khái niệm về mảng Một biến (biến đơn) tại một thời điểm chỉ có thể biểu diễn được một giá trị. Vậy để có thể lưu trữ được một dãy các giá trị cùng kiểu chẳng hạn như các thành phần của vector trong không gian n chiều chúng ta cần n biến a1, a2, ,an. rất cồng kềnh và rất bất tiện nhất là khi n lớn và lại không phải là cố định. Các ngôn ngữ lập trình đưa ra một khái niệm mảng để giải quyết vấn đề này. Mảng là một tập hợp hữu hạn các phần tử có cùng kiểu dữ liệu được lưu trữ kế tiếp nhau trong bộ nhớ. Các phần tử trong mảng có cùng tên (và cũng là tên mảng) nhưng phân biệt với nhau ở chỉ số cho biết vị trí của chúng trong mảng. • Các thông tin về mảng: Với một mảng phải xác định các thông tin: tên mảng, kiểu các phần tử (kiểu mảng), số phần tử trong mảng (kích thước mảng). Ví dụ như chúng ta nói a là mảng có 20 phần tử, kiểu nguyên. Mảng cũng như các biến đơn khác trong ngôn ngữ C, trước khi sử dụng nó phải đảm bảo là nó đã được cấp phát trong bộ nhớ và đã sẵn sàng để sử dụng • Số chiều của mảng: trong ví dụ chúng ta nêu trên về vector, chúng ta có một dãy n các số, nếu như chúng ta dùng một mảng để lưu trữ các số đó thì chúng ta cần mảng có n phần tử và chỉ cần 1 chỉ số để xác định các phần tử - đây là mảng một chiều. Nếu như chúng ta cần một mảng để biểu diễn một bảng có n dòng, m cột, và để xác định một phần tử trong mảng chúng ta cần 2 chỉ số: chỉ số dòng và chỉ số cột, như vậy chúng ta có mảng 2 chiều. 5.1.2. Khai báo và sử dụng mảng Khai báo mảng Trong C để khai báo một mảng ta sử dụng cú pháp khai báo sau: kiểu_dữ_liệu tên_mảng [kích_thước_mảng]; Trong đó kiểu_dữ_liệu là kiểu dữ liệu của các phần tử trong mảng. tên_mảng là tên của mảng. kích_thứớc_mảng cho biết số phần tử trong mảng. Ví dụ: int mang_nguyen[10]; // khai báo mảng 10 phần tử có kiểu dữ liệu int float mang_thuc[4]; // khai báo mảng 4 phần tử có kiểu dữ liệu float char mang_ki_tu[6]; // khai báo mảng 6 phần tử có kiểu dữ liệu char Trong ví dụ trên, mảng mang_nguyen được lưu trữ trên 20 ô nhớ (mỗi ô nhớ có kích thước 1 byte, 2 ô nhớ kích thước là 2 byte lưu trữ được một số nguyên kiểu int) liên tiếp nhau. Do C đánh số các phần tử của mảng bắt đầu từ 0 nên phần tử thứ i của mảng sẽ có chỉ số là i-1 và do vậy sẽ có tên là mang_nguyen[i-1]. Ví dụ: phần tử thứ nhất của mảng là mang_nguyen[0], phần tử thứ 2 là mang_nguyen[1], phần tử thứ 5 là mang_nguyen[4] mang_nguyen[0] mang_nguyen[1] mang_nguyen[9] Kích thước của mảng bằng kích thước một phần tử nhân với số phần tử. Sử dụng mảng Trang 55
- Đểtruy nhập vào một phần tử của mảng thông qua tên của nó. Tên một phần tử của mảng được tạo thành từ tên mảng và theo sau là chỉ số của phần tử đó trong mảng được đặt trong cặp dấu ngoặc vuông tên_mảng[chỉ_số_của_phần_tử] Ví dụ với khai báo int mang_nguyen[3]; Thì mang_nguyen[0] sẽ là phần tử thứ nhất của mảng mang_nguyen[1] sẽ là phần tử thứ 2 của mảng mang_nguyen[2] sẽ là phần tử thứ 3 của mảng Với mảng nhiều chiều như int a[6][5]; thì a[0] là phần tử đầu tiên của một mảng, phần tử này bản thân nó lại là một mảng một chiều. Phần tử đầu tiên của mảng một chiều a[0] sẽ là a[0][0]. Phần tử tiếp theo của a[0] sẽ là a[0][1] Và dễ dàng tính được a[2][3] sẽ là phần tử thứ 4 của phần tử thứ 3 của a. Một cách tổng quát a[i][j] sẽ là phần tử thứ j+1 của a[i], mà phần tử a[i] lại là phần tử thứ i+1 của a. 5.1.2.1. Khai báo mảng 1 chiều Cú pháp Kiểu_mảng tên_mảng [ số_phần_tử]; Trong đó: - Kiểu_mảng: đây là kiểu của mảng, là tên một kiểu dữ liệu đã tồn tại, có thể là kiểu chuẩn hoặc kiểu dữ liệu do người lập trình định nghĩa . - tên_mảng : là tên của mảng, do người lập trình đặt, theo quy tắc về tên của C - số_phần_tử : là hằng (hoặc biểu thức hằng) nguyên, dương là số phần tử của mảng. Ví dụ: int vector [15]; // tên mảng: vector, có 15 phần tử, kiểu int float MT[10], D[20]; // có hai mảng kiểu float: MT có 10 phần tử, D có 20 phần tử char * s[30]; // s là mảng có 30 phần tử kiểu char * (mảng các con trỏ) Khi gặp (dòng lệnh) định nghĩa một mảng, chương trình dịch sẽ cấp phát một vùng nhớ (lên tiếp) cho đủ các phần tử liên tiếp của mảng, ví dụ vector[15] sẽ được cấp phát một vùng nhớ có kích thước 15*sizeof(int) =30 byte 5.1.2.2. Khai báo mảng 2 chiều Mảng hai chiều có thể hiểu như bảng gồm các dòng các cột, các phần tử thuộc cùng một kiểu dữ liệu nào đó. Mảng hai chiều được định nghĩa như sau. Cú pháp Kiểu_mảng tên_mảng [sd][sc]; Trong đó: - Kiểu_mảng: đây là kiểu của mảng, là tên một kiểu dữ liệu đã tồn tại, có thể là kiểu chuẩn hoặc kiểu dữ liệu do người lập trình định nghĩa. Trang 56
- - tên_mảng : là tên của mảng, do người lập trình đặt, theo quy tắc về tên của C. - sd, sc : là hằng (hoặc biểu thức hằng) nguyên, dương tương ứng là số dòng và số cột mảng, số phần tử của mảng sẽ là sd*sc. Ví dụ: int a[2][5]; // a là mảng số nguyên có 2 dòng, 5 cột (có 10 phần tử) float D[3][10]; // D là mảng số thực có 3 dòng, 10 cột (có 30 phần tử) char DS[5][30]; // DS là mảng kí tự có 5 dòng, 30 cột Khi gặp một định nghĩa mảng, chương trình dịch sẽ cấp phát một vùng nhớ liên tiếp có kích thước là sd*sc*sizeof (Kiểu_mảng) cho mảng. Có thể coi mảng 2 chiều n dòng, m cột là mảng 1 chiểu có n phần tử, mỗi phần tử lại là 1 mảng một chiều có m phần tử (mảng của mảng). Ví dụ với float D[3][10] có thể xem D là mảng có 3 phần tử D[0], D[1], D[2], mỗi phần tử này là mảng có 10 phần tử. 5.2. Một số bài toán với mảng 1 chiều 5.2.1. Nhập dữ liệu cho mảng Sau khi khai báo mảng ta phải nhập dữ liệu cho mảng. Nhập dữ liệu cho mảng là nhập dữ liệu cho từng phần tử của mảng. Mỗi một phần tử của mảng thực chất là một biến có kiểu dữ liệu là kiểu dữ liệu chung của mảng. Để nhập dữ liệu cho các phần tử của mảng ta có thể dùng hàm scanf() hoặc lệnh gán tương tự như biến thông thường. Ví dụ float a[10]; // khai bao mot mang so thuc co 10 phan tu int i; // Nhap tu ban phim mot so thuc va gan gia tri so thuc do // cho phan tu thu 2 cua mang, tuc la a[1] scanf(“%f”,&a[1]); // Gán giá trị cho phần tử a[2] a[2] = a[1] + 5; Nếu ta muốn gán giá trị cho các phần tử của mảng một cách hàng loạt, ta có thể dùng lệnh for. Ví dụ int b[10]; int i; // Nhap gia tri tu ban phim cho tat ca cac phan tu cua mang b for(i = 0; i < 10; i++) { printf(“\n Nhap gia tri cho b[%d]”, i); scanf(“%d”,&b[i]); } Trường hợp ta không biết trước mảng sẽ có bao nhiêu phần tử mà chỉ biết số phần tử tối đa có thể có của mảng. Còn số phần tử thực sự của mảng thì chỉ biết khi chạy chương trình. Khi đó cần khai báo mảng với số phần tử bằng số phần tử tối đa, ngoài ra cần một biến để lưu giữ số phần tử thực sự của mảng. int a[100]; // khai báo mảng, giả sử số phần tử tối đa của a là 100 Trang 57
- int n; // biến lưu giữ số phần tử thực sự của mảng int i; printf(“\n Cho biet so phan tu cua mang: “); scanf(“%d”,&n); for(i = 0; i #include void main() { int a[5]; int i, k; // Nhap gia tri cho cac phan tu cua mang a tu ban phim for(i = 0; i < 5; i++) { printf(“\n a[%d] = “, i); scanf(“%d”, &a[i]); } // Hien thi gia tri cua phan tu bat ki, gia su a[3] len man hinh printf(“\n a[3] = %d”, a[3]); // Hien thi gia tri cua tat ca cac phan tu, moi phan tu tren mot dong for(i = 0; i < 5; i++) printf(“\n%d”, a[i]); // Hien thi gia tri cua tat ca cac phan tu tren cung mot dong, cac gia tri cach nhau 2 vi tri printf(“\n”); // Xuong dong moi for(i = 0; i < 5; i++) printf(“%d “, a[i]); // Hien thi gia tri cua tat ca cac phan tu, trong do k phan tu tren mot dong. Trang 58
- // Cac phan tu tren cung dong cach nhau 2 vi tri printf(“\n Cho biet gia tri cua k = “); scanf(“%d”,&k); for(i = 0; i < 5; i++) { printf(“%d “,a[i]); if((i+1)%k == 0) // da hien thi du k phan tu tren mot dong thi phai xuong dong printf(“\n”); } getch(); } Kết quả a[0] = 6 a[1] = 14 a[2] = 23 a[3] = 37 a[4] = 9 a[3] = 37 6 14 23 37 9 61423379 Cho biet gia tri cua k = 2 6 14 23 37 9 5.2.3. Tìm giá trị lớn nhất/nhỏ nhất (Max/Min) Để tìm phần tử có giá trị lớn nhất trong mảng ban đầu ta giả sử phần tử đó là phần tử đầu tiên của mảng. Sau đó lần lượt so sánh với các phần tử còn lại trong mảng. Nếu gặp phần tử nhỏ hơn thì chuyển sang so sánh với phần tử tiếp theo. Nếu gặp phần tử lớn hơn thì ta sẽ coi phần tử này là phần tử lớn nhất rồi chuyển sang so sánh với phần tử tiếp theo. Sau khi so sánh với phần tử cuối cùng thì ta sẽ tìm được phần tử lớn nhất trong mảng. Đoạn chương trình sau minh họa giải thuật tìm phần tử lớn nhất int a[100]; int i, n; int max; printf("\n Cho biet so phan tu cua mang: "); Trang 59
- scanf("%d",&n); // Nhap du lieu cho mang for(i = 0; i < n; i++) { printf("\n a[%d] = ",i); scanf("%d",&a[i]); } // Tim phan tu lon nhat max = a[0]; // Ban dau gia su phan tu lon nhat la a[0] // Lan luot so sanh voi cac phan tu con lai trong mang for(i = 1; i < n; i++) if(max < a[i]) // gap phan tu co gia tri lon hon max = a[i]; // coi phan tu nay la phan tu lon nhat printf("\n Phan tu lon nhat trong mang la: %d", max); Ta cũng làm tương tự với trường hợp tìm phần tử nhỏ nhất trong mảng. 5.2.4. Sắp xếp mảng Yêu cầu của bài toán: cho một mảng dữ liệu m[n] với n là số phần tử trong mảng. Hãy sắp xếp các phần tử trong mảng theo một trật tự nào đó, giả sử là theo chiều tăng dần (với chiều giảm dần ta hoàn toàn có thể suy luận từ cách làm với chiều tăng dần). Sắp xếp kiểu lựa chọn (Selection sort): ý tưởng của phương pháp là như sau ta cần thực hiện n – 1 lượt sắp xếp, trong đó: - Ở lượt sắp xếp đầu tiên ta so sánh phần tử đầu tiên của mảng m[0] với tất cả các phần tử đứng sau nó trong mảng (tức là các phần tử m[1], m[2] m[n-1]). Nếu có giá trị m[i] nào đó (i = 1, 2, n–1) nhỏ hơn m[0] thì ta hoán đổi giá trị giữa m[0] và m[i] cho nhau. Rõ ràng sau lượt sắp xếp thứ nhất m[0] sẽ là phần tử có giá trị nhỏ nhất trong mảng. - Ở lượt sắp xếp thứ 2 ta so sánh phần tử thứ 2 của mảng m[1] với tất cả các phần tử đứng sau nó trong mảng (tức là các phần tử m[2], m[3] m[n-1]). Nếu có giá trị m[i] nào đó (i = 2, 3, n–1) nhỏ hơn m[1] thì ta hoán đổi giá trị giữa m[1] và m[i] cho nhau. Sau lượt sắp xếp thứ 2 thì m[1] sẽ là phần tử có giá trị nhỏ thứ 2 trong mảng. - Ở lượt sắp xếp thứ k ta so sánh phần tử thứ k của mảng là m[k-1] với tất cả các phần tử đứng sau nó trong mảng (tức là các phần tử m[k], m[k+1] m[n-1]). Nếu có giá trị m[i] nào đó (i = k, k+1, n–1) nhỏ hơn m[k] thì ta hoán đổi giá trị giữa m[k] và m[i] cho nhau. Sau lượt sắp xếp thứ k thì m[k-1] sẽ là phần tử có giá trị nhỏ thứ k trong mảng. - Ở lượt sắp xếp thứ n-1 ta so sánh phần tử thứ n-1 của mảng m[n-2] với tất cả các phần tử đứng sau nó trong mảng (tức là phần tử m[n-1]). Nếu m[n-1] nhỏ hơn m[n-2] thì ta hoán đổi giá trị giữa m[n-2] và m[n-1] cho nhau. Sau lượt sắp xếp thứ n-1 thì m[n-2] sẽ là phần tử có giá trị nhỏ thứ n-2 trong mảng. Và dĩ Trang 60
- nhiên phần tử còn lại là m[n-1] sẽ là phần tử nhỏ thứ n trong mảng (tức là phần tử lớn nhất trong mảng). Kết thúc n-1 lượt sắp xếp ta có các phần tử trong mảng đã được sắp xếp theo thứ tự tăng dần. Cài đặt giải thuật #include #include void main() { int m[100]; int n; // n la số phần tử trong mảng int i, j, k; clrscr(); // xóa màn hình để tiện theo dõi // Nhập giá trị dữ liệu cho mảng m // Trước tiên phải biết số phần tử của mảng printf(“ Cho biet so phan tu co trong mang: “); scanf(“%d”,&n); // Rồi lần lượt nhập giá trị cho các phần tử trong mảng for(i = 0;i<n;i++) { int temp; printf(“\n Cho biet gia tri cua m[%d] = “,i); scanf(“%d”,&temp); m[i] = temp; } // Hiển thị ra màn hình mảng vừa nhập vào printf(“\n Mang truoc khi sap xep: “); for(i=0;i<n;i++) printf(“%3d”,m[i]); // Bắt đầu sắp xếp for(i = 0; i<n-1;i++) { // Ở lượt sắp xếp thứ i+1 for(j = i+1;j<n;j++) { // So sánh m[i] với các phần tử còn lại // và đổi chỗ khi tìm thấy phần tử < m[i]. if(m[j]<m[i]) { int temp; temp = m[j]; m[j] = m[i]; m[i] = temp; } } Trang 61
- // Hiển thị mảng sau lượt sắp xếp thứ i+1 printf(“\n Mang o luot sap xep thu %d”,i+1); for(k = 0;k #include void main() { int m[100], chi_so[100]; int n; // n la số phần tử trong mảng Trang 62
- int i, k, kiem_tra; clrscr(); // xóa màn hình để tiện theo dõi // Nhập giá trị dữ liệu cho mảng m // Trước tiên phải biết số phần tử của mảng printf(“ Cho biet so phan tu co trong mang: “); scanf(“%d”,&n); // Rồi lần lượt nhập giá trị cho các phần tử trong mảng for(i = 0;i 0) { printf(“\n Trong mang co %d phan tu co gia tri bang %d”,kiem_tra,k); printf(“\n Chi so cua cac phan tu la: “); for(i = 0;i < kiem_tra;i++) printf(“%3d”,chi_so[i]); } else printf(“\n Trong mang khong co phan tu nao co gia tri bang %d”,k); getch(); // Chờ người sử dụng ấn phím bất kì để kết thúc. } 5.3. Một số bài toán với mảng 2 chiều 5.3.1. Nhập dữ liệu cho mảng 2 chiều for (i = 0; i < 5; i++) //vòng for có giá trị i chạy từ 0 đến 4 cho hàng Trang 63
- for (ij = 0; ij 3#include 4#define MAX 20 5//Khai bao prototype 6void input(float); 7void output(float); 8void add(float, float, float); 9float max(float); 10//khai bao bien toan cuc 11 int in; 12 //ham tim so lon nhat trong mang 2 chieu 13 float max(float fa[][MAX]) 14 { 15 float fmax; Trang 64
- 16 fmax = fa[0][0]; //cho phan tu dau tien la max 17 for (int i = 0; i max 20 fmax = fa[i][ij]; //gan so nay cho max 21 return fmax; //tra ve ket qua so lon nhat 22 } 23 //ham nhap lieu mang 2 chieu 24 void input(float fa[][MAX]) 25 { 26 for (int i = 0; i < in; i++) 27 for (int ij = 0; ij < in; ij++) 28 { 29 printf("Nhap vao ptu[%d][%d]: ", i, ij); 30 scanf("%f", &fa[i, j]); 31 } 32 } 33 //ham in mang 2 chieu ra man hinh 34 void output(float fa[][MAX]) 35 { 36 for (int i = 0; i < in; i++) 37 { 38 for (int ij = 0; ij < n; ij++) 39 printf("%5.2f", fa[i][ij]); 40 printf("\n"); 41 } 42 } 43 //ham cong 2 mang 2 chieu 44 void add(float fa[][MAX], float fb[][MAX], float fc[][MAX]) 45 { 46 for (int i = 0; i < in; i++) 47 for (int ij = 0; ij < in; ij++) 48 fc[i, ij] = fa[i, ij] + fb[i, ij]; 49 } 50 void main(void) 51 { 52 float fa[MAX][MAX], fb[MAX][MAX], fc[MAX][MAX]; 53 printf("Nhap vao cap ma tran: "); 54 scanf("%d", &in); 55 printf("Nhap lieu ma tran a: \n"); 56 input(fa); 57 printf("Nhap lieu ma tran b: \n"); Trang 65
- 58 input(fb); 59 printf("Nhap lieu ma tran c: \n"); 60 input(fc); 61 add(fa, fb, fc); 62 printf("Ma tran a: \n"); 63 output(fa); 64 printf("Ma tran b: \n"); 65 output(fb); 66 printf("Ma tran c: \n"); 67 output(fc); 68 printf("So lon nhat cua ma tran c la: %5.2f.\n", max(fc)); 69 getch(); 70 } F1 Help Alt-F8 Next Msg Alt-F7 Prev Msg Alt - F9 Compile F9 Make F10 Menu Kết quả in ra màn hình Nhap vao cap ma tran : 2 Ma tran a: Nhap lieu ma tran a: 5.20 4.00 Nhap vao ptu[0][0] : 5.2 7.10 9.00 Nhap vao ptu[0][1] : 4 Ma tran b: Nhap vao ptu[1][0] : 7.1 12.00 3.40 Nhap vao ptu[1][1] : 9 9.60 11.00 Nhap lieu ma tran b: Ma tran c: Nhap vao ptu[0][0] : 12 17.20 7.40 Nhap vao ptu[0][1] : 3.4 16.70 20.00 Nhap vao ptu[1][0] : 9.6 So lon nhat cua ma tran c la: 20.00 Nhap vao ptu[1][1] : 11 Chạy lại chương trình và thử lại với số liệu khác. Viết thêm hàm tìm số nhỏ nhất. CHƯƠNG 6: XÂU KÝ TỰ (STRING) MỤC TIÊU CỦA BÀI Sau khi học xong bài này người học có khả năng Kiến thức - Trình bày khái niệm về xâu ký tự, cách biểu diễn ký tự, xâu ký tự - Cách lưu trữ xâu ký tự Kĩ năng - Thực hiện lập trình và các thao tác cơ bản với ký tự và xâu ký tự. - Phân biệt được ký tự và xâu ký tự Thái độ Vận dụng tích cực, linh hoạt các kiến thức đã học vào các bài học tiếp theo, áp dụng viết các chương trình cơ bản. Trang 66
- 6.1. Khái niệm và khai báo xâu 6.1.1. Khái niệm về xâu ký tự Xâu kí tự (string) là một dãy các kí tự viết liên tiếp nhau. Xâu rỗng: là xâu không gồm kí tự nào cả. Độ dài xâu là số kí tự có trong xâu. Biểu diễn xâu kí tự: xâu kí tự được biểu diễn bởi dãy các kí tự đặt trong cặp dấu ngoặc kép. Các kí tự nằm trong cặp dấu ngoặc kép là nội dung của xâu. Ví dụ: • “String” là một xâu kí tự gồm 6 kí tự: ‘S’, ‘t’, ‘r’, ‘i’, ‘n’, ‘g’ được viết liên tiếp nhau. • “Tin hoc” là một xâu kí tự gồm 7 kí tự: ‘T’, ‘i’, ‘n’, dấu cách (‘ ‘), ‘h’, ‘o’, và ‘c’. Lưu trữ dữ liệu kiểu xâu kí tự: các kí tự của xâu được lưu trữ kế tiếp nhau và kết thúc bằng kí tự kết thúc xâu (kí tự '\0' hay kí tự NUL, có số thứ tự 0 trong bảng mã ASCII). Nhờ có kí tự NUL mà người ta xác định được độ dài của xâu kí tự bằng cách đếm các kí tự có trong xâu đến khi gặp kí tự NUL (kí tự NUL không được tính vào độ dài xâu). Ví dụ xâu kí tự “Tin hoc” sẽ được lưu trữ như sau ‘T’ ‘i’ ‘n’ ‘ ‘ ‘h’ ‘o’ ‘c’ ‘\0’ Lưu ý: Xâu kí tự khác mảng kí tự ở chỗ xâu kí tự có kí tự kết thúc xâu (kí tự NUL hay ‘\0’) trong khi mảng kí tự không có kí tự kết thúc. Phân biệt giữa một kí tự và xâu kí tự có một kí tự: ví dụ ‘A’ là một kí tự, nó được lưu trữ trong 1 byte, còn “A” là xâu kí tự, nó được lưu trữ trong 2 bytes, trong đó byte đầu tiên lưu trữ kí tự ‘A’, byte thứ 2 lưu trữ kí tự kết thúc xâu (NUL). 6.1.2. Khai báo và sử dụng biến xâu a) Khai báo xâu ký tự Trong C, một xâu kí tự được khai báo với cú pháp như sau: char tên_xâu [số_kí_tự_tối_đa]; Trong đó số_kí_tự_tối_đa cho biết số lượng kí tự nhiều nhất có thể có trong xâu. Sau khi khai báo, biến xâu kí tự tên_xâu có thể được dùng để lưu trữ một xâu kí tự bất kì, miễn là độ dài xâu kí tự (số kí tự có trong xâu) đó không vượt quá giá trị số_kí_tự_tối_đa. Ví dụ char ho_va_ten[20]; Đây là khai báo của một biến xâu kí tự tên là ho_va_ten, biến này có thể có tối đa 20 kí tự. Lưu ý: Đôi khi ta vẫn có thể nhập một xâu có nhiều hơn 20 kí tự cho xâu ho_va_ten mà trình biên dịch C vẫn không báo lỗi, tuy nhiên cần tránh điều này vì khi chạy chương trình thì chương trình quản lí bộ nhớ của hệ điều hành sẽ bắt lỗi và buộc chương trình kết thúc. b) Truy nhập vào một phần tử của xâu Trang 67