VinhQuangGROUP

Bài 1 Những khái niệm cơ bản về ngôn ngữ C

Mục tiêu:

Kết thúc bài học này, bạn có thể:

Phân biệt sự khác nhau giữa Câu lệnh, Chương trình và Phần mềm

Biết được quá trình hình thành C

Nên dùng C khi nào và tại sao

Nắm được cấu trúc một chương trình C

Hiểu rõ khái niệm giải thuật (algorithms)

Vẽ lưu đồ (flowchart)

Liệt kê các ký hiệu dùng trong lưu đồ

Giới thiệu

Ngày nay, khoa học máy tính thâm nhập vào mọi lĩnh vực. Tự động hóa hiện đang là ngành chủ chốt điều hướng sự phát triển thế giới. Bất cứ ngành nghề nào cũng cần phải hiểu biết ít nhiều về Công nghệ Thông tin và lập trình nói chung. Cụ thể, C là một ngôn ngữ lập trình cấp cao mà mọi lập trình viên cần phải biết. Vì thế, trong giáo trình này, chúng ta sẽ nghiên cứu chi tiết cấu trúc ngôn ngữ C. Ðầu tiên chúng ta tìm hiểu sự khác nhau của những khái niệm: Lệnh (Command), Chương trình (Program) và Phần mềm (Software).

1.1 Ra lệnh cho máy tính làm việc

Khi một máy tính được khởi động, nó sẽ tự động thực thi một số tiến trình và xuất kết quả ra màn hình. Ðiều này diễn ra thế nào? Câu trả lời đơn giản là nhờ vào Hệ điều hành cài đặt bên trong máy tính. Hệ điều hành (operating system) được xem như phần mềm hệ thống. Phần mềm này khởi động máy tính và thiết lập các thông số ban đầu trước khi trao quyền cho người dùng. Để làm được điều này, hệ điều hành phải được cấu tạo từ một tập hợp các chương trình. Mọi chương trình đều cố gắng đưa ra lời giải cho một hay nhiều bài toán nào đóMọi chương trình cố gắng đưa ra giải pháp cho một hay nhiều vấn đề. Mỗi chương trình là tập hợp các câu lệnh giải quyết một bài toán cụ thể. Một nhóm lệnh tạo thành một chương trình và một nhóm các chương trình tạo thành một phần mềm.

Để rõ hơn, chúng ta hãy xem xét một thí dụ : Một người bạn đến nhà chúng ta chơi và được mời món sữa dâu. Anh ta thấy ngon miệng và muốn xin công thức làm. Chúng ta hướng dẫn cho anh ta làm như sau :

1.Lấy một ít sữa.

2.Đổ nước ép dâu vào.

3.Trộn hỗn hợp này và làm lạnh.

Bây giờ nếu bạn của chúng ta theo những chỉ dẫn này, họ cũng có thể tạo ra món sữa dâu tuyệt vời.

Chúng ta hãy phân tích chỉ thị (lệnh) ở trên

Lệnh đầu tiên : Lệnh này hoàn chỉnh chưa ? Nó có trả lời được câu hỏi lấy sữa 'ở đâu' ?.

Lệnh thứ hai : Một lần nữa, lệnh này không nói rõ nước ép dâu để 'ở đâu'.

May mắn là bạn của chúng ta đủ thông minh để hiểu được công thức pha chế nói trên, dù rằng còn nhiều điểm chưa rõ ràng. Do vậy nếu chúng ta muốn phổ biến cách làm, chúng ta cần bổ sung các bước như sau :

1.Rót một ly sữa vào máy trộn.

2.Đổ thêm vào một ít nước dâu ép.

3.Ðóng nắp máy trộn

4.Mở điện và bắt đầu trộn

5.Dừng máy trộn lại

6.Nếu đã trộn đều thì tắt máy, ngược lại thì trộn tiếp.

7.Khi đã trộn xong, rót hỗn hợp vào tô và đặt vào tủ lạnh.

8.Ðể lạnh một lúc rồi lấy ra dùng.

So sánh hai cách hướng dẫn nêu trên, hướng dẫn thứ hai chắc chắn hoàn chỉnh, rõ ràng hơn, ai cũng có thể đọc và hiểu được.

Tương tự, máy tính cũng xử lý dữ liệu dựa vào tập lệnh mà nó nhận được. Ðương nhiên các chỉ thị đưa cho máy vi tính cũng cần phải hoàn chỉnh và có ý nghĩa rõ ràng. Những chỉ thị này cần phải tuân thủ các quy tắc:

1.Tuần tự

2.Có giới hạn

3.Chính xác.

Mỗi chỉ thị trong tập chỉ thị được gọi là "câu lệnh" và tập các câu lệnh được gọi là "chương trình".

Chúng ta hãy xét trường hợp chương trình hướng dẫn máy tính cộng hai số.

Các lệnh trong chương trình có thể là :

1.Nhập số thứ nhất và nhớ nó.

2.Nhập số thứ hai và nhớ nó.

3.Thực hiện phép cộng giữa số thứ nhất và số thứ hai, nhớ kết quả phép cộng.

4.Hiển thị kết quả.

5.Kết thúc.

Tập lệnh trên tuân thủ tất cả các quy tắc đã đề cập. Vì vậy, tập lệnh này là một chương trình và nó sẽ thực hiện thành công việc cộng hai số trên máy tính.

Ghi chú: Khả năng nhớ của con người được biết đến như là trí nhớ, khả năng nhớ dữ liệu được đưa vào máy tính được gọi là "bộ nhớ". Máy tính nhận dữ liệu tại một thời điểm và làm việc với dữ liệu đó vào thời điểm khác, nghĩa là máy tính ghi dữ liệu vào trong bộ nhớ rồi sau đó đọc ra để truy xuất các giá trị dữ liệu và làm việc với chúng.

Khi khối lượng công việc giao cho máy tính ngày càng nên nhiều và phức tạp thì tất cả các câu lệnh không thể được đưa vào một chương trình, chúng cần được chia ra thành một số chương trình nhỏ hơn. Tất cả các chương trình này cuối cùng được tích hợp lại để chúng có thể làm việc với nhau. Một tập hợp các chương trình như thế được gọi là phần mềm.

Mối quan hệ giữa ba khái niệm câu lệnh, chương trình và phần mềm có thể được biểu diễn bằng sơ đồ trong hình 1.1:

Hình 1.1: Phần mềm, chương trình và câu lệnh

1.2 Ngôn ngữ C

Vào đầu những năm 70 tại phòng thí nghiệm Bell, Dennis Ritchie đã phát triển ngôn ngữ C. C được sử dụng lần đầu trên một hệ thống cài đặt hệ điều hành UNIX. C có nguồn gốc từ ngôn ngữ BCPL do Martin Richards phát triển. BCPL sau đó đã được Ken Thompson phát triển thành ngôn ngữ B, đây là người khởi thủy ra C.

Trong khi BCPL và B không hỗ trợ kiểu dữ liệu, thì C đã có nhiều kiểu dữ liệu khác nhau. Những kiểu dữ liệu chính gồm : kiểu ký tự (character), kiểu số nguyên (interger) và kiểu số thực (float).

C liên kết chặt chẽ với hệ thống UNIX nhưng không bị trói buộc vào bất cứ một máy tính hay hệ điều hành nào. C rất hiệu quả để viết các chương trình thuộc nhiều những lĩnh vực khác nhau.

C cũng được dùng để lập trình hệ thống. Một chương trình hệ thống có ý nghĩa liên quan đến hệ điều hành của máy tính hay những tiện ích hỗ trợ nó. Hệ điều hành (OS), trình thông dịch (Interpreters), trình soạn thảo (Editors), chương trình Hợp Ngữ (Assembly) là các chương trình hệ thống. Hệ điều hành UNIX được phát triển dựa vào C. C đang được sử dụng rộng rãi bởi vì tính hiệu quả và linh hoạt. Trình biên dịch (compiler) C có sẵn cho hầu hết các máy tính. Mã lệnh viết bằng C trên máy này có thể được biên dịch và chạy trên máy khác chỉ cần thay đổi rất ít hoặc không thay đổi gì cả. Trình biên dịch C dịch nhanh và cho ra mã đối tượng không lỗi.

C khi thực thi cũng rất nhanh như hợp ngữ (Assembly). Lập trình viên có thể tạo ra và bảo trì thư viện hàm mà chúng sẽ được tái sử dụng cho chương trình khác. Do đó, những dự án lớn có thể được quản lý dễ dàng mà tốn rất ít công sức.

1.2.1 C - Ngôn ngữ bậc trung

C được hiểu là ngôn ngữ bậc trung bởi vì nó kết hợp những yếu tố của những ngôn ngữ cấp cao và những chức năng của hợp ngữ (ngôn ngữ cấp thấp). C cho phép thao tác trên những thành phần cơ bản của máy tính như bits, bytes, địa chỉ.... Hơn nữa, mã C rất dễ di chuyển nghĩa là phần mềm viết cho loại máy tính này có thể chạy trên một loại máy tính khác. Mặc dù C có năm kiểu dữ liệu cơ bản, nhưng nó không được xem ngang hàng với ngôn ngữ cao cấp về mặt kiểu dữ liệu. C cho phép chuyển kiểu dữ liệu. Nó cho phép thao tác trực tiếp trên bits, bytes, word và con trỏ (pointer). Vì vậy, nó được dùng cho lập trình mức hệ thống.

1.2.2 C - Ngôn ngữ cấu trúc

Thuật ngữ ngôn ngữ cấu trúc khối (block-structured language) không áp dụng với C. Ngôn ngữ cấu trúc khối cho phép thủ tục (procedures) hay hàm (functions) được khai báo bên trong các thủ tục và hàm khác. C không cho phép việc tạo hàm trong hàm nên nó không phải là ngôn ngữ cấu trúc khối. Tuy nhiên, nó được xem là ngôn ngữ cấu trúc vì nó có nhiều điểm giống với ngôn ngữ cấu trúc ALGOL, Pascal và một số ngôn ngữ tương tự khác.

C cho phép có sự tổng hợp của mã lệnh và dữ liệu. Ðiều này là một đặc điểm riêng biệt của ngôn ngữ cấu trúc. Nó liên quan đến khả năng tập hợp cũng như ẩn dấu tất cả thông tin và các lệnh khỏi phần còn lại của chương trình để dùng cho những tác vụ riêng biệt. Ðiều này có thể thực hiện qua việc dùng các hàm hay các khối mã lệnh (Code Block). Các hàm được dùng để định nghĩa hay tách rời những tác vụ được yêu cầu trong chương trình. Ðiều này cho phép những chương trình hoạt động như trong một đơn vị thống nhất. Khối mã lệnh là một nhóm các câu lệnh chương trình được nối kết với nhau theo một trật tự logic nào đó và cũng được xem như một đơn vị thống nhất. Một khối mã lệnh được tạo bởi một tập hợp nhiều câu lệnh tuần tự giữa dấu ngoặc mở và đóng xoắn như dưới đây:

do

{

i = i + 1;

.

.

.

} while (i < 40);

Ngôn ngữ cấu trúc hỗ trợ nhiều cấu trúc dùng cho vòng lặp (loop) như là while, do-while, và for. Những cấu trúc lặp này giúp lập trình viên điều khiển hướng thực thi trong chương trình.

1.3 Cấu trúc chương trình C

C có một số từ khóa, chính xác là 32. Những từ khóa này kết hợp với cú pháp của C hình thành ngôn ngữ C. Nhưng nhiều trình biên dịch cho C đã thêm vào những từ khóa dùng cho việc tổ chức bộ nhớ ở những giai đoạn tiền xử lý nhất định.

Vài quy tắc khi lập trình C như sau :

- Tất cả từ khóa là chữ thường (không in hoa)

- Ðoạn mã trong chương trình C có phân biệt chữ thường và chữ hoa. Ví dụ : do while thì khác với DO WHILE

- Từ khóa không thể dùng cho các mục đích khác như đặt tên biến (variable name) hoặc tên hàm (function name)

- Hàm main() luôn là hàm đầu tiên được gọi đến khi một chương trình bắt đầu chạy (chúng ta sẽ xem xét kỹ hơn ở phần sau)

Xem xét đoạn mã chương trình:

main ()

{

/* This is a sample program */

int i = 0;

i = i + 1;

.

.

}

Ghi chú: Những khía cạnh khác nhau của chương trình C được xem xét qua đoạn mã trên. Ðoạn mã này xem như là đoạn mã mẫu, nó sẽ được dùng lại trong suốt phần còn lại của giáo trình này.

1.3.1 Ðịnh nghĩa Hàm

Chương trình C được chia thành từng đơn vị gọi là hàm. Ðoạn mã mẫu chỉ có duy nhất một hàm main(). Hệ điều hành luôn trao quyền điều khiển cho hàm main() khi một chương trình C được thực thi. Tên hàm luôn được theo sau là cặp dấu ngoặc đơn (). Trong dấu ngoặc đơn có thể có hay không có những tham số (parameters).

1.3.2 Dấu phân cách (Delimiters)

Sau định nghĩa hàm sẽ là dấu ngoặc xoắn mở {. Nó thông báo điểm bắt đầu của hàm. Tương tự, dấu ngoặc xoắn đóng } sau câu lệnh cuối trong hàm chỉ ra điểm kết thúc của hàm. Dấu ngoặc xoắn mở đánh dấu điểm bắt đầu của một khối mã lệnh, dấu ngoặc xoắn đóng đánh dấu điểm kết thúc của khối mã lệnh đó. Trong đoạn mã mẫu có 2 câu lệnh giữa 2 dấu ngoặc xoắn.

Hơn nữa, đối với hàm, dấu ngoặc xoắn cũng dùng để phân định những đoạn mã trong trường hợp dùng cho cấu trúc vòng lặp và lệnh điều kiện..

1.3.3 Dấu kết thúc câu lệnh (Terminator)

Dòng int i = 0; trong đoạn mã mẫu là một câu lệnh (statement). Một câu lệnh trong C thì được kết thúc bằng dấu chấm phẩy (;). C không hiểu việc xuống dòng dùng phím Enter, khoảng trắng dùng phím spacebar hay một khoảng cách do dùng phím tab. Có thể có nhiều hơn một câu lệnh trên cùng một hàng nhưng mỗi câu lệnh phải được kết thúc bằng dấu chấm phẩy. Một câu lệnh không được kết thúc bằng dấu chấm phẩy được xem như một câu lệnh sai.

1.3.4 Dòng chú thích (Comment)

Những chú thích thường được viết để mô tả công việc của một lệnh đặc biệt, một hàm hay toàn bộ chương trình. Trình biên dịch sẽ không dịch chúng. Trong C, chú thích bắt đầu bằng ký hiệu /* và kết thúc bằng */. Trường hợp chú thích có nhiều dòng, ta phải chú ý ký hiệu kết thúc (*/), nếu thiếu ký hiệu này, toàn bộ chương trình sẽ bị coi như là một chú thích. Trong đoạn mã mẫu dòng chữ "This is a sample program" là dòng chú thích. Trong trường hợp chú thích chỉ trên một dòng ta có thể dùng //. Ví dụ:

int a = 0; // Biến 'a' đã được khai báo như là một kiểu số nguyên (interger)

1.3.5 Thư viện C (Library)

Tất cả trình biên dịch C chứa một thư viện hàm chuẩn dùng cho những tác vụ chung. Một vài bộ cài đặt C đặt thư viện trong một tập tin (file) lớn trong khi đa số còn lại chứa nó trong nhiều tập tin nhỏ. Khi lập trình, những hàm được chứa trong thư viện có thể được dùng cho nhiều loại tác vụ khác nhau. Một hàm (được viết bởi một lập trình viên) có thể được đặt trong thư viện và được dùng bởi nhiều chương trình khi được yêu cầu. Vài trình biên dịch cho phép hàm được thêm vào thư viện chuẩn trong khi số khác lại yêu cầu tạo một thư viện riêng.

1.4 Biên dịch và thực thi một chương trình (Compiling and Running)

Những bước khác nhau của việc dịch một chương trình C từ mã nguồn thành mã thực thi được thực hiện như sau :

Soạn thảo/Xử lý từ

Ta dùng một trình xử lý từ (word processor) hay trình soạn thảo (editor) để viết mã nguồn (source code). C chỉ chấp nhận loại mã nguồn viết dưới dạng tập tin văn bản chuẩn. Vài trình biên dịch (compiler) cung cấp môi trường lập trình (xem phụ lục) gồm trình soạn thảo.

Mã nguồn

Ðây là đoạn văn bản của chương trình mà người dùng có thể đọc. Nó là đầu vào của trình biên dịch C.

Bộ tiền xử lý C

Từ mã nguồn, bước đầu tiên là chuyển nó qua bộ tiền xử lý của C. Bộ tiền xử lý này sẽ xem xét những câu lệnh bắt đầu bằng dấu #. Những câu lệnh này gọi là các chỉ thị tiền biên dịch (directives). Điều này sẽ được giải thích sau. Chỉ thị tiền biên dịch thường được đặt nơi bắt đầu chương trình mặc dù nó có thể được đặt bất cứ nơi nào khác. Chỉ thị tiền biên dịch là những tên ngắn gọn được gán cho một tập mã lệnh.

Mã nguồn mở rộng C

Bộ tiền xử lý của C khai triển các chỉ thị tiền biên dịch và đưa ra kết quả. Ðây gọi là mã nguồn C mở rộng, sau đó nó được chuyển cho trình biên dịch C.

Trình biên dịch C (Compiler)

Trình biên dịch C dịch mã nguồn mở rộng thành ngôn ngữ máy để máy tính hiểu được.

Nếu chương trình quá lớn nó có thể được chia thành những tập tin riêng biệt và mỗi tập tin có thể được biên dịch riêng rẽ. Ðiều này giúp ích khi mà một tập tin bị thay đổi, toàn chương trình không phải biên dịch lại.

Bộ liên kết (Linker)

Mã đối tượng cùng với những thủ tục hỗ trợ trong thư viện chuẩn và những hàm được dịch riêng lẻ khác kết nối lại bởi Bộ liên kết để cho ra mã có thể thực thi được.

Bộ nạp (Loader)

Mã thực thi được thi hành bởi bộ nạp của hệ thống.

Tiến trình trên được mô tả qua lưu đồ 1.2 sau :

Hình 1.2: Biên dịch và thực thi một chương trình

1.5 Các bước lập trình giải quyết vấn đề

Chúng ta thường gặp phải những bài toán. Để giải quyết những bài toán đó, chúng ta cần hiểu chúng trước rồi sau đó mới hoạch định các bước cần làm .

Giả sử chúng ta muốn đi từ phòng học đến quán ăn tự phục vụ ở tầng hầm. Ðể thực hiện việc này chúng ta cần hiểu nó rồi tìm ra các bước giải quyết trước khi thực thi các bước đó:

BƯỚC 1 : Rời phòng

BƯỚC 2 : Ðến cầu thang

BƯỚC 3 : Xuống tầng hầm

BƯỚC 4 : Ði tiếp đến quán ăn tự phục vụ

Thủ tục trên liệt kê tập hợp các bước thực hiện được xác định rõ ràng cho việc giải quyết vấn đề. Một tập hợp các bước như vậy gọi là giải thuật (Algorithm hay gọi vắn tắt là algo ).

Một giải thuật (còn gọi là thuật toán) có thể được định nghĩa như là một thủ tục, công thức hay cách giải quyết vấn đề. Nó gồm một tập hợp các bước giúp đạt được lời giải.

Qua phần trên, chúng ta thấy rõ ràng để giải quyết được một bài toán, trước tiên ta phải hiểu bài toán đó, kế đến chúng ta cần tập hợp tất cả những thông tin liên quan tới nó. Bước kế sẽ là xử lý những mẩu thông tin đó. Cuối cùng, chúng ta cho ra lời giải của bài toán đó.

Giải thuật chúng ta có là một tập hợp các bước được liệt kê dưới dạng ngôn ngữ đơn giản. Rất có thể rằng các bước trên do hai người khác nhau viết vẫn tương tự nhau nhưng ngôn ngữ dùng diễn tả các bước có thể khác nhau. Do đó, cần thiết có những phương pháp chuẩn mực cho việc viết giải thuật để mọi người dễ dàng hiểu nó. Chính vì vậy , giải thuật được viết bằng cách dùng hai phương pháp chuẩn là mã giả (pseudo code) và lưu đồ (flowchart).

Cả hai phương pháp này đều dùng để xác định một tập hợp các bước cần được thi hành để có được lời giải. Liên hệ tới vấn đề đi đến quán ăn tự phục vụ trên, chúng ta đã vạch ra một kế hoạch (thuật toán) để đến đích. Tuy nhiên, để đến nơi, chúng ta phải cần thi hành những bước này thật sự. Tương tự, mã giả và lưu đồ chỉ đưa ra những bước cần làm. Lập trình viên phải viết mã cho việc thực thi những bước này qua việc dùng một ngôn ngữ nào đó.

Chi tiết về về mã giả và lưu đồ được trình bày dưới đây.

1.5.1 Mã giả (pseudo code)

Nhớ rằng mã giả không phải là mã thật. Mã giả sử dụng một tập hợp những từ tương tự như mã thật nhưng nó không thể được biên dịch và thực thi như mã thật.

Chúng ta hãy xem xét mã giả qua ví dụ sau.Ví dụ này sẽ hiển thị câu 'Hello World!'.

Ví dụ 1:

BEGIN

DISPLAY 'Hello World!'

END

Qua ví dụ trên, mỗi đoạn mã giả phải bắt đầu với từ BEGIN hoặc START, và kết thúc với từ END hay STOP. Ðể hiển thị giá trị nào đó, từ DISPLAY hoặc WRITE được dùng. Khi giá trị được hiển thị là một giá trị hằng (không đổi), trong trường hợp này là (Hello World), nó được đặt bên trong dấu nháy. Tương tự, để nhận một giá trị của người dùng, từ INPUT hay READ được dùng.

Ðể hiểu điều này rõ hơn, chúng ta xem xét ví dụ 2, ở ví dụ này ta sẽ nhập hai số và máy sẽ hiển thị tổng của hai số.

Ví dụ 2:

BEGIN

INPUT A, B

DISPLAY A + B

END

Trong đoạn mã giả này, người dùng nhập vào hai giá trị, hai giá trị này được lưu trong bộ nhớ và có thể được truy xuất như là A và B theo thứ tự. Những vị trí được đặt tên như vậy trong bộ nhớ gọi là biến. Chi tiết về biến sẽ được giải thích trong phần sau của chương này. Bước kế tiếp trong đoạn mã giả sẽ hiển thị tổng của hai giá trị trong biến A và B.

Tuy nhiên, cũng đoạn mã trên, ta có thể bổ sung để lưu tổng của hai biến trong một biến thứ ba rồi hiển thị giá trị biến này như trong ví dụ 3 sau đây.

Ví dụ 3:

BEGIN

INPUT A, B

C = A + B

DISPLAY C

END

Một tập hợp những chỉ thị hay các bước trong mã giả thì được gọi chung là một cấu trúc. Có ba loại cấu trúc : tuần tự, chọn lựa và lặp lại. Trong đoạn mã giả ta viết ở trên,chúng ta dùng cấu trúc tuần tự. Chúng được gọi như vậy vì những chỉ thị được thi hành tuần tự, cái này sau cái khác và bắt đầu từ điểm đầu tiên. Hai loại cấu trúc còn lại sẽ được đề cập trong những chương sau.

1.5.2 Lưu đồ (Flowcharts)

Một lưu đồ là một hình ảnh minh hoạ cho giải thuật. Nó vẽ ra biểu đồ của luồng chỉ thị hay những hoạt động trong một tiến trình. Mỗi hoạt động như vậy được biểu diễn qua những ký hiệu.

Ðể hiểu điều này rõ hơn, chúng ta xem lưu đồ trong hình 1.3 dùng hiển thị thông điệp truyền thống 'Hello World!'.

Hình 1.3: Lưu đồ

Lưu đồ giống với đoạn mã giả là cùng bắt đầu với từ BEGIN hoặc START, và kết thúc với từ END hay STOP. Tương tự, từ khóa DISPLAY được dùng để hiển thị giá trị nào đó đến người dùng. Tuy nhiên, ở đây, mọi từ khóa thì nằm trong những ký hiệu. Những ký hiệu khác nhau mang một ý nghĩa tương ứng được trình bày ở bảng trong Hình 1.4.

Hình 1.4: Ký hiệu trong lưu đồ

Ta hãy xét lưu đồ cho ví dụ 3 như ở Hình 1.5 dưới đây.

Hình 1.5: Lưu đồ cộng hai số

Tại bước mà giá trị của hai biến được cộng và gán cho biến thứ ba thì xem như là một xử lý và được trình bày bằng một hình chữ nhật.

Lưu đồ mà chúng ta xét ở đây là đơn giản.Thông thường, lưu đồ trải rộng trên nhiều trang giấy. Trong trường hợp như thế, biểu tượng bộ nối được dùng để chỉ điểm nối của hai phần trong một chương trình nằm ở hai trang kế tiếp nhau. Vòng tròn chỉ sự nối kết và phải chứa ký tự hoặc số như ở hình 1.6. Như thế, chúng ta có thể tạo liên kết giưa hai lưu đồ chưa hoàn chỉnh.

Hình 1.6: Bộ nối

Bởi vì lưu đồ được sử dụng để viết chương trình, chúng cần được trình bày sao cho mọi lập trình viên hiểu chúng dễ dàng. Nếu có ba lập trình viên dùng ba ngôn ngữ lập trình khác nhau để viết mã, bài toán họ cần giải quyết phải như nhau. Trong trường hợp này, mã giả đưa cho lập trình viên có thể giống nhau mặc dù ngôn ngữ lập trình họ dùng và tất nhiên là cú pháp có thể khác nhau. Nhưng kết quả cuối cùng là một. Do đó, cần thiết phải hiểu rõ bài toán và mã giả phải được viết cẩn thận. Chúng ta cũng kết luận rằng mã giả độc lập với ngôn ngữ lập trình.

Vài điểm cần thiết khác phải chú ý khi vẽ một lưu đồ :

Lúc đầu chỉ tập trung vào khía cạnh logic của bài toán và vẽ các luồng xử lý chính của lưu đồ

Một lưu đồ phải có duy nhất một điểm bắt đầu (START) và một điểm kết thúc (STOP).

Không cần thiết phải mô tả từng bước của chương trình trong lưu đồ mà chỉ cần các bước chính và có ý nghĩa cần thiết.

Chúng ta tuân theo những cấu trúc tuần tự, mà trong đó luồng thực thi chương trình đi qua tất cả các chỉ thị bắt đầu từ chỉ thị đầu tiên. Chúng ta có thể bắt gặp các điều kiện trong chương trình, dựa trên các điều kiện này hướng thực thi của chương trình có thể rẽ nhánh. Những cấu trúc cho việc rẽ nhánh như là cấu trúc chọn lựa, cấu trúc điều kiện hay rẽ nhánh. Những cấu trúc này được đề cập chi tiết sau đây:

Cấu trúc IF (Nếu)

Cấu trúc chọn lựa cơ bản là cấu trúc 'IF'. Ðể hiểu cấu trúc này chúng ta hãy xem xét ví dụ trong đó khách hàng được giảm giá nếu mua trên 100 đồng. Mỗi lần khách hàng trả tiền, một đoạn mã chương trình sẽ kiểm tra xem lượng tiền trả có quá 100 đồng không?. Nếu đúng thế thì sẽ giảm giá 10% của tổng số tiền trả, ngược lại thì không giảm giá.

Ðiều này được minh họa sơ lược qua mã giả như sau:

IF khách hàng mua trên 100 thì giảm giá 10%

Cấu trúc dùng ở đây là câu lệnh IF.

Hình thức chung cho câu lệnh IF (cấu trúc IF) như sau:

IF Điều kiện

Các câu lệnh Phần thân của cấu trúc IF

END IF

Một cấu trúc 'IF' bắt đầu là IF theo sau là điều kiện. Nếu điều kiện là đúng (thỏa điều kiện) thì quyền điều khiển sẽ được chuyển đến các câu lệnh trong phần thân để thực thi. Nếu điều kiện sai (không thỏa điều kiện), những câu lệnh ở phần thân không được thực thi và chương trình nhảy đến câu lệnh sau END IF (chấm dứt cấu trúc IF). Cấu trúc IF phải được kết thúc bằng END IF.

Chúng ta xem ví dụ 4 cho cấu trúc IF.

Ví dụ 4:

Yêu cầu: Kiểm xem một số là chẵn hay không và hiển thị thông điệp báo nếu đúng là số chẵn,

ta xử lý như sau :

BEGIN

INPUT num

r = num MOD 2

IF r=0

Display "Number is even"

END IF

END

Ðoạn mã trên nhập một số từ người dùng, thực hiện toán tử MOD (lấy phần dư) và kiểm tra xem phần dư có bằng 0 hay không. Nếu bằng 0 hiển thị thông điệp, ngược lại thoát ra.

Lưu đồ cho đoạn mã giả trên thể hiện qua hình 1.7.

Hình 1.7 : Kiểm tra số chẵn

Cú pháp của lệnh IF trong C như sau:

if (Điều kiện)

{

Câu lệnh

}

Cấu trúc IF...ELSE

Trong ví dụ 4, sẽ hay hơn nếu ta cho ra thông điệp báo rằng số đó không là số chẵn tức là số lẻ thay vì chỉ thoát ra. Ðể làm điều này ta có thể thêm câu lệnh IF khác để kiểm tra xem trường hợp số đó không chia hết cho 2. Ta xem ví dụ 5.

Example 5:

BEGIN

INPUT num

r = num MOD 2

IF r=0

DISPLAY "Even number"

END IF

IF r<>0

DISPLAY "Odd number"

END IF

END

Ngôn ngữ lập trình cung cấp cho chúng ta cấu trúc IF...ELSE. Dùng cấu trúc này sẽ hiệu quả và tốt hơn để giải quyết vấn đề. Cấu trúc IF ...ELSE giúp lập trình viên chỉ làm một phép so sánh và sau đó thực thi các bước tùy theo kết quả của phép so sánh là True (đúng) hay False (sai).

Cấu trúc chung của câu lệnh IF...ELSE như sau:

IF Điều kiện

Câu lệnh 1

ELSE

Câu lệnh 2

END IF

Cú pháp của cấu trúc if...else trong C như sau:

if(Điều kiện)

{

Câu lệnh 1

}

else

{

Câu lệnh 2

}

Nếu điều kiện thỏa (True), câu lệnh 1 được thực thi. Ngược lại, câu lệnh 2 được thực thi. Không bao giờ cả hai được thực thi cùng lúc. Vì vậy, đoạn mã tối ưu hơn cho ví dụ tìm số chẵn được viết ra như ví dụ 6.

Ví dụ 6:

BEGIN

INPUT num

r = num MOD 2

IF r = 0

DISPLAY "Even Number"

ELSE

DISPLAY "Odd Number"

END IF

END

Lưu đồ cho đoạn mã giả trên thể hiện qua Hình 1.8.

Hình 1.8: Số chẵn hay số lẻ

Ða điều kiện sử dụng AND/OR

Cấu trúc IF...ELSE làm giảm độ phức tạp, gia tăng tính hữu hiệu. Ở một mức độ nào đó, nó cũng nâng cao tính dễ đọc của mã. Các thí dụ IF chúng ta đã đề cập đến thời điểm này thì khá đơn giản. Chúng chỉ có một điều kiện trong IF để đánh giá. Thỉnh thoảng chúng ta phải kiểm tra cho hơn một điều kiện, thí dụ: Ðể xem xét nhà cung cấp có đạt MVS (nhà cung cấp quan trọng nhất) không?, một công ty sẽ kiểm tra xem nhà cung cấp đó đã có làm việc với công ty ít nhất 10 năm không? và đã có tổng doanh thu ít nhất 5,000,000 không?. Hai điều kiện thỏa mãn thì nhà cung cấp được xem như là một MVS. Do đó toán tử AND có thể được dùng trong câu lệnh 'IF' như trong ví dụ 7 sau:

Ví dụ 7:

BEGIN

INPUT yearsWithUs

INPUT bizDone

IF yearsWithUs >= 10 AND bizDone >=5000000

DISPLAY "Classified as an MVS"

ELSE

DISPLAY "A little more effort required!"

END IF

END

Ví dụ 7 cũng khá đơn giản, vì nó chỉ có 2 điều kiện. Ở tình huống thực tế, chúng ta có thể có nhiều điều kiện cần được kiểm tra. Nhưng chúng ta có thể dễ dàng dùng toán tử AND để nối những điều kiện lại giống như ta đã làm ở trên.

Bây giờ, giả sử công ty trong ví dụ trên đổi quy định, họ quyết định đưa ra điều kiện dễ dàng hơn. Như là : Hoặc làm việc với công ty trên 10 năm hoặc có doanh số (giá trị thương mại,giao dịch) từ 5,000,000 trở lên. Vì vâỵ, ta thay thế toán tử AND bằng toán tử OR. Nhớ rằng toán tử OR cho ra giá trị True (đúng) nếu chỉ cần một điều kiện là True.

Cấu trúc IF lồng nhau

Một cách khác để thực hiện ví dụ 7 là sử dụng cấu trúc IF lồng nhau. Cấu trúc IF lồng nhau là câu lệnh IF này nằm trong trong câu lệnh IF khác. Chúng ta viết lại ví dụ 7 sử dụng cấu trúc IF lồng nhau ở ví dụ 8 như sau:

Ví dụ 8:

BEGIN

INPUT yearsWithUs

INPUT bizDone

IF yearsWithUs >= 10

IF bizDone >=5000000

DISPLAY "Classified as an MVS"

ELSE

DISPLAY "A little more effort required!"

END IF

ELSE

DISPLAY "A little more effort required!"

END IF

END

Ðoạn mã trên thực hiện cùng nhiệm vụ nhưng không có 'AND'. Tuy nhiên, chúng ta có một lệnh IF (kiểm tra xem bizDone lớn hơn hoặc bằng 5,000,000 hay không?) bên trong lệnh IF khác (kiểm tra xem yearsWithUs lớn hơn hoặc bằng 10 hay không?). Câu lệnh IF đầu tiên kiểm tra điều kiện thời gian nhà cung cấp làm việc với công ty có lớn hơn 10 năm hay không. Nếu dưới 10 năm (kết quả trả về là False), nó sẽ không công nhận nhà cung cấp là một MVS; Nếu thỏa điều kiện nó xét câu lệnh IF thứ hai, nó sẽ kiểm tra tới điều kiện bizDone lớn hơn hoặc bằng 5,000,000 hay không. Nếu thỏa điều kiện (kết quả trả về là True) lúc đó nhà cung cấp được xem là một MVS, nếu không thì một thông điệp báo rằng đó không là một MVS.

Lưu đồ cho mã giả của ví dụ 8 được trình bày qua hình 1.9.

Hình 1.9: Câu lệnh IF lồng nhau

Mã giả trong trường hợp này của cấu trúc IF lồng nhau tại ví dụ 8 chưa hiệu quả. Câu lệnh thông báo không thỏa điều kiện MVS phải viết hai lần. Hơn nữa lập trình viên phải viết thêm mã nên trình biên dịch phải xét hai điều kiện của lệnh IF, do đó lãng phí thời gian. Ngược lại, nếu dùng toán tử AND chỉ xét tới điều kiện của câu lệnh IF một lần. Ðiều này không có nghĩa là cấu trúc IF lồng nhau nói chung là không hiệu quả. Nó tùy theo tình huống cụ thể mà ta dùng nó. Có khi dùng toán tử AND hiệu quả hơn, có khi dùng cấu trúc IF lồng nhau hiệu quả hơn. Chúng ta sẽ xét một ví dụ mà dùng cấu trúc IF lồng nhau hiệu quả hơn dùng toán tử AND.

Một công ty định phần lương cơ bản cho công nhân dựa trên tiêu chuẩn như trong bảng 1.1.

Grade

Experience

Salary

E

2

2000

E

3

3000

M

2

3000

M

3

4000

Bảng 1.1: Lương cơ bản

Vì vậy, nếu một công nhân được xếp loại là E và có hai năm kinh nghiệm thì lương là 2000, nếu ba năm kinh nghiệm thì lương là 3000.

Mã giả dùng toán tử AND cho vấn đề trên như ví dụ 9:

Ví dụ 9:

BEGIN

INPUT grade

INPUT exp

IF grade ="E" AND exp =2

salary=2000

ELSE

IF grade = "E" AND exp=3

salary=3000

END IF

END IF

IF grade ="M" AND exp =2

salary=3000

ELSE

IF grade = "M" AND exp=3

salary=4000

END IF

END IF

END

Câu lệnh IF đầu tiên kiểm tra xếp loại và kinh nghiệm của công nhân. Nếu xếp loại là E và kinh nghiệm là 2 năm thì lương là 2000, ngoài ra nếu xếp loại E, nhưng có 3 năm kinh nghiệm thì lương là 3000.

Nếu cả 2 điều kiện không thỏa thì câu lệnh IF thứ hai cũng tương tự sẽ kiểm điều kiện xếp loại và kinh nghiệm cho công nhân để phân định lương.

Giả sử xếp loại của một công nhân là E và có hai năm kinh nghiệm. Lương người đó sẽ được tính theo mệnh đề IF đầu tiên. Phần còn lại của câu lệnh IF thứ nhất được bỏ qua. Tuy nhiên, điều kiện tại mệnh đề IF thứ hai sẽ được xét và tất nhiên là không thỏa, do đó nó kiểm tra mệnh đề ELSE của câu lệnh IF thứ 2 và kết quả cũng là False. Ðây quả là những bước thừa mà chương trình đã xét qua. Trong ví dụ, ta chỉ có hai câu lệnh IF bởi vì ta chỉ xét có hai loại là E và M. Nếu có khoảng 15 loại thì sẽ tốn thời gian và tài nguyên máy tính cho việc tính toán thừa mặc dù lương đã xác định tại câu lệnh IF đầu tiên. Ðây dứt khoát không phải là mã nguồn hiệu quả.

Bây giờ chúng ta xét mã giả dùng cấu trúc IF lồng nhau đã được sửa đổi trong ví dụ 10.

Ví dụ 10:

BEGIN

INPUT grade

INPUT exp

IF grade="E"

IF exp=2

salary = 2000

ELSE

IF exp=3

salary=3000

END IF

END IF

ELSE

IF grade="M"

IF exp=2

Salary=3000

ELSE

IF exp=3

Salary=4000

END IF

END IF

END IF

END IF

END

Ðoạn mã trên nhìn khó đọc. Tuy nhiên, nó đem lại hiệu suất cao hơn. Chúng ta xét cùng ví dụ như trên. Nếu công nhân được xếp loại là E và kinh nghiệm là 2 năm thì lương được tính là 2000 ngay trong bước đầu của câu lệnh IF. Sau đó, chương trình sẽ thoát ra vì không cần thực thi thêm bất cứ lệnh ELSE nào. Do đó, không có sự lãng phí và đoạn mã này mang lại hiệu suất cho chương trình và chương trình chạy nhanh hơn.

Vòng lặp

Một chương trình máy tính là một tập các câu lệnh sẽ được thực hiện tuần tự. Nó có thể lặp lại một số bước với số lần lặp xác định theo yêu cầu của bài toán hoặc đến khi một số điều kiện nhất định được thỏa.

Chẳng hạn, ta muốn viết chương trình hiển thị tên của ta 5 lần. Ta xét mã giả dưới đây.

Ví dụ 11:

BEGIN

DISPLAY "Scooby"

DISPLAY "Scooby"

DISPLAY "Scooby"

DISPLAY "Scooby"

DISPLAY "Scooby"

END

Nếu để hiển thị tên ta 1000 lần, nếu ta viết DISPLAY "Scooby" 1000 lần thì rất tốn công sức. Ta có thể tinh giản vấn đề bằng cách viết câu lệnh DISPLAY chỉ một lần, sau đó đặt nó trong cấu trúc vòng lặp, và chỉ thị máy tính thực hiện lặp 1000 lần cho câu lệnh trên.

Ta xem mã giả của cấu trúc vòng lặp trong ví dụ 12 như sau:

Ví dụ 12:

Do loop 1000 times

DISPLAY "Scooby"

End loop

Những câu lệnh nằm giữa Do loop và End loop (trong ví dụ trên là lệnh DISPLAY) được thực thi 1000 lần. Những câu lệnh này cùng với các lệnh do loop và end loop được gọi là cấu trúc vòng lặp. Cấu trúc vòng lặp giúp lập trình viên phát triển thành những chương trình lớn trong đó có thể yêu cầu thực thi hàng ngàn câu lệnh. Do loop...end loop là một dạng thức tổng quát của vòng lặp.

Ví dụ sau là cách viết khác nhưng cũng dùng cấu trúc vòng lặp.

Ví dụ 13:

BEGIN

cnt=0

WHILE (cnt < 1000)

DO

DISPLAY "Scooby"

cnt=cnt+1

END DO

END

Lưu đồ cho mã giả trong ví dụ 13 được vẽ trong Hình 1.10.

Hình 1.10: Cấu trúc vòng lặp

Chú ý rằng Hình 1.10 không có ký hiệu đặc biệt nào để biểu diễn cho vòng lặp. Chúng ta dùng ký hiệu phân nhánh để kiểm tra điều kiện và quản lý hướng đi của của chương trình bằng các dòng chảy (flow_lines).

Tóm tắt bài học

Phần mềm là một tập hợp các chương trình.

Một chương trình là một tập hợp các chỉ thị (lệnh).

Những đoạn mã lệnh là cơ sở cho bất kỳ một chương trình C nào.

Ngôn ngữ C có 32 từ khóa.

Các bước cần thiết để giải quyết một bài toán là nghiên cứu chi tiết bài toán đó, thu thập thông tin thích hợp, xử lý thông tin và đi đến kết quả.

Một giải thuật là một danh sách rút gọn và logic các bước để giải quyết vấn đề. Giải thuật được viết bằng mã giả hoặc lưu đồ.

Mã giả là sự trình bày của giải thuật trong ngôn ngữ tương tự như mã thật

Một lưu đồ là sự trình bày dưới dạng biểu đồ của một giải thuật.

Lưu đồ có thể chia nhỏ thành nhiều phần và đầu nối dùng cho việc nối chúng lại tại nơi chúng bị chia cắt.

Một chương trình có thể gặp một điều kiện dựa theo đó việc thực thi có thể được phân theo các nhánh rẽ khác nhau. Cấu trúc lệnh như vậy gọi là cấu trúc chọn lựa, điều kiện hay cấu trúc rẽ nhánh.

Cấu trúc chọn cơ bản là cấu trúc "IF".

Cấu trúc IF ...ELSE giúp lập trình viên chỉ làm so sánh đơn và sau đó thực thi các bước tùy theo kết quả của phép so sánh là True (đúng) hay False (sai).

Cấu trúc IF lồng nhau là câu lệnh IF này nằm trong câu lệnh IF khác.

Thông thường ta cần lặp lại một số bước với số lần lặp xác định theo yêu cầu của bài toán hoặc đến khi một số điều kiện nhất định được thỏa. Những cấu trúc giúp làm việc này gọi là cấu trúc vòng lặp.

Kiểm tra tiến độ học tập

1.C cho phép ____________ của mã và dữ liệu.

2.Một là một sự trình bày dạng biểu đồ minh họa tính tuần tự của những hoạt động được thực thi nhằm đạt được một lời giải.

3.Lưu đồ giúp chúng ta xem xét lại và gỡ rối chương trình một cách dễ dàng. (True / False)

4.Một lưu đồ có thể có tuỳ ý số điểm bắt đầu và số điểm kết thúc. (True / False)

5.Một ____ cơ bản là việc thực thi tuần tự những câu lệnh đến khi một điều kiện cụ thể nào đó là đúng (True) hay sai (False).

Bài tập tự làm

1.Viết một đoạn mã giả và vẽ một lưu đồ để nhập một giá trị là độ 0C (Celsius) và chuyển nó sang độ 0F (Fahrenheit). [Hướng dẫn: C/5 = (F-32)/9]

2.Viết một đoạn mã giả và vẽ một lưu đồ để nhập điểm của một sinh viên cho các môn : Vật lý, Hóa học, và Sinh học. Sau đó hiển thị điểm trung bình và tổng của những điểm này.

Bài 2 Biến và Kiểu dữ liệu

Mục tiêu:

Kết thúc bài học này, bạn có thể:

Hiểu và sử dụng được biến (variables)

Phân biệt sự khác nhau giữa biến và hằng (constants)

Nắm vững và sử dụng các kiểu dữ liệu khác nhau trong chương trình C

Hiểu và sử dụng các toán tử số học.

Giới thiệu

Bất cứ chương trình ứng dụng nào cần xử lý dữ liệu cũng cần có nơi để lưu trữ tạm thời dữ liệu ấy. Nơi mà dữ liệu được lưu trữ gọi là bộ nhớ. Những vị trí khác nhau trong bộ nhớ có thể được xác định bởi các địa chỉ duy nhất. Những ngôn ngữ lập trình trước đây yêu cầu lập trình viên quản lý mỗi vị trí ô nhớ thông qua địa chỉ, cũng như giá trị lưu trong nó. Các lập trình viên dùng những địa chỉ này để truy cập hoặc thay đổi nội dung của các ô nhớ. Khi ngôn ngữ lập trình phát triển, việc truy cập hay thay đổi giá trị ô nhớ đã được đơn giản hoá nhờ sự ra đời của khái niệm biến .

2.1 Biến (variable)

Một chương trình ứng dụng có thể quản lý nhiều loại dữ liệu. Trong trường hợp này, chương trình phải chỉ định bộ nhớ cho mỗi đơn vị dữ liệu. Khi chỉ định bộ nhớ, có hai điểm cần lưu ý như sau :

1.Bao nhiêu bộ nhớ sẽ được gán

2.Mỗi đơn vị dữ liệu được lưu trữ ở đâu trong bộ nhớ.

Trước đây, các lập trình viên phải viết chương trình theo ngôn ngữ máy gồm các mã 1 và 0. Nếu muốn lưu trữ một giá trị tạm thời, vị trí chính xác nơi mà dữ liệu được lưu trữ trong bộ nhớ máy tính phải được chỉ định. Vị trí này là một con số cụ thể, gọi là địa chỉ bộ nhớ.

Các ngôn ngữ lập trình hiện đại cho phép chúng ta sử dụng các tên tượng trưng gọi là biến (variable), chỉ đến một vùng bộ nhớ nơi mà các giá trị cụ thể được lưu trữ.

Kiểu dữ liệu quyết định tổng số bộ nhớ được chỉ định. Những tên được gán cho biến giúp chúng ta sử dụng lại dữ liệu khi cần đến.

Chúng ta đã quen với cách sử dụng các ký tự đại diện trong một công thức. Ví dụ, diện tích hình chữ nhật được tính bởi :

Diện tích = A = chiều dài x chiều rộng = L x B

Cách tính lãi suất đơn giản được cho như sau:

Tiền lãi = I = Số tiền ban đầu x Thời gian x Tỷ lệ/100 = P x T x R /100

Các ký tự A, L, B, I, P, T, R là các biến và là các ký tự viết tắt đại diện cho các giá trị khác nhau.

Xem ví dụ sau đây :

Tính tổng điểm cho 5 sinh viên và hiển thị kết quả. Việc tính tổng được thực hiện theo hướng dẫn sau.

Hiển thị giá trị tổng của 24, 56, 72, 36 và 82

Khi giá trị tổng được hiển thị, giá trị này không còn được lưu trong bộ nhớ máy tính. Giả sử, nếu chúng ta muốn tính điểm trung bình, thì giá trị tổng đó phải được tính một lần nữa.

Tốt hơn là chúng ta sẽ lưu kết quả vào bộ nhớ máy tính, và sẽ lấy lại nó khi cần đến.

sum = 24 + 56 + 72 + 36 + 82

Ở đây, sum là biến được dùng để chứa tổng của 5 số. Khi cần tính điểm trung bình, có thể thực hiện như sau:

Avg = sum / 5

Trong C, tất cả biến cần phải được khai báo trước khi dùng chúng.

Chúng ta hãy xét ví dụ nhập hai số và hiển thị tổng của chúng trong ví dụ 1.

Ví dụ 1:

BEGIN

DISPLAY 'Enter 2 numbers'

INPUT A, B

C = A + B

DISPLAY C

END

A, B và C trong đoạn mã trên là các biến. Tên biến giúp chúng ta tránh phải nhớ địa chỉ của vị trí bộ nhớ. Khi đoạn mã được viết và thực thi, hệ điều hành đảm nhiệm việc cấp không gian nhớ còn trống cho những biến này. Hệ điều hành ánh xạ một tên biến đến một vị trí xác định trong bộ nhớ (ô nhớ). Và để tham chiếu tới một giá trị riêng biệt trong bộ nhớ, chúng ta chỉ cần chỉ ra tên của biến. Trong ví dụ trên, giá trị của hai biến được nhập từ người dùng và chúng được lưu trữ nơi nào đó trong bộ nhớ. Những vị trí này có thể được truy cập thông qua các tên biến A và B. Trong bước kế tiếp, giá trị của hai biến được cộng và kết quả được lưu trong biến thứ 3 là biến C. Cuối cùng, giá trị biến C được hiển thị.

Trong khi một vài ngôn ngữ lập trình cho phép hệ điều hành xóa nội dung trong ô nhớ và cấp phát bộ nhớ này để dùng lại thì những ngôn ngữ khác như C yêu cầu lập trình viên xóa vùng nhớ không sử dụng thông qua mã chương trình. Trong cả hai trường hợp, hệ điều hành đều lo việc cấp phát và thu hồi ô nhớ.

Hệ điều hành hoạt động như một giao diện giữa các ô nhớ và lập trình viên. Lập trình viên không cần lưu tâm về vị trí ô nhớ mà để cho hệ điều hành đảm nhiệm. Vậy việc điều khiển bộ nhớ (vị trí mà dữ liệu thích hợp lưu trữ) sẽ do hệ điều hành đảm trách, chứ không phải lập trình viên.

2.2Hằng (constant)

Trong trường hợp ta dùng biến, giá trị được lưu sẽ thay đổi. Một biến tồn tại từ lúc khai báo đến khi thoát khỏi phạm vi dùng nó. Những câu lệnh trong phạm vi khối mã này có thể truy cập giá trị của biến, và thậm chí có thể thay đổi giá trị của biến. Trong thực tế, đôi khi cần sử dụng một vài khoản mục mà giá trị của chúng không bao giờ bị thay đổi.

Một hằng là một giá trị không bao giờ bị thay đổi. Ví dụ, 5 là một hằng, mà giá trị toán học luôn là 5 và không thể bị thay đổi bởi bất cứ ai. Tương tự, 'Black' là một hằng, nó biểu thị cho màu đen. Khi đó, 5 được gọi là hằng số (numeric constant), 'Black' được gọi là hằng chuỗi (string constant).

2.3 Định danh (Identifier)

Tên của các biến (variables), các hàm (functions), các nhãn (labels) và các đối tượng khác nhau do người dùng định nghĩa gọi là định danh. Những định danh này có thể chứa một hay nhiều ký tự. Ký tự đầu tiên của định danh phải là một chữ cái hay một dấu gạch dưới ( _ ). Các ký tự tiếp theo có thể là các chữ cái, các con số hay dấu gạch dưới.

Arena, s_count, marks40, và class_one là những định danh đúng. Các ví dụ về các định danh sai là 1sttest, oh!god, và start... end.

Các định danh có thể có chiều dài tuỳ ý, nhưng số ký tự trong một biến được nhận diện bởi trình biên dịch thì thay đổi theo trình biên dịch. Ví dụ, nếu một trình biên dịch nhận diện 31 con số có ý nghĩa đầu tiên cho một tên định danh thì các câu sau sẽ hiển thị cùng một kết quả:

Đây là biến testing.... testing

Đây là biến testing.... testing ... testing

Các định danh trong C có phân biệt chữ hoa và chữ thường, cụ thể, arena thì khác ARENA.

2.3.1 Các nguyên tắc cho việc chỉ đặt tên

Các quy tắc đặt tên biến khác nhau tuỳ ngôn ngữ lập trình. Tuy nhiên, vài quy ước chuẩn được tuân theo như :

Tên biến phải bắt đầu bằng một ký tự chữ cái.

Các ký tự theo sau ký tự đầu bằng một chuỗi các chữ cái hoặc con số và cũng có thể bao gồm ký tự đặc biệt như dấu gạch dưới.

Tránh dùng ký tự O tại những vị trí mà có thể gây lầm lẫn với số không (0) và tương tự chữ cái l (chữ thường của chữ hoa L) có thể lầm lẫn với số 1.

Tên riêng nên tránh đặt tên cho biến.

Theo tiêu chuẩn C các chữ cái thường và hoa thì xem như khác nhau ví dụ. biến ADD, add và Add là khác nhau.

Việc phân biệt chữ hoa và chữ thường khác nhau tuỳ theo ngôn ngữ lập trình. Do đó, tốt nhất nên đặt tên cho biến theo cách thức chuẩn.

Tên một biến nên có ý nghĩa, gợi tả và mô tả rõ kiểu dữ liệu của nó. Ví dụ, nếu tìm tổng của hai số thì tên biến lưu trữ tổng nên đặt là sum (tổng). Nếu đặt tên là s hay ab12 thì không hay lắm.

2.3.2 Từ khóa (Keywords)

Tất cả các ngôn ngữ dành một số từ nhất định cho mục đích riêng. Những từ này có một ý nghĩa đặc biệt trong ngữ cảnh của từng ngôn ngữ, và được xem là "từ khóa". Khi đặt tên cho các biến, chúng ta cần bảo đảm rằng không dùng bất cứ từ khóa nào làm tên biến.

Tên kiểu dữ liệu tất cả được coi là từ khóa.

Do vậy, đặt tên cho một biến là int sẽ phát sinh một lỗi, nhưng đặt tên cho biến là integer thì không.

Vài ngôn ngữ lập trình yêu cầu lập trình viên chỉ ra tên của các biến cũng như kiểu dữ liệu của nó trước khi dùng biến đó thật sự. Bước này được gọi là khai báo biến. Ta sẽ nói rõ bước này trong phần tiếp theo khi thảo luận về các kiểu dữ liệu. Ðiều quan trọng cần nhớ bây giờ là bước này giúp hệ điều hành thật sự cấp phát một khoảng không gian vùng nhớ cho biến trước khi bắt đầu sử dụng nó.

2.4 Các kiểu dữ liệu (Data types)

Các loại dữ liệu khác nhau được lưu trữ trong biến là :

Số (Numbers)

Các số nguyên.

Ví dụ : 10 hay 178993455.

Các số thực.

Ví dụ : 15.22 hay 15463452.25.

Các số dương.

Các số âm.

Tên.

Ví dụ : John.

Giá trị luận lý.

Ví dụ : Y hay N.

Khi dữ liệu được lưu trữ trong các biến có kiểu dữ liệu khác nhau, nó yêu cầu dung lượng bộ nhớ sẽ khác nhau.

Dung lượng bộ nhớ được chỉ định cho một biến tùy thuộc vào kiểu dữ liệu của nó.

Ðể chỉ định bộ nhớ cho một đơn vị dữ liệu, chúng ta phải khai báo một biến với một kiểu dữ liệu cụ thể.

Khai báo một biến có nghĩa là một vùng nhớ nào đó đã được gán cho biến. Vùng bộ nhớ đó sau này sẽ được tham chiếu thông qua tên của biến. Dung lượng bộ nhớ được cấp cho biến bởi hệ điều hành phụ thuộc vào kiểu dữ liệu được lưu trữ trong biến. Vì vậy, một kiểu dữ liệu sẽ mô tả loại dữ liệu phù hợp với biến.

Dạng thức chung cho việc khai báo một biến:

Kiểu dữ liệu (Tên biến)

Kiểu dữ liệu thường được dùng trong các công cụ lập trình có thể được phân chia thành:

1Kiểu dữ liệu số - lưu trữ giá trị số.

2Kiểu dữ liệu ký tự - lưu trữ thông tin mô tả

Những kiểu dữ liệu này có thể có tên khác nhau trong các ngôn ngữ lập trình khác nhau. Ví dụ, một kiểu dữ liệu số được gọi trong C là int trong khi đó tại Visual Basic được gọi là integer. Tương tự, một kiểu dữ liệu ký tự được đặt tên là char trong C trong khi đó trong Visual Basic nó được đặt tên là string. Trong bất cứ trường hợp nào, các dữ liệu được lưu trữ luôn giống nhau. Ðiểm khác duy nhất là các biến được dùng trong một công cụ phải được khai báo theo tên của kiểu dữ liệu được hỗ trợ bởi chính công cụ đó.

C có 5 kiểu dữ liệu cơ bản. Tất cả những kiểu dữ liệu khác dựa vào một trong số những kiểu này. 5 kiểu dữ liệu đó là:

int là một số nguyên, về cơ bản nó biểu thị kích cỡ tự nhiên của các số nguyên (integers).

float và double được dùng cho các số có dấu chấm động. Kiểu float (số thực) chiếm 4 byte và có thể có tới 6 con số phần sau dấu thập phân, trong khi double chiếm 8 bytes và có thể có tới 10 con số phần thập phân.

char chiếm 1 byte và có khả năng lưu một ký tự đơn (character).

void được dùng điển hình để khai báo một hàm không trả về giá trị. Ðiều này sẽ được nói rõ hơn trong phần hàm.

Dung lượng nhớ và phạm vi giá trị của những kiểu này thay đổi theo mỗi loại bộ xử lý và việc cài đặt các trình biên dịch C khác nhau.

Lưu ý: Các con số dấu chấm động được dùng để biểu thị các giá trị cần có độ chính xác ở phần thập phân.

Kiểu dữ liệu int

Là kiểu dữ liệu lưu trữ dữ liệu số và là một trong những kiểu dữ liệu cơ bản trong bất cứ ngôn ngữ lập trình nào. Nó bao gồm một chuỗi của một hay nhiều con số.

Thí dụ trong C, để lưu trữ một giá trị số nguyên trong một biến tên là 'num', ta khai báo như sau:

int num;

Biến num không thể lưu trữ bất cứ kiểu dữ liệu nào như "Alan" hay "abc". Kiểu dữ liệu số này cho phép các số nguyên trong phạm vi -32768 tới 32767 được lưu trữ. Hệ điều hành cấp phát 16 bit (2 byte) cho một biến đã được khai báo kiếu int. Ví dụ: 12322, 0, -232.

Nếu chúng ta gán giá trị 12322 cho num thì biến này là biến kiểu số nguyên và 12322 là hằng số nguyên.

Kiểu dữ liệu số thực (float)

Một biến có kiểu dữ liệu số thực được dùng để lưu trữ các giá trị chứa phần thập phân. Trình biên dịch phân biệt các kiểu dữ liệu float và int.

Ðiểm khác nhau chính của chúng là kiểu dữ liệu int chỉ bao gồm các số nguyên, trong khi kiểu dữ liệu float có thể lưu giữ thêm cả các phân số.

Ví dụ, trong C, để lưu trữ một giá trị float trong một biến tên gọi là 'num', việc khai báo sẽ như sau :

float num;

Biến đã khai báo là kiểu dữ liệu float có thể lưu giá trị thập phân có độ chính xác tới 6 con số. Biến này được cấp phát 32 bit (4 byte) của bộ nhớ. Ví dụ: 23.05, 56.5, 32.

Nếu chúng ta gán giá trị 23.5 cho num, thì biến num là biến số thực và 23.5 là một hằng số thực.

Kiểu dữ liệu double

Kiểu dữ liệu double được dùng khi giá trị được lưu trữ vượt quá giới hạn về dung lượng của kiểu dữ liệu float. Biến có kiểu dữ liệu là double có thể lưu trữ nhiều hơn khoảng hai lần số các chữ số của kiểu float.

Số các chữ số chính xác mà kiểu dữ liệu float hoặc double có thể lưu trữ tùy thuộc vào hệ điều hành cụ thể của máy tính.

Các con số được lưu trữ trong kiểu dữ liệu float hay double được xem như nhau trong hệ thống tính toán. Tuy nhiên, sử dụng kiểu dữ liệu float tiết kiệm bộ nhớ một nửa so với kiểu dữ liệu double.

Kiểu dữ liệu double cho phép độ chính xác cao hơn (tới 10 con số). Một biến khai báo kiểu dữ liệu double chiếm 64 bit (8 byte) trong bộ nhớ.

Thí dụ trong C, để lưu trữ một giá trị double cho một biến tên 'num', khai báo sẽ như sau:

double num;

Nếu chúng ta gán giá trị 23.34232324 cho num, thì biến num là biến kiểu double và 23.34232324 là một hằng kiểu double.

Kiểu dữ liệu char

Kiểu dữ liệu char được dùng để lưu trữ một ký tự đơn.

Một kiểu dữ liệu char có thể lưu một ký tự đơn được bao đóng trong hai dấu nháy đơn (''). Thí dụ kiểu dữ liệu char như: 'a', 'm', '$' '%'.

Ta có thể lưu trữ những chữ số như­ những ký tự bằng cách bao chúng bên trong cặp dấu nháy đơn. Không nên nhầm lẫn chúng với những giá trị số. Ví dụ, '1', '5' và '9' sẽ không đư­ợc nhầm lẫn với những số 1, 5 và 9.

Xem xét những câu lệnh của mã C dưới đây:

char gender;

gender='M';

Hàng đầu tiên khai báo biến gender của kiểu dữ liệu char. Hàng thứ hai lưu giữ một giá trị khởi tạo cho nó là 'M'. Biến gender là một biến ký tự và 'M' là một hằng ký tự. Biến này đư­ợc cấp phát 8 bit (1 byte) trong bộ nhớ.

Kiểu dữ liệu void

C có một kiểu dữ liệu đặc biệt gọi là void. Kiểu dữ liệu này chỉ cho trình biên dịch C biết rằng không có dữ liệu của bất cứ kiểu nào. Trong C, các hàm số thường trả về dữ liệu thuộc một kiểu nào đó. Tuy nhiên, khi một hàm không có gì để trả về, kiểu dữ liệu void được sử dụng để chỉ ra điều này.

2.4.1 Những kiểu dữ liệu cơ bản và dẫn xuất

Bốn kiểu dữ liệu (char, int, float và double) mà chúng ta đã thảo luận ở trên đ­ược sử dụng cho việc trình bày dữ liệu thực sự trong bộ nhớ của máy tính. Những kiểu dữ liệu này có thể đư­ợc sửa đổi sao cho phù hợp với những tình huống khác nhau một cách chính xác. Kết quả, chúng ta có được các kiểu dữ liệu dẫn xuất từ những kiểu cơ bản này.

Một bổ từ (modifier) đ­ược sử dụng để thay đổi kiểu dữ liệu cơ bản nhằm phù hợp với các tình huống đa dạng. Ngoại trừ kiểu void, tất cả các kiểu dữ liệu khác có thể cho phép những bổ từ đứng trước chúng. Bổ từ được sử dụng với C là signed, unsigned, long và short. Tất cả chúng có thể đư­ợc áp dụng cho dữ liệu kiểu ký tự và kiểu số nguyên. Bổ từ long cũng có thể được áp dụng cho double.

Một vài bổ từ như :

1.unsigned

2.long

3.short

Ðể khai báo một biến kiểu dẫn xuất, chúng ta cần đặt trư­ớc khai báo biến thông thư­ờng một trong những từ khóa của bổ từ. Một giải thích chi tiết về các bổ từ này và cách thức sử dụng chúng đư­ợc trình bày bên d­ưới.

Các kiểu có dấu (signed) và không dấu(unsigned)

Khi khai báo một số nguyên, mặc định đó là một số nguyên có dấu. Tính quan trọng nhất của việc dùng signed là để bổ sung cho kiểu dữ liệu char, vì char là kiểu không dấu theo mặc định.

Kiểu unsigned chỉ rõ rằng một biến chỉ có thể có giá trị dư­ơng. Bổ từ này có thể đư­ợc sử dụng với kiểu dữ liệu int và kiểu dữ liệu float. Kiểu unsigned có thể áp dụng cho kiểu dữ liệu float trong vài trường hợp nhưng điều này giảm bớt tính khả chuyển (portability) của mã lệnh.

Với việc thêm từ unsigned vào trước kiểu dữ liệu int, miền giá trị cho những số dư­ơng có thể được tăng lên gấp đôi.

Ta xem những câu lệnh của mã C cung cấp ở bên dư­ới, nó khai báo một biến theo kiểu unsigned int và khởi tạo biến này có giá trị 23123.

unsigned int varNum;

varNum = 23123;

Chú ý rằng không gian cấp phát cho kiểu biến này vẫn giữ nguyên. Nghĩa là, biến varNum đư­ợc cấp phát 2 byte như khi nó dùng kiểu int. Tuy nhiên, những giá trị mà một kiểu unsgned int hỗ trợ sẽ nằm trong khoảng từ 0 đến 65535, thay vì là -32768 tới 32767 mà kiểu int hỗ trợ. Theo mặc định, int là một kiểu dữ liệu có dấu.

Các kiểu long và short

Chúng đư­ợc sử dụng khi một số nguyên có chiều dài ngắn hơn hoặc dài hơn chiều dài bình thường. Một bổ từ short đư­ợc áp dụng cho kiểu dữ liệu khi chiều dài yêu cầu ngắn hơn chiều dài số nguyên bình thường và một bổ từ long đư­ợc dùng khi chiều dài yêu cầu dài hơn chiều dài số nguyên bình thư­ờng.

Bổ từ short đư­ợc sử dụng với kiểu dữ liệu int. Nó sửa đổi kiểu dữ liệu int theo hướng chiếm ít vị trí bộ nhớ hơn. Bởi vậy, trong khi một biến kiểu int chiếm giữ 16 bit (2 byte) thì một biến kiểu short int (hoặc chỉ là short), chiếm giữ 8 bit (1 byte) và cho phép những số có trong phạm vi từ -128 tới 127.

Bổ từ long đư­ợc sử dụng tương ứng một miền giá trị rộng hơn. Nó có thể đ­ược sử dụng với int cũng như với­ kiểu dữ liệu double. Khi đ­ược sử dụng với kiểu dữ liệu int, biến chấp nhận những giá trị số trong khoảng từ -2,147,483,648 đến 2,147,483,647 và chiếm giữ 32 bit ( 4 byte). T­ương tự, kiểu long double của một biến chiếm giữ 128 bit (16 byte).

Một biến long int đư­ợc khai báo nh­ư sau:

long int varNum;

Nó cũng có thể được khai báo đơn giản như long varNum. Một số long integer có thể được khai báo như long int hay chỉ là long. Tương tự, ta có short int hay short.

Bảng dưới đây trình bày phạm vi giá trị cho các kiểu dữ liệu khác nhau và số bit nó chiếm giữ dựa theo tiêu chuẩn ANSI.

Kiểu

Dung lượng xấp xỉ (đơn vị là bit)

Phạm vi

char

8

-128 tới 127

unsigned

8

0 tới 255

signed char

8

-128 tới 127

16

-32,768 tới 32,767

unsigned int

16

0 tới 65,535

signed int

16

Giống như kiểu int

short int

16

-128 tới 127

unsigned short int

16

0 tới 65, 535

signed short int

16

Giống như kiểu short int

long int

32

-2,147,483,648 tới 2,147,483,647

signed long int

32

Giống như kiểu long int

unsigned long int

32

0 tới 4,294,967,295

float

32

6 con số thập phân

double

64

10 con số thập phân

long double

128

10 con số thập phân

Table 2.1: Các kiểu dữ liệu và phạm vi

Thí dụ sau trình bày cách khai báo những kiểu dữ liệu trên.

Ví dụ 2:

main()

{

char abc; /*abc of type character */

int xyz; /*xyz of type integer */

float length; /*length of type float */

double area; /* area of type double */

long liteyrs; /*liteyrs of type long int */

short arm; /*arm of type short integer*/

}

Chúng ta xem lại ví dụ cộng hai số và hiển thị tổng ở chương trước. Mã giả như sau :

Ví dụ 3:

BEGIN

INPUT A, B

C = A + B

DISPLAY C

END

Trong ví dụ này, các giá trị cho hai biến A và B được nhập. Các giá trị được cộng và tổng được lưu cho biến C bằng cách dùng câu lệnh C = A + B. Trong câu lệnh này, A và B là những biến và ký hiệu + gọi là toán tử. Chúng ta sẽ nói về toán tử số học của C ở phần sau đây. Tuy nhiên, có những loại toán tử khác trong C sẽ được bàn tới ở phần kế tiếp.

2.5 Các toán tử số học (Arithmetic Operators)

Những toán tử số học đư­ợc sử dụng để thực hiện những thao tác mang tính số học. Chúng đư­ợc chia thành hai lớp : Toán tử số học một ngôi (unary) và toán tử số học hai ngôi (binary).

Bảng 2.2 liệt kê những toán tử số học và chức năng của chúng.

Các toán tử một ngôi

Chức năng

Các toán tử hai ngôi

Chức năng

-

Lấy đối số

+

Cộng

++

Tăng một giá trị

-

Trừ

--

Giảm một giá trị

*

Nhân

%

Lấy phần dư

Chia

^

Lấy số mũ

Bảng 2.2: Các toán tử số học và chức năng

Các toán tử hai ngôi

Trong C, các toán tử hai ngôi có chức năng giống như trong các ngôn ngữ khác. Những toán tử như +, -, * và / có thể đư­ợc áp dụng cho hầu hết kiểu dữ liệu có sẵn trong C. Khi toán tử / được áp dụng cho một số nguyên hoặc ký tự, bất kỳ phần dư­ nào sẽ đư­ợc cắt bỏ. Ví dụ, 5/2 sẽ bằng 2 trong phép chia số nguyên. Toán tử % sẽ cho ra kết quả là số dư của phép chia số nguyên. Ví dụ: 5%2 sẽ có kết quả là 1. Tuy nhiên, % không thể đư­ợc sử dụng với những kiểu có dấu chấm động.

Chúng ta hãy xem xét một ví dụ của toán tử số mũ.

9^2

Ở đây 9 là cơ số và 2 là số mũ.

Số bên trái của '^' là cơ số và số bên phải '^' là số mũ.

Kết quả của 9^2 là 9*9 = 81.

Thêm ví dụ khác:

5 ^ 3

Có nghĩa là:

5 * 5 * 5

Do đó: 5 ^ 3 = 5 * 5 * 5 = 125.

Ghi chú: Những ngôn ngữ lập trình như Basic, hỗ trợ toán tử mũ. Tuy nhiên, ANSI C không hỗ trợ ký hiệu ^ cho phép tính lũy thừa. Ta có thể dùng cách khác tính lũy thừa trong C là dùng hàm pow() đã được định nghĩa trong math.h. Cú pháp của nó thể hiện qua ví dụ sau:

...

#include<math.h>

void main(void)

{

....

/* the following function will calculate x to the power y. */

z = pow(x, y);

....

}

Ví dụ sau trình bày tất cả toán tử hai ngôi được dùng trong C. Chú ý rằng ta chưa nói về hàm printf() và getchar(). Chúng ta sẽ bàn trong những phần sau.

Ví dụ 4:

#include<stdio.h>

main()

{

int x,y;

x = 5;

y = 2;

printf("The integers are : %d & %d

", x, y);

printf("The addition gives : %d

", x + y);

printf("The subtraction gives : %d

", x - y);

printf("The multiplication gives : %d

", x * y);

printf("The division gives : %d

", x / y);

printf("The modulus gives : %d

", x % y);

getchar();

}

Kết quả là:

The integers are : 5 & 2

The addition gives : 7

The subtraction gives : 3

The multiplication gives : 10

The division gives : 2

The modulus gives : 1

Các toán tử một ngôi (unary)

Các toán tử một ngôi là toán tử trừ một ngôi '-', toán tử tăng '++' và toán tử giảm '--'

Toán tử trừ một ngôi

Ký hiệu giống như phép trừ hai ngôi. Lấy đối số để chỉ ra hay thay đổi dấu đại số của một giá trị. Ví dụ:

a = -75;

b = -a;

Kết quả của việc gán trên là a được gán giá trị -75 và b được gán cho giá trị 75 (-(- 75)). Dấu trừ đ­ược sử dụng như thế gọi là toán tử một ngôi vì nó chỉ có một toán hạng.

Nói một cách chính xác, không có toán tử một ngôi + trong C. Vì vậy, một lệnh gán như.

invld_pls = +50;

khi mà invld_pls là một biến số nguyên là không hợp lệ trong chuẩn của C. Tuy nhiên, nhiều trình biên dịch không phản đối cách dùng như­ vậy.

Các toán tử Tăng và Giảm

C bao chứa hai toán tử hữu ích mà ta không tìm thấy được trong những ngôn ngữ máy tính khác. Chúng là ++ và --. Toán tử ++ thêm vào toán hạng của nó một đơn vị, trong khi toán tử -- giảm đi toán hạng của nó một đơn vị.

Cụ thể:

x = x + 1;

có thể đ­ược viết là:

x++;

và:

x = x - 1;

có thể được viết là:

x--;

Cả hai toán tử này có thể đứng trước hoặc sau toán hạng, chẳng hạn:

x = x + 1;

có thể được viết lại là

x++ hay ++x;

Và cũng tương tự cho toán tử --.

Sự khác nhau giữa việc xử lý trước hay sau trong toán tử một ngôi thật sự có ích khi nó đư­ợc dùng trong một biểu thức. Khi toán tử đứng trư­ớc toán hạng, C thực hiện việc tăng hoặc giảm giá trị tr­ước khi sử dụng giá trị của toán hạng. Ðây là tiền xử lý (pre-fixing). Nếu toán tử đi sau toán hạng, thì giá trị của toán hạng đ­ược sử dụng trư­ớc khi tăng hoặc giảm giá trị của nó. Ðây là hậu xử lý (post-fixing). Xem xét ví dụ sau :

a = 10;

b = 5;

c = a * b++;

Trong biểu thức trên, giá trị hiện thời của b đư­ợc sử dụng cho tính toán và sau đó giá trị của b sẽ tăng sau. Tức là, c đư­ợc gán 50 và sau đó giá trị của b được tăng lên thành 6.

Tuy nhiên, nếu biểu thức trên là:

c = a * ++b;

thì giá trị của c sẽ là 60, và b sẽ là 6 bởi vì b được tăng 1 trước khi thực hiện phép nhân với a, sau đó giá trị được gán vào c.

Trong trường hợp mà tác động của việc tăng hay giảm là riêng lẻ thì toán tử có thể đứng trước hoặc sau toán hạng đều được.

Hầu hết trình biên dịch C sinh mã rất nhanh và hiệu quả đối với việc tăng và giảm giá trị. Mã này sẽ tốt hơn so với khi ta dùng toán tử gán. Vì vậy, các toán tử tăng và giảm nên được dùng bất cứ khi nào có thể.

Tóm tắt bài học

Thông thường, khi chương trình ứng dụng cần xử lý dữ liệu, nó cần có nơi nào đó để lưu trữ tạm thời dữ liệu này. Nơi mà dữ liệu được lưu trữ gọi là bộ nhớ.

Các ngôn ngữ lập trình hiện đại ngày nay cho phép chúng ta sử dụng các tên tượng trưng gọi là biến (variable), dùng để chỉ đến một vùng trong bộ nhớ nơi mà các giá trị cụ thể được lưu trữ.

Không có giới hạn về số vị trí bộ nhớ mà một chương trình có thể dùng.

Một hằng (constant) là một giá trị không bao giờ bị thay đổi.

Tên của các biến (variable), các hàm (function), các nhãn (label) và các đối tượng khác nhau do người dùng định nghĩa gọi là định danh.

Tất cả ngôn ngữ dành một số từ nhất định cho mục đích riêng. Những từ này được gọi là là "từ khóa" (keywords).

Các kiểu dữ liệu chính của C là character, integer, float, double và void.

Một bổ từ đ­ược sử dụng để thay đổi kiểu dữ liệu cơ bản sao cho phù hợp với nhiều tình huống đa dạng. Các bổ từ được sử dụng trong C là signed, unsigned, long và short.

C hỗ trợ hai loại toán tử số học: một ngôi và hai ngôi.

Toán tử tăng '++' và toán tử giảm '--' là những toán tử một ngôi. Nó chỉ hoạt động trên biến kiểu số.

Toán tử hai ngôi số học là +, -, *, /, %, nó chỉ tác động lên những hằng số, biến hay biểu thức.

Toán tử phần dư '%' chỉ áp dụng trên các số nguyên và cho kết quả là phần dư của phép chia số nguyên.

Kiểm tra tiến độ học tập

1C có phân biệt chữ thường và hoa. (True / False)

2Số 10 là một _______________.

3Ký tự đầu của định danh có thể là một số. (True / False)

4Dùng kiểu _________ sẽ tiết kiệm bộ nhớ do nó chiếm chỉ nửa không gian nhớ so với _________.

5Kiểu dữ liệu _______ được dùng để chỉ cho trình biên dịch C biết rằng không có giá trị nào được trả về.

6_______ và _______ là hai nhóm toán tử số học.

A. Bitwise & và |

B. Một ngôi và hai ngôi

C. Luận lý AND

D. Không câu trả lời nào cả

7Các toán tử một ngôi số học là __ và __.

A. ++ và --

B. % và ^

C. ^ và $

D. Không câu trả lời nào cả

Bài tập tự làm

1. Chọn tương ứng cho cột A và B:

Cột A

Cột B

8

10.34

ABC

abc

23

12112134.86868686886

_A1

$abc

'A'

Tên định danh sai

Hằng số nguyên

Hằng ký tự

Double

Số chấm động

Tên định danh đúng

Hướng dẫn : Nhiều mục trên cột A có thể tương ứng tới một mục đơn tại cột B.

2.Tính giá trị của các biến sau mỗi câu lệnh dưới đây:

a.int a = 4^4

b.int a = 23.34

c.a = 10

b = a + a++

d.a = -5

b = -a

Bài 3 Biến, Toán tử và Kiểu dữ liệu

Mục tiêu:

Kết thúc bài học này, bạn có thể:

Sử dụng biến, kiểu dữ liệu và biểu thức số học.

Phần I - Trong thời gian 1 giờ 30 phút đầu:

3.1Biến

Như chúng ta đã biết, Biến là tên đặt cho vị trí bộ nhớ máy tính, có thể dùng để lưu trữ các giá trị khác nhau tại những thời điểm khác nhau. Trong chương này, chủ yếu chúng ta sẽ học cách tạo và sử dụng biến.

3.1.1 Tạo biến

Tạo biến bao gồm việc tạo kiểu dữ liệu và tên hợp lý cho biến, ví dụ:

int currentVal;

Trong ví dụ trên, tên biến là "currentVal" có kiểu dữ liệu là số nguyên (integer).

3.2 Kiểu dữ liệu

Kiểu dữ liệu định nghĩa loại giá trị mà sẽ được lưu trong một biến nào đó, ví dụ:

int currentVal;

Trong ví dụ trên "int" chỉ rằng biến currentVal sẽ lưu giá trị kiểu số nguyên (integer).

3.3 Biểu thức số học

Một biểu thức số học trong C bao gồm một tên biến nằm phía bên trái của dấu "=", tên biến hoặc hằng nằm bên phải dấu "=". Biến và hằng nằm bên phải của dấu "=" được nối với nhau bởi những toán tử số học như +, -, *, và /. Thí dụ,

delta = alpha * beta / gamma + 3.2 * 2 / 5;

Bây giờ chúng ta xét một chương trình tính tiền lãi đơn giản như sau

Ví dụ 1:

1.Gọi trình soạn thảo để nhập những câu lệnh cho chương trình C.

2.Tạo ra một tập tin mới.

3.Nhập vào đoạn mã sau:

#include <stdio.h>

void main()

{

int principal, period;

float rate, si;

principal = 1000;

period = 3;

rate = 8.5;

si = principal * period * rate / 100;

printf("%f", si);

}

Ðể thấy kết quả ở đầu ra, thực hiện tiếp các bước sau:

4.Lưu tập tin với tên myprogramI.C.

5.Biên dịch tập tin myprogramI.C.

6.Thực thi chương trình myprogramI.C.

7.Trở về trình soạn thảo.

Mẫu kết xuất cho chương trình trên như hình sau:

Hình 3.1: Kết quả của myprogramI.C

Ví dụ 2:

1.Tạo một tập tin mới.

2.Gõ vào mã sau:

#include <stdio.h>

void main()

{

int a, b, c, sum;

printf("

Enter any three numbers: ");

scanf("%d %d %d", &a, &b, &c);

sum = a + b + c;

printf("

Sum = %d", sum);

}

3.Lưu tập tin với tên myprogramII.C.

4.Biên dịch tập tin myprogramII.C.

5.Thực thi chương trình myprogramII.C.

6.Trở về trình soạn thảo.

Mẫu kết quả ở đầu ra của chương trình trên như hình sau:

Hình 3.2: Kết quả của myprogramII.C

Phần II - Trong thời gian 30 phút kế tiếp:

1.Viết một chương trình nhập vào một số và tính bình phương của số đó.

Hướng dẫn: Thực hiện theo các bước sau:

a.Nhập vào một số.

b.Nhân số đó với chính nó và hiển thị kết quả đó.

Bài tập tự làm

1.Viết chương trình tính diện tích và chu vi của một vòng tròn.

2.Viết chương trình nhập lương và tuổi của một người và hiển thị các số vừa nhập đó ra màn hình.

Bài 4 Toán tử và Biểu thức

Mục tiêu:

Kết thúc bài học này, bạn có thể:

Hiểu được Toán tử gán

Hiểu được biểu thức số học

Nắm được toán tử quan hệ (Relational Operators) và toán tử luận lý (Logical Operators)

Hiểu toán tử luận lý nhị phân (Bitwise Logical Operators) và biểu thức (Expressions)

Hiểu khái niệm ép kiểu

Hiểu độ ưu tiên của các toán tử.

Giới thiệu

C có một tập các toán tử phong phú. Toán tử là công cụ dùng để thao tác dữ liệu. Một toán tử là một ký hiệu dùng để đại diện cho một thao tác cụ thể nào đó được thực hiện trên dữ liệu. C định nghĩa bốn loại toán tử: toán tử số học (arithmetic), quan hệ (relational), luận lý (logical), và toán tử luận lý nhị phân (bitwise). Bên cạnh đó, C còn có một số toán tử đặc biệt.

Toán tử thao tác trên hằng hoặc biến. Hằng hoặc biến này được gọi là toán hạng (operands). Biến đã được đề cập ở các chương trước. Hằng là những giá trị cố định mà chương trình không thể thay đổi. Hằng trong C có thể là bất cứ kiểu dữ liệu cơ bản nào. Toán tử được phân loại: toán tử một ngôi, hai ngôi hoặc ba ngôi. Toán tử một ngôi chỉ thao tác trên một phần tử dữ liệu, toán tử hai ngôi trên hai phần tử dữ liệu và ba ngôi trên ba phần tử dữ liệu.

Ví dụ 4.1:

c = a + b;

Ở đây a, b, c là những toán hạng, dấu '=' và dấu '+' là những toán tử.

4.1 Biểu thức (Expressions)

Một biểu thức là tổ hợp các toán tử và toán hạng. Toán tử thực hiện các thao tác như cộng, trừ, so sánh v.v... Toán hạng là những biến hay những giá trị mà các phép toán được thực hiện trên nó. Trong ví dụ a + b, "a" và "b" là toán hạng và "+" là toán tử. Tất cả kết hợp lại là một biểu thức.

Trong quá trình thực thi chương trình, giá trị thực sự của biến (nếu có) sẽ được sử dụng cùng với các hằng có mặt trong biểu thức. Việc đánh giá biểu thức được thực hiện nhờ các toán tử. Vì vậy, mọi biểu thức trong C đều có một giá trị.

Các ví dụ về biểu thức là:

2

x

3 + 7

2 × y + 5

2 + 6 × (4 - 2)

z + 3 × (8 - z)

Ví dụ 4.2:

Roland nặng 70 kilograms, và Mark nặng k kilograms. Viết một biểu thức cho tổng cân nặng của họ. Tổng cân nặng của hai người tính bằng kilograms là 70 + k.

Ví dụ 4.3:

Tính giá trị biểu thức 4 × z + 12 với z = 15.

Chúng ta thay thế mọi z với giá trị 15, và đơn giản hóa biểu thức theo quy tắc: thi hành phép toán trong dấu ngoặc trước tiên, kế đến lũy thừa, phép nhân và chia rồi phép cộng và trừ.

4 × z + 12 trở thành

4 × 15 + 12 = (phép nhân thực hiện trước phép cộng)

60 + 12 =

72

Toán tử gán (Assignment Operator)

Trước khi nghiên cứu các toán tử khác, ta hãy xét toán tử gán (=). Ðây là toán tử thông dụng nhất cho mọi ngôn ngữ và mọi người đều biết. Trong C, toán tử gán có thể được dùng cho bất kỳ biểu thức C hợp lệ. Dạng thức chung cho toán tử gán là:

Tên biến = biểu thức;

Gán liên tiếp

Nhiều biến có thể được gán cùng một giá trị trong một câu lệnh đơn. Việc này thực hiện qua cú pháp gán liên tiếp. Ví dụ:

a = b = c =10;

Dòng mã trên gán giá trị 10 cho a, b,và c. Tuy nhiên, việc này không thể thực hiện lúc khai báo biến. Ví dụ,

int a = int b = int c= 0;

Câu lệnh trên phát sinh lỗi vì sai cú pháp.

Biểu thức số học (Arithmetic Expressions)

Các phép toán thường được thực hiện theo một thứ tự cụ thể (hoặc riêng biệt) để cho ra giá trị cuối cùng. Thứ tự này gọi là độ ưu tiên (sẽ nói đến sau).

Các biểu thức toán học trong C được biểu diễn bằng cách sử dụng toán tử số học cùng với các toán hạng dạng số và ký tự. Những biểu thức này gọi là biểu thức số học (Arithmetic Expressions). Ví dụ về biểu thức số học là :

a * (b+c/d)/22;

++i % 7;

5 + (c = 3+8);

Như chúng ta thấy ở trên, toán hạng có thể là hằng, biến hay kết hợp cả hai. Hơn nữa, một biểu thức có thể là sự kết hợp của nhiều biểu thức con. Chẳng hạn, trong biểu thức đầu, c/d là một biểu thức con, và trong biểu thức thứ ba c = 3+8 cũng là một biểu thức con.

4.2 Toán tử quan hệ (Relational Operators)

Toán tử quan hệ được dùng để kiểm tra mối quan hệ giữa hai biến, hay giữa một biến và một hằng. Ví dụ, việc xét số lớn hơn của hai số, a và b, được thực hiện thông qua dấu lớn hơn (>) giữa hai toán hạng a và b (a > b).

Trong C, true (đúng) là bất cứ giá trị nào khác không (0), và false (sai) là bất cứ giá trị nào bằng không (0). Biểu thức dùng toán tử quan hệ trả về 0 cho false và 1 cho true. Ví dụ biểu thức sau :

a == 14 ;

Biểu thức này kiểm tra xem giá trị của a có bằng 14 hay không. Giá trị của biểu thức sẽ là 0 (false) nếu a có giá trị khác 14 và 1 (true) nếu nó là 14.

Bảng sau mô tả ý nghĩa của các toán tử quan hệ.

Toán tử

Ý nghĩa

lớn hơn

>=

lớn hơn hoặc bằng

<

nhỏ hơn

<=

nhỏ hơn hoặc bằng

==

bằng

!=

không bằng

Bảng 4.1: Toán tử quan hệ và ý nghĩa

4.3 Toán tử luận lý (Logical Operators) và biểu thức

Toán tử luận lý là các ký hiệu dùng để kết hợp hay phủ định biểu thức có chứa các toán tử quan hệ.

Những biểu thức dùng toán tử luận lý trả về 0 cho false và 1 cho true.

Bảng sau mô tả ý nghĩa của các toán tử luận lý.

Toán tử

Ý nghĩa

AND: trả về kết quả là true khi cả 2 toán hạng đều true

OR : trả về kết quả là true khi chỉ một trong hai toán hạng đều true

!

NOT: Chuyển đổi giá trị của toán hạng duy nhất từ true thành false và ngược lại.

Table 4.2: Toán tử luận lý và ý nghĩa

Lưu ý: Bất cứ toán tử luận lý nào có ký hiệu là hai ký tự thì không được có khoảng trắng giữa hai ký tự đó, ví dụ : == sẽ không đúng nếu viết là = =.

Giả sử một chương trình phải thực thi những bước nhất định nếu điều kiện a < 10 và b == 7 được thoả mãn. Ðiều kiện này được viết ra bằng cách dùng toán tử quan hệ kết hợp với toán tử luận lý AND. Toán tử AND được viết là &&. Ta sẽ có điều kiện để kiểm tra như sau :

(a < 10) && (b == 7);

Tương tự, toán tử OR dùng để kiểm tra xem có một trong số các điều kiện kiểm tra là đúng hay không. Nó có dạng là dấu (||). Cùng ví dụ trên nhưng điều kiện cần kiểm tra là: chỉ cần một trong hai câu lệnh là đúng thì ta có mã sau :

(a < 10) || (b == 7);

Toán tử luận lý thứ ba là NOT được biểu diễn bằng ký hiệu dấu chấm than '!'. Toán tử này đảo ngược giá trị luận lý của biểu thức. Ví dụ, để kiểm tra xem biến s có bé hơn 10 hay không, ta viết đều kiện kiểm tra như sau:

(s < 10);

hay là

(! (s >= 10)) /* s không lớn hơn hay bằng 10 */

Cả toán tử quan hệ và luận lý có quyền ưu tiên thấp hơn toán tử số học. Ví dụ, 5 > 4 + 3 được tính tương đương với 5 > (4 + 3), nghĩa là 4+3 sẽ được tính trước và sau đó toán tử quan hệ sẽ được thực hiện. Kết quả sẽ là false, tức là trả về 0.

Câu lệnh sau:

printf("%d", 5> 4 + 3);

sẽ cho ra:

0

vì 5 bé hơn (4 + 3) .

4.4 Toán tử luận lý nhị phân (Bitwise Logical Operators) và biểu thức

Ví dụ xét toán hạng có giá trị là 12, toán tử luận lý nhị phân sẽ coi số 12 này như 1100. Toán tử luận lý nhị phân xem xét các toán hạng dưới dạng chuỗi bit chứ không là giá trị số thông thường. Giá trị số có thể thuộc các cơ số: thập phân (decimal), bát phân (octal) hay thập lục phân (hexadecimal). Riêng toán tử luận lý nhị phân sẽ chuyển đổi toán hạng mà nó thao tác thành biểu diễn nhị phân tương ứng, đó là dãy số 1 hoặc là 0.

Toán tử luận lý nhị phân gồm &, | , ^ , ~ , vv ... được tổng kết qua bảng sau:.

Toán tử

Mô tả

Bitwise AND

( x & y)

Mỗi vị trí của bit trả về kết quả là 1 nếu bit tại vị trí tương ứng của hai toán hạng đều là 1.

Bitwise OR

( x | y)

Mỗi vị trí của bit trả về kết quả là 1 nếu bit tại vị trí tương ứng của một trong hai toán hạng là 1.

Bitwise NOT

( ~ x)

Ðảo ngược giá trị các bit của toán hạng (1 thành 0 và ngược lại).

Bitwise XOR

( x ^ y)

Mỗi vị trí của bit trả về kết quả là 1 nếu bit tại vị trí tương ứng của một trong hai toán hạng là 1 chứ không phải cả hai cùng là 1.

Bảng 4.3: Toán tử luận lý nhị phân

Toán tử luận lý nhị phân xem kiểu dữ liệu số như là số nhị phân 32-bit, giá trị số được đổi thành giá trị bit để tính toán trước rồi sau đó sẽ trả về kết quả ở dạng số ban đầu. Ví dụ:

Biểu thức 10 & 15 có nghĩa là (1010 & 1111) trả về giá trị 1010 có nghĩa là 10.

Biểu thức 10 | 15 có nghĩa là (1010 | 1111) trả về giá trị 1111 có nghĩa là 15.

Biểu thức 10 ^ 15 có nghĩa là (1010 ^ 1111) trả về giá trị 0101 có nghĩa là 5.

Biểu thức ~10 có nghĩa là ( ~1010 ) trả về giá trị 1111.1111.1111.1111.1111.1111.1111.0101 có nghĩa là -11.

4.5 Biểu thức dạng hỗn hợp & Chuyển đổi kiểu

Một biểu thức dạng hỗn hợp là một biểu thức mà trong đó các toán hạng của một toán tử thuộc về nhiều kiểu dữ liệu khác nhau. Những toán hạng này thông thường được chuyển về cùng kiểu với toán hạng có kiểu dữ liệu lớn nhất. Điều này được gọi là tăng cấp kiểu. Sự phát triển về kiểu dữ liệu theo thứ tự sau :

char < int <long <float <double

Chuyển đổi kiểu tự động được trình bày dưới đây nhằm xác định giá trị của biểu thức:

a. char và short được chuyển thành int và float được chuyển thành double.

b. Nếu có một toán hạng là double, toán hạng còn lại sẽ được chuyển thành double, và kết quả là double.

c. Nếu có một toán hạng là long, toán hạng còn lại sẽ được chuyển thành long, và kết quả là long.

d. Nếu có một toán hạng là unsigned, toán hạng còn lại sẽ được chuyển thành unsigned và kết quả cũng là unsigned.

e.Nếu tất cả toán hạng kiểu int, kết quả là int.

Ngoài ra nếu một toán hạng là long và toán hạng khác là unsigned và giá trị của kiểu unsigned không thể biểu diễn bằng kiểu long. Do vậy, cả hai toán hạng được chuyển thành unsigned long.

Sau khi áp dụng những quy tắc trên, mỗi cặp toán hạng có cùng kiểu và kết quả của mỗi phép tính sẽ cùng kiểu với hai toán hạng.

char ch;

float f;

double d;

result = (ch/i) + (f*d) - (f+i);

int double float

double

double

Trong ví dụ trên, trước tiên, ch có kiểu ký tự được chuyển thành integer và float f được chuyển thành double. Sau đó, kết quả của ch/i được chuyển thành double bởi vì f*d là double. Kết quả cuối cùng là double bởi vì các toán hạng lúc này đều là double.

4.5.1 Ép kiểu (Casts)

Thông thường, ta nên đổi tất cả hằng số nguyên sang kiểu float nếu biểu thức bao gồm những phép tính số học dựa trên số thực, nếu không thì vài biểu thức có thể mất đi giá trị thật của nó.Ta xem ví dụ:

int x,y,z;

x = 10;

y = 100;

z = x/y;

Trong trường hợp này, z sẽ được gán 0 khi phép chia diễn ra và phần thập phân (0.10) sẽ bị cắt bỏ.

Do đó một biểu thức có thể được ép thành một kiểu nhất định. Cú pháp chung của cast là:

(kiểu dữ liệu) biểu thức

Ví dụ, để đảm bảo rằng biểu thức a/b, với a và b là số nguyên, cho kết quả là kiểu float, dòng mã sau được viết:

(float) a/b;

Ép kiểu có thể áp dụng cho các giá trị hằng, biểu thức hay biến, ví dụ:

(int) 17.487;

(double) (5 * 4 / 8);

(float) (a + 7);

Trong ví dụ thứ hai, toán tử ép kiểu không đạt mục đích của nó bởi vì nó chỉ thực thi sau khi toàn biểu thức trong dấu ngoặc đã được tính. Biểu thức 5 * 4 / 8 cho ra giá trị là 2 (vì nó có kiểu là số nguyên nên đã cắt đi phần thập phân), vì vậy, giá trị kết quả với kiểu double cũng là 2.0.

Ví dụ:

int i = 1, j = 3;

x = i / j; /* x = 0.0 */

x = (float) i/(float) j; /* x = 0.33 */

4.6 Độ ưu tiên của toán tử (Precedence)

Độ ưu tiên của toán tử thiết lập thứ tự ưu tiên tính toán khi một biểu thức số học cần được ước lượng. Tóm lại, độ ưu tiên đề cập đến thứ tự mà C thực thi các toán tử. Thứ tự ưu tiên của toán tử số học được thể hiện như bảng dưới đây.

Loại toán tử

Toán tử

Tính kết hợp

Một ngôi

- , ++, --

Phải sang trái

Hai ngôi

^

Trái sang phải

*, /, %

+, -

=

Phải sang trái

Bảng 4.4: Thứ tự ưu tiên của toán tử số học

Những toán tử nằm cùng một hàng ở bảng trên có cùng quyền ưu tiên. Việc tính toán của một biểu thức số học sẽ được thực hiện từ trái sang phải cho các toán tử cùng độ ưu tiên. Toán tử *, /, và % có cùng đô ưu tiên và cao hơn + và - (hai ngôi).

Độ ưu tiên của những toán tử này có thể được thay đổi bằng cách sử dụng dấu ngoặc đơn. Một biểu thức trong ngoặc luôn luôn được tính toán trước. Một cặp dấu ngoặc đơn này có thể được bao trong cặp khác. Ðây là sự lồng nhau của những dấu ngoặc đơn. Trong trường hợp đó, việc tính toán trước tiên được thực hiện tại cặp dấu ngoặc đơn trong cùng nhất rồi đến dấu ngoặc đơn bên ngoài.

Nếu có nhiều bộ dấu ngoặc đơn thì việc thực thi sẽ theo thứ tự từ trái sang phải.

Tính kết hợp cho biết cách thức các toán tử kết hợp với các toán hạng của chúng. Ví dụ, đối với toán tử một ngôi: toán hạng nằm bên phải được tính trước, trong phép chia thì toán hạng bên trái được chia cho toán hạng bên phải. Đối với toán tử gán thì biểu thức bên phải được tính trước rồi gán giá trị cho biến bên trái toán tử.

Tính kết hợp cũng cho biết thứ tự mà theo đó C đánh giá các toán tử trong biểu thức có cùng độ ưu tiên. Các toán tử như vậy có thể tính toán từ trái sang phải hoặc ngược lại như thấy trong bảng 4.5.

Ví dụ:

a = b = 10/2;

Giá trị 5 sẽ gán cho b xong rồi gán cho a. Vì vậy thứ tự ưu tiên sẽ là phải sang trái. Hơn nữa,

-8 * 4 % 2 - 3

được tính theo trình tự sau:

Trình tự Thao tác Kết quả

1. - 8 (phép trừ một ngôi) số âm của 8

2. - 8 * 4 - 32

3. - 32 % 2 0

4. 0-3 -3

Theo trên thì toán tự một ngôi (dấu - ) có quyền ưu tiên cao nhất được tính trước tiên. Giữa * và % thì được tính từ trái sang phải. Tiếp đến sẽ là phép trừ hai ngôi.

Thứ tự ưu tiên của các biểu thức con

Những biểu thức phức tạp có thể chứa những biểu thức nhỏ hơn gọi là biểu thức con. C không xác định thứ tự mà các biểu thức con được lượng giá. Một biểu thức sau:

a * b /c + d *c;

bảo đảm rằng biểu thức con a * b/c và d*c sẽ được tính trước phép cộng. Hơn nữa, quy tắc từ trái sang phải cho phép toán nhân và chia bảo đảm rằng a sẽ được nhân với b và sau đó sẽ chia cho c. Nhưng không có quy tắc xác định hoặc a*b /c được tính trước hay sau d*c. Tùy chọn này là ở người thiết kế trình biên dịch quyết định. Quy tắc trái sang phải hay ngược lại chỉ áp dụng cho một chuỗi toán tử cùng độ ưu tiên. Cụ thể, nó áp dụng cho phép nhân và chia trong a*b/c. Nhưng nó không áp dụng cho toán tử + vì đã khác cấp.

Bởi vì không thể xác định thứ tự tính toán các biểu thức con, do vậy, ta không nên dùng các biểu thức nếu giá trị biểu thức phụ thuộc vào thứ tự tính toán các biểu thức con . Xét ví dụ sau:

a * b + c * b++ ;

Có thể trình biên dịch này tính giá trị mục bên trái trước và dùng cùng giá trị b cho cả hai biểu thức con. Nhưng trình biên dịch khác lại tính giá trị mục bên phải và tăng giá trị b trước khi tính giá trị mục bên trái.

Ta không nên dùng toán tử tăng hay giảm cho một biến mà nó xuất hiện nhiều hơn một lần trong một biểu thức.

Thứ tự ưu tiên giữa những toán tử so sánh (toán tử quan hệ)

Ta đã thấy trong phần trước một số toán tử số học có độ ưu tiên cao hơn các toán tử số học khác. Riêng với toán tử so sánh, không có thứ tự ưu tiên giữa các toán tử và chúng được ước lượng từ trái sang phải.

Thứ tự ưu tiên giữa những toán tử luận lý

Bảng dưới đây trình bày thứ tự ưu tiên cho toán tử luận lý.

Thứ tự

Toán tử

1

NOT

2

AND

3

OR

Bảng 4.5: Thứ tự ưu tiên cho toán tử luận lý

Khi có nhiều toán tử luận lý trong một điều kiện, chúng được lượng giá từ phải sang trái.

Ví dụ, xét điều kiện sau:

False OR True AND NOT False AND True

Ðiều kiện này được tính như sau:

1.False OR True AND [NOT False] AND True

NOT có độ ưu tiên cao nhất.

2.False OR True AND [True AND True]

Ở đây, AND là toán tử có độ ưu tiên cao nhất và những toán tử có cùng ưu tiên được tính từ phải sang trái.

3.False OR [True AND True]

4. [False OR True]

5.True

Thứ tự ưu tiên giữa các kiểu toán tử khác nhau

Khi một biểu thức có nhiều hơn một kiểu toán tử thì thứ tự ưu tiên phải được thiết lập giữa các kiểu toán tử với nhau.

Bảng dưới đây cho biết thứ tự ưu tiên giữa các kiểu toán tử khác nhau.

Thứ tự

Kiểu toán tử

1

Số học

2

So sánh (Quan hệ)

3

Luận lý

Bảng 4.6. Thứ tự ưu tiên giữa các kiểu toán tử khác nhau

Do vậy, trong một biểu thức gồm cả ba kiểu toán tử, các toán tử số học được tính trước, kế đến là toán tử so sánh và sau đó là toán tử luận lý. Thứ tự ưu tiên của các toán tử trong cùng một kiểu thì đã được nói tới ở những phần trước.

Xét ví dụ sau:

2*3+4/2 > 3 AND 3<5 OR 10<9

Việc thực hiện tính toán sẽ như sau:

1.[2*3+4/2] > 3 AND 3<5 OR 10<9

Ðầu tiên toán tử số học sẽ được tính theo thứ tự ưu tiên như bảng 4.4.

2.[[2*3]+[4/2]] > 3 AND 3<5 OR 10<9

3.[6+2] >3 AND 3<5 OR 10<9

4.[8 >3] AND [3<5] OR [10<9]

Kế đến sẽ tính tất cả những toán tử so sánh có cùng độ ưu tiên theo quy tắc tính từ trái sang phải.

5.True AND True OR False

Cuối cùng tính toán các toán tử kiểu luận lý. AND sẽ có độ ưu tiên cao hơn OR.

6.[True AND True]OR False

7.True OR False

8.True

Dấu ngoặc đơn

Thứ tự ưu tiên của các toán tử có thể thay đổi bởi các dấu ngoặc đơn. Khi đó, chương trình sẽ tính toán các phần dữ liệu trong dấu ngoặc đơn trước.

Khi một cặp dấu ngoặc đơn này được bao trong cặp khác, việc tính toán thực hiện trước tiên tại cặp dấu ngoặc đơn trong cùng nhất, rồi đến dấu ngoặc đơn bên ngoài.

Nếu có nhiều bộ dấu ngoặc đơn thì việc thực hiện sẽ theo thứ tự từ trái sang phải.

Xét ví dụ sau:

5+9*3^2-4 > 10 AND (2+2^4-8/4 > 6 OR (2<6 AND 10>11))

Cách tính sẽ là:

1.5+9*3^2-4 > 10 AND (2+2^4-8/4 > 6 OR (True AND False))

Dấu ngoặc đơn trong cùng sẽ được tính trước tất cả các toán tử khác và áp dụng quy tắc cơ bản trong bảng 4.6 cho tính toán bên trong cặp dấu ngoặc này.

2.5+9*3^2-4 > 10 AND (2+2^4-8/4 > 6 OR False)

3.5+9*3^2-4 > 10 AND (2+16-8/4 > 6 OR False)

Kế đến dấu ngoặc đơn ở ngoài được xét đến. Xem lại các bảng nói về thứ tự ưu tiên của các toán tử.

4.5+9*3^2-4 > 10 AND (2+16-2 > 6 OR False)

5.5+9*3^2-4 > 10 AND (18-2 > 6 OR False)

6.5+9*3^2-4 > 10 AND (16 > 6 OR False)

7.5+9*3^2-4 > 10 AND (True OR False)

8.5+9*3^2-4 > 10 AND True

9.5+9*9-4>10 AND True

Ta tính biểu thức bên trái trước theo các quy tắc

10.5+81-4>10 AND True

11.86-4>10 AND True

12.82>10 AND True

13.True AND True

14.True.

Tóm tắt bài học

C định nghĩa bốn loại toán tử: số học, quan hệ (so sánh), luận lý và luận lý nhị phân.

Tất cả toán tử trong C được tính toán theo thứ tự độ ưu tiên.

Toán tử quan hệ được dùng kiểm tra mối quan hệ giữa hai biến hay giữa một biến và một hằng.

Toán tử luận lý là những ký hiệu dùng để kết hợp hay phủ định những biểu thức chứa các toán tử quan hệ.

Toán tử luận lý nhị phân xét các toán hạng như là bit nhị phân chứ không phải là các giá trị số thập phân.

Phép gán (=) được xem như là một toán tử có tính kết hợp từ phải sang trái.

Độ ưu tiên thiết lập sự phân cấp của một tập các toán tử so với tập các toán tử khác khi ước lượng một biểu thức.

Kiểm tra tiến độ học tập

1.______ là những công cụ thao tác dữ liệu.

A. Những toán tử

B. Những toán hạng

C. Những biểu thức

D. Không câu nào đúng

2. Một ­­­­­_______ bao gồm sự kết hợp của các toán tử và các toán hạng.

A. Biểu thức

B. Hàm

C. Con trỏ

D. Không câu nào đúng

3.________ thiết lập sự phân cấp của một tập các toán tử so với tập các toán tử khác khi ước lượng một biểu thức.

A. Những toán hạng

B. Độ ưu tiên

C. Toán tử

D. Không câu nào đúng

4.____________ là một biểu thức có các toán hạng thuộc nhiều kiểu dữ liệu khác nhau.

A. Biểu thức đơn

B. Biểu thức hỗn hợp

C. Quyền ưu tiên

D. Không câu nào đúng

5.Một biểu thức được ép thành một kiểu nhất định bằng cách dùng ____.

A. Ép kiểu

B. Quyền ưu tiên

C. Toán tử

D. Không câu nào đúng

6._________ được dùng để kết hợp hay phủ định biểu thức chứa các toán tử quan hệ.

A. Những toán tử luận lý

B. Những toán tử luận lý nhị phân

C. Những toán tử phức

D. Không câu nào đúng

7.Những toán tử luận lý nhị phân là __, ___, __ và __ .

A. % , ^ , * and @

B. &,|,~ and ^

C. !,],& and *

D. Không câu nào đúng

8. Ðộ ưu tiên của các toán tử có thể được thay đổi bằng cách đặt các phần tử được yêu cầu của biểu thức trong _________ .

A. Dấu ngoặc xoắn ({ })

B. Ký hiệu mũ ( ^)

C. Những dấu ngoặc đơn (())

D. Không câu nào đúng

Bài tập tự làm

1.Viết một chương trình nhập và cộng ba số.

2.Viết một chương trình tính giá trị của biểu thức với các giá trị sau:

z = a*b+(c/d)-e*f ;

a = 10

b = 7

c = 15.75

d = 4

e = 2

f = 5.6

3.Viết một chương trình tính diện tích và chu vi của hình chữ nhật.

4.Viết một chương trình tính thể tích của một hình trụ.

5.Viết một chương trình tính lương thực lãnh của một nhân viên theo công thức dưới đây

Lương cơ bản : $ 12000

DA : 12% lương cơ bản

HRA : $150

TA : $120

Các mục khác : $450

Thuế :

PF :14% lương cơ bản và IT: 15% lương cơ bản

Lương thực lãnh = Lương cơ bản + DA + HRA + TA + Các mục khác - (PF + IT)

Bài 5 Toán tử và Biểu thức

Mục tiêu:

Kết thúc bài học này, bạn có thể:

Sử dụng được các toán tử số học, so sánh và luận lý

Chuyển đổi các kiểu dữ liệu

Nắm được thứ tự ưu tiên giữa các toán tử.

Các bước trong chương này đã được nghiên cứu kỹ và giải thích chi tiết để chúng ta có thể hiểu rõ và áp dụng chúng một cách hoàn chỉnh.Ta hãy theo các bước cẩn thận.

Phần I - Trong thời gian 1 giờ 30 phút đầu:

Ví dụ 1:

Trong chương này, ta sẽ viết một chương trình tính toán tiền lãi đơn giản (lãi thuần chưa tính tiền vốn vào) khi ta vay tiền.

Công thức để tính toán là p * n * r /100. Ở đây 'p' có nghĩa là tiền vốn, 'n' là số năm và 'r' có nghĩa là tỉ lệ lãi suất.

Chương trình khai báo ba biến số thực 'float' có tên là p, n và r. Chú ý rằng, các biến được khai báo trên cùng một dòng mã thì ta dùng dấu phẩy (,) để phân chia chúng với nhau. Mỗi biến trên được gán một giá trị.

Xét dòng mã sau:

printf("

Amount is: %f", p*n*r/100);

Trong printf() ở trên, chúng ta đã dùng '%f' để hiển thị giá trị của biến kiểu float (số thực), giá trị biến này là p*n*r/100, công thức dùng tính lãi đơn giản được đưa vào trong printf(). Đó là p, n và r được nhân với nhau và kết quả được chia cho 100. Như vậy printf() sẽ hiển thị lãi đơn.

Gọi Borland C.

5.1 Tính lãi đơn

1. Tạo ra một tập tin mới.

2. Gõ đoạn mã sau trong 'Edit window':

#include <stdio.h>

#include <conio.h>

void main()

{

float p, n, r;

clrscr();

p = 1000;

n = 2.5;

r = 10.5;

printf("

Amount is: %f", p*n*r/100);

}

3. Lưu tập tin với tên simple.c.

4. Biên dịch tập tin simple.c.

5. Thực thi chương trình simple.c.

6. Trở về trình soạn thảo.

Kết quả:

The Amount is: 262.500000

5.2 Dùng toán tử số học

Trong phần này ta sẽ viết một chương trình có sử dụng toán tử số học.

Chương trình này khai báo bốn biến số nguyên tên là a, b, c và d. Giá trị sẽ gán cho các biến a, b và c là:

a = 50, b = 24, c = 68

Xét dòng mã sau:

d = a*b+c/2;

a nhân với b. c chia cho 2. Kết quả của a*b được cộng với thương số của c/2. Giá trị này sẽ gán cho d qua toán tử (=). Biểu thức được tính như sau:

1.50 * 24 = 1200

2.68 / 2 = 34

3.1200 + 34 = 1234

4.d = 1234

'printf( )' : hiển thị giá trị của biến d.

Xét biểu thức:

d = a*(b+c+(a-c)*b);

Ở đây dấu ngoặc đơn trong cùng có độ ưu tiên cao nhất. Do vậy, (a-c) được tính trước. Sau đó, tính tới các dấu ngoặc đơn ngoài. Kết quả của (a-c) được nhân cho b bởi vì '*' có độ ưu tiên cao hơn '-' và '+'. Biểu thức được tính như dưới đây:

1.d = 50 * (24 + 68 + (50 - 68) * 24)

2.d = 50 * (24 + 68 + (-18) * 24)

3.d = 50 * (24 + 68 + (-432))

4.d = 50 * (92 - 432)

5.d = 50 * (-340)

6.d = -17000

Các biểu thức khác được tính tùy vào các toán tử đã được dùng. Kết quả được hiển thị bởi lệnh 'printf()'.

1.Tạo mới một tập tin.

2. Gõ đoạn mã sau trong 'Edit window':

#include <stdio.h>

#include <conio.h>

void main()

{

int a,b,c,d;

clrscr();

a = 50;

b = 24;

c = 68;

d = a*b+c/2;

printf("

The value after a*b+c/2 is: %d", d);

d = a%b;

printf("

The value after a mod b is: %d", d);

d = a*b-c;

printf("

The value after a*b-c is: %d", d);

d = a/b+c;

printf("

The value after a/b+c is: %d", d);

d = a+b*c;

printf("

The value after a+b*c is: %d", d);

d = (a+b)*c;

printf("

The value after (a+b)*c is: %d", d);

d = a*(b+c+(a-c)*b);

printf("

The value after a*(b+c+(a-c)*b) is: %d", d);

}

3. Lưu tập tin với tên arith.c.

4. Biên dịch tập tin arith.c.

5. Thực thi chương trình arith.c.

6. Trở về trình soạn thảo.

Kết quả xuất:

The value after a*b+c/2 is: 1234

The value after a mod b is: 2

The value after a*b-c is: 1132

The value after a/b+c is: 70

The value after a+b*c is: 1682

The value after (a+b)*c is: 5032

The value after a*(b+c+(a-c)+b) is: -17000

5.3 Dùng toán tử so sánh và luận lý

Trong phần này chúng ta sẽ viết một chương trình sử dụng toán tử so sánh và toán tử luận lý.

Ba biến số nguyên tên là a, b và c được khai báo trong chương trình này. Các giá trị gán cho biến như sau: a = 5, b = 6 & c = 7.

Xét những dòng mã sau:

1. a + b >= c;

Ðầu tiên, a+b sẽ được tính (toán tử số học có độ ưu tiên cao hơn toán tử so sánh), kết quả là 11. Kế đến giá trị 11 được so sánh với c. Kết quả là 1(true) bởi vì 11 > 7.

2.Xét biểu thức khác:

a > 10 && b <5;

Tính toán đầu tiên sẽ là a> 10 và b<5, bởi vì toán tử so sánh (> <) có quyền ưu tiên cao hơn toán tử luận lý AND (&&). Tính toán theo sau:

1.5 > 10 && 6<5

2.FALSE && FALSE

3.FALSE tức là, 0

1.Tạo một tập tin mới.

2. Gõ đoạn mã sau vào 'Edit window':

#include <stdio.h>

#include <conio.h>

void main()

{

int a = 5, b = 6, c = 7;

printf ("int a = 5, b = 6, c = 7;

");

printf("The value of a > b is \t%i

", a > b);

printf("The value of b < c is \t%i

", b < c);

printf("The value of a + b >= c is \t%i

", a + b >= c);

printf("The value of a - b <= b - c is \t%i

", a-b <= b-c);

printf("The value of b-a == b - c is \t%i

", b - a == b - c);

printf("The value of a*b != c * c is \t%i

", a * b < c * c);

printf("Result of a>10 && b<5 = %d

", a>10 && b<5);

printf("Result of a > 100 || b < 50 = %d

", a>100 || b<50);

}

3. Lưu tập tin với tên compare.c.

4. Biên dịch tập tin compare.c.

5. Thực thi chương trình compare.c.

6. Trở về trình soạn thảo.

Kết quả xuất:

int a = 5, b = 6, c = 7;

The value of a > b is 0

The value of b < c is 1

The value of a + b >= c is 1

The value of a - b <= b - c is 1

The value of b - a == b - c is 0

The value of a * b != c * c is 1

Result of a > 10 && b < 5 = 0

Result of a > 100 || b < 50 = 1

5.4 Chuyển đổi kiểu dữ liệu

Trong phần này, ta sẽ viết một chương trình để hiểu rõ việc chuyển đổi kiểu dữ liệu.

Trong biểu thức đầu tiên, tất cả đều là số nguyên 'int' 40 / 17 * 13 / 3 sẽ có kết quả là 8 (40 / 17 làm tròn ra 2, 2 * 13 = 26, 26 / 3 làm tròn ra 8)

Biểu thức thứ hai như sau:

1.Ðịnh giá trị: 40 / 17 * 13 / 3.0

2.40 / 17 làm tròn kết quả là 2

3.2 * 13 = 26

4.Nhưng vì có số 3.0 đã ép kiểu phép chia cuối cùng thành số kiểu double, vì vậy 26.0 / 3.0 = 8.666667

Trong biểu thức thứ ba:

Nếu chúng ta di chuyển dấu chấm thập phân sang số 13 (40 / 17 * 13.0 / 3), kết quả vẫn sẽ là 8.666667 bởi vì:

1.40 / 17 làm tròn là 2

2.Số 13.0 ép kiểu dữ liệu phép nhân thành double nhưng kết quả vẫn là 26 vì 2.0 * 13.0 = 26.0

3.Và 26.0 ép phép chia cuối cùng thành kiểu double, vì vậy 26.0 / 3.0 = 8.666667

Trong biểu thức cuối:

nếu chúng ta di chuyển dấu chấm thập phân sang số 17 (40 / 17.0 * 13 / 3), kết quả bây giờ sẽ là 10.196078 bởi vì:

1.17.0 ép kiểu của phép chia đầu thành kiểu double và 40.0 / 17.0 = 2.352941

2.2.352941 * 13.0 = 30.588233

3.và 30.588233 / 3.0 = 10.196078

1. Tạo một tập tin mới.

2. Gõ đoạn mã sau vào 'Edit window':

#include <stdio.h>

#include <conio.h>

void main()

{

clrscr();

printf("40/17*13/3 = %d", 40/17*13/3);

printf("

40/17*13/3.0 = %lf", 40/17*13/3.0);

printf("

40/17*13.0/3 = %lf", 40/17*13.0/3);

printf("

40/17.0*13/3 = %lf", 40/17.0*13/3);

}

3. Lưu tập tin với tên type.c.

4. Biên dịch tập tin type.c.

5. Thực thi chương trình type.c.

6. Trở về trình soạn thảo.

Kết quả xuất:

40/17*13/3 = 8

40/17*13/3.0 = 8.666667

40/17*13.0/3 = 8.666667

40/17.0*13/3 = 10.196078

5.5 Thứ tự ưu tiên của các toán tử

Trong phần này chúng ta sẽ viết một chương trình để tìm hiểu thứ tự ưu tiên giữa các toán tử.

Biểu thức sau đây sẽ được tính như sau:

(4-2*9/6<=3 && (10*2/4-3>3|| (1<5 && 8>10)))

Hãy theo các quy tắc chúng ta đã học trong chương "Toán tử và Biểu thức" (Chú ý rằng biểu thức được in đậm dưới đây sẽ được tính trước)

1.(4-2*9/6<=3 && (10*2/4-3>3|| (1<5 && 8>10)))

2.(4-2*9/6<=3 && (10*2/4-3>3|| ( 1 && 0 )))

3.(4-2*9/6<=3 && (10*2/4-3>3|| 0))

4.(4-2*9/6<=3 && (20/4-3>3|| 0))

5.(4-2*9/6<=3 && (5-3>3|| 0))

6.(4-2*9/6<=3 && (2>3|| 0))

7.(4-2*9/6<=3 && (0|| 0))

8.(4-2*9/6<=3 && 0)

9.(4-18/6<=3 && 0)

10.(4-3<=3 && 0)

11.(1<=3 && 0)

12.( 1 && 0)

13.0 (False)

1. Tạo một tập tin mới.

2. Gõ đoạn mã sau vào cửa sổ soạn thảo:

#include <stdio.h>

#include <conio.h>

void main()

{

clrscr();

printf("Result = %d",(4-2*9/6<=3 && (10*2/4-3 > 3 ||

(1 < 5 && 8>10))));

}

3. Lưu tập tin với tên precede.c.

4. Biên dịch tập tin precede.c.

5. Thực thi chương trình precede.c.

6. Trở về trình soạn thảo.

Kết quả xuất:

Result = 0

Phần II - Trong thời gian 30 phút kế tiếp:

1. Tính giá trị biểu thức sau:

10 * 3 ^ 6 * 6 + 5 - 2 AND (2 * 2 + 6 /3 > 1 OR 2 >8)

Thực hiện như sau:

Gõ vào biểu thức trên sử dụng câu lệnh printf(). AND được thay thế bởi && và OR được thay thế bởi ||.

2. Giả sử tất cả biến có kiểu là int. Tìm giá trị cho mỗi biến sau:

a.x = (2 + 3) * 6;

b.x = (12 + 6) / 2 * 3;

c.y = x = (2 + 3) / 4;

d.y = 3 + 2 * (x = 7 / 2);

e.x = (int) 3.8 + 3.3;

f.x = (2 + 3) * 10.5;

g.x = 3 / 5 * 22.0;

h.x = 22.0 * 3 / 5;

Bài tập tự làm

1. Tính giá trị được gán (nằm phía bên trái) cho mỗi trường hợp sau:

int s, m = 3, n = 5, r, t;

float x = 3.0, y;

t = n/m;

r = n%m;

y = n/m;

t = x*y-m/2;

x = x*2.0;

s = (m+n)/r;

y = --n;

2. Viết một chương trình nhập vào một số thực. Ðơn vị tính cho số này là centimet (cm). Hãy in ra số tương đương tính bằng foot (số thực, có 1 số lẻ thập phân) và inch (số thực, có 1 số lẻ thập phân). Ðộ chính xác của foot và inch là một số lẻ thập phân.

Hướng dẫn: 2.54 centimeters = 1 inch, và 12 inches = 1 foot.

Nếu giá trị nhập vào là 333.3, kết quả là:

333.3 centimeters tương đương 10.9 feet.

333.3 centimeters tương đương 131.2 inches.

3. Tìm giá trị của iResult cho những câu lệnh sau:

int iResult, a = 10, b = 8, c = 6, d = 5, e = 2;

iResult = a - b - c - d;

iResult = a - b + c - d;

iResult = a + b / c / d;

iResult = a + b / c * d;

iResult = a / b * c * d;

iResult = a % b / c * d;

iResult = a % b % c % d;

iResult = a - (b - c) - d;

iResult = (a - (b - c)) - d;

iResult = a - ((b - c) - d);

iResult = a % (b % c) * d * e;

iResult = a + (b - c) * d - e;

iResult = (a + b) * c + d * e;

iResult = (a + b) * (c / d) % e;

Bài 6 Nhập và Xuất trong C

Mục tiêu:

Kết thúc bài học này, bạn có thể:

Hiểu các hàm nhập xuất có định dạng scanf() và printf()

Sử dụng các hàm nhập xuất ký tự getchar() và putchar().

Giới thiệu

Trong bất kỳ ngôn ngữ lập trình nào, việc nhập giá trị cho các biến và in chúng ra sau khi xử lý có thể đư­ợc làm theo hai cách:

1. Thông qua phương tiện nhập/xuất chuẩn (I / O).

2. Thông qua những tập tin.

Trong phần này ta sẽ nói về chức năng nhập và xuất cơ bản. Nhập và xuất (I/O) luôn là các thành phần quan trọng của bất kỳ chương trình nào. Ðể tạo tính hữu ích, chương trình của bạn cần có khả năng nhập dữ liệu vào và hiển thị lại những kết quả của nó.

Trong C, th­ư viện chuẩn cung cấp những thủ tục cho việc nhập và xuất. Th­ư viện chuẩn có những hàm quản lý các thao tác nhập/xuất cũng như các thao tác trên ký tự và chuỗi. Trong bài học này, tất cả những hàm nhập dùng để đọc dữ liệu vào từ thiết bị nhập chuẩn và tất cả những hàm xuất dùng để viết kết quả ra thiết bị xuất chuẩn. Thiết bị nhập chuẩn thông thường là bàn phím. Thiết bị xuất chuẩn thông thư­ờng là màn hình (console). Nhập và xuất ra có thể được định hướng đến tập tin hay từ tập tin thay vì thiết bị chuẩn. Những tập tin có thể được lưu trên đĩa hay trên bất cứ thiết bị lưu trữ nào khác. Dữ liệu đầu ra cũng có thể đư­ợc gửi đến máy in.

6.1 Tập tin tiêu đề <stdio.h>

Trong các ví dụ trước, ta đã từng viết dòng mã sau:

#include <stdio.h>

Ðây là lệnh tiền xử lý (preprocessor command). Trong C chuẩn, ký hiệu # nên đặt tại cột đầu tiên. stdio.h là một tập tin và được gọi là tập tin tiêu đề (header). Nó chứa các macro cho nhiều hàm nhập và xuất được dùng trong C. Hàm printf(), scanf(), putchar() và getchar() được thiết kế theo cách gọi các macro trong tập tin stdio.h để thực thi các công việc tương ứng.

6.2 Nhập và xuất trong C (Input and Output)

Thư viện chuẩn trong C cung cấp hai hàm để thực hiện các yêu cầu nhập và xuất có định dạng. Chúng là:

printf() - Hàm xuất có định dạng.

scanf() - Hàm nhập có định dạng.

Những hàm này gọi là những hàm được định dạng vì chúng có thể đọc và in dữ liệu ra theo các định dạng khác nhau được điều khiển bởi người dùng. Bộ định dạng qui định dạng thức mà theo đó giá trị của biến sẽ được nhập vào và in ra.

6.2.1 printf()

Chúng ta đã quen thuộc với hàm này qua các phần trước. Ở đây, chúng ta sẽ xem chúng chi tiết hơn. Hàm printf() được dùng để hiển thị dữ liệu trên thiết bị xuất chuẩn - console (màn hình). Dạng mẫu chung của hàm này như sau:

printf("control string", argument list);

Danh sách tham số (argument list) bao gồm các hằng, biến, biểu thức hay hàm và được phân cách bởi dấu phẩy. Cần phải có một lệnh định dạng nằm trong chuỗi điều khiển (control string) cho mỗi tham số trong danh sách. Những lệnh định dạng phải tương ứng với danh sách các tham số về số lượng, kiểu dữ liệu và thứ tự. Chuỗi điều khiển phải luôn được đặt bên trong cặp dấu nháy kép"", đây là dấu phân cách (delimiters). Chuỗi điều khiển chứa một hay nhiều hơn ba thành phần dưới đây :

Ký tự văn bản (Text characters) - Bao gồm các ký tự có thể in ra được và sẽ được in giống như ta nhìn thấy. Các khoảng trắng thường được dùng trong việc phân chia các trường (field) được xuất ra.

Lệnh định dạng - Định nghĩa cách thức các mục dữ liệu trong danh sách tham số sẽ được hiển thị. Một lệnh định dạng bắt đầu với một ký hiệu % và theo sau là một mã định dạng tương ứng cho mục dữ liệu. Dấu % được dùng trong hàm printf() để chỉ ra các đặc tả chuyển đổi. Các lệnh định dạng và các mục dữ liệu tương thích nhau theo thứ tự và kiểu từ trái sang phải. Một mã định dạng thì cần thiết cho mọi mục dữ liệu cần in ra.

Các ký tự không in được - Bao gồm phím tab, dấu khoảng trắng và dấu xuống dòng.

Mỗi lệnh định dạng gồm một hay nhiều mã định dạng. Một mã định dạng bao gồm ký hiệu % và một bộ định kiểu. Bảng 6.1 liệt kê các mã định dạng khác nhau được hỗ trợ bởi câu lệnh printf():

Ðịnh dạng

printf()

scanf()

Ký tự đơn (Single Character)

%c

%c

Chuỗi (String)

%s

%s

Số nguyên có dấu (Signed decimal integer)

%d

%d

Số thập phân có dấu chấm động (Floating point)

%f

%f hoặc %e

Số thập phân có dấu chấm động - Biểu diễn phần thập phân

%lf

%lf

Số thập phân có dấu chấm động - Biểu diễn dạng số mũ

%e

%f hoặc %e

Số thập phân có dấu chấm động (%f hoặc %e, con số nào ít hơn)

%g

Số nguyên không dấu (Unsigned decimal integer)

%u

%u

Số thập lục phân không dấu (Dùng "ABCDEF")

(Unsigned hexadecimal integer)

%x

%x

Số bát phân không dấu (Unsigned octal integer)

%o

%o

Bảng 6.1: Mã định dạng trong printf ()

Trong bảng trên, c, d, f, lf, e, g, u, s, o và x là bộ định kiểu.

Các quy ước in cho các mã định dạng khác nhau được tổng kết trong Bảng 6.2:

Mã định dạng

Quy ước in ấn

%d

Các con số trong số nguyên.

%f

Phần số nguyên của số sẽ được in nguyên dạng. Phần thập phân sẽ chứa 6 con số. Nếu phần thập phân của con số ít hơn 6 số, nó sẽ được thêm các số không (0) bên phải hay gọi là làm tròn phía bên phải.

%e

Một con số bên trái dấu chấm thập phân và 6 con số bên phải giống như %f.

Bảng 6.2: Quy ước in

Bởi vì các ký hiệu %,\ và " được dùng đặc biệt trong chuỗi điều khiển, nếu chúng ta cần in các ký hiệu này lên màn hình, chúng phải được dùng như trong Bảng 6.3:

\\

In ký tự \

\ "

In ký tự "

%%

In ký tự %

Bảng 6.3: Các ký tự đặc biệt trong chuỗi điều khiển

Bảng dưới đây đưa ra vài ví dụ sử dụng chuỗi điều khiển và mã định dạng khác nhau.

Số

Câu lệnh

Chuỗi điều khiển

Nội dung mà chuỗi điều khiển chứa đựng

Danh sách tham số

Giải thích danh sách tham số

Hiển thị trên màn hình

1.

printf("%d", 300);

%d

Chỉ chứa lệnh định dạng

300

Hằng số

300

2.

printf("%d", 10+5);

%d

Chỉ chứa lệnh định dạng

10 + 5

Biểu thức

15

3.

printf("Good Morning Mr. Lee.");

Good Morning Mr. Lee.

Chỉ là các ký tự văn bản

Không có (Nil)

Không có

Good Morning Mr. Lee.

4.

int count = 100;

printf("%d", count);

%d

Chỉ chứa lệnh định dạng

Count

Biến

100

5.

printf("

hello");

hello

Chỉ là các ký tự văn bản và ký tự không in được.

Không có

Không có

Hello

(Trên dòng mới)

6.

#define str "Good Apple"

........

printf("%s", str);

%s

Chỉ chứa lệnh định dạng

Str

Hằng chuỗi

Good Apple

7.

........

int count,stud_num;

count = 0;

stud_num = 100;

printf("%d %d

", count, stud_num);

%d %d

Chỉ chứa lệnh định dạng và trình tự thoát ra

count, stud_num

Hai biến

0, 100

Bảng 6.4 : Chuỗi điều khiển và mã định dạng

Ví dụ 6.1 :

Ðây là một chương trình đơn giản dùng minh họa cho một chuỗi có thể được in theo lệnh định dạng. Chương trình này cũng hiển thị một ký tự đơn, số nguyên và số thực (a single character, integer, và float).

#include <stdio.h>

void main()

{

int a = 10;

float b = 24.67892345;

char ch = 'A';

printf("

Integer data = %d", a);

printf("

Float Data = %f", b);

printf("

Character = %c", ch);

printf("

This prints the string");

printf("%s", "

This also prints a string");

}

Kết quả chương trình như sau:

Integer data = 10

Float Data = 24.678923

Character = A

This prints the string

This also prints a string

Bổ từ (Modifier) cho các lệnh định dạng trong printf()

Các lệnh định dạng có thể có bổ từ (modifier), để thay đổi các đặc tả chuyển đổi gốc. Sau đây là các bổ từ được chấp nhận trong câu lệnh printf(). Nếu có nhiều bổ từ được dùng thì chúng tuân theo trình tự sau :

Bổ từ '-'

Dữ liệu sẽ được canh trái bên trong không gian dành cho nó, chúng sẽ được in bắt đầu từ vị trí ngoài cùng bên trái.

Bổ từ xác định độ rộng

Chúng có thể được dùng với kiểu: float, double hay char array (chuỗi-string). Bổ từ xác định độ rộng là một số nguyên xác định độ rộng nhỏ nhất của trường dữ liệu. Các dữ liệu có độ rộng nhỏ hơn sẽ cho kết quả canh phải trong trường dữ liệu. Các dữ liệu có kích thước lớn hơn sẽ được in bằng cách dùng thêm những vị trí cho đủ yêu cầu.Ví dụ, %10f là lệnh định dạng cho các mục dữ liệu kiểu số thực với độ rộng trường dữ liệu thấp nhất là 10.

Bổ từ xác định độ chính xác

Chúng có thể được dùng với kiểu float, double hay mảng ký tự (char array, string). Bổ từ xác định độ rộng chính xác được viết dưới dạng .m với m là một số nguyên. Nếu sử dụng với kiểu float và double, chuỗi số chỉ ra số con số tối đa có thể được in ra phía bên phải dấu chấm thập phân.

Nếu phần phân số của các mục dữ liệu kiểu float hay double vượt quá độ rộng con số chỉ trong bổ từ, thì số đó sẽ được làm tròn. Nếu chiều dài chuỗi vượt quá chiều dài chỉ định thì chuỗi sẽ được cắt bỏ phần dư ra ở phía cuối. Một vài số không (0) sẽ được thêm vào nếu số con số thực sự trong một mục dữ liệu ít hơn được chỉ định trong bổ từ. Tương tự, các khoảng trắng sẽ được thêm vào cho chuỗi ký tự. Ví dụ, %10.3f là lệnh định dạng cho mục dữ liệu kiểu float, với độ rộng tối thiểu cho trường dữ liệu là 10 và 3 vị trí sau phần thập phân.

Bổ từ '0'

Theo mặc định, việc thêm vào một trường được thực hiện với các khoảng trắng. Nếu người dùng muốn thêm vào trường với số không (0), bổ từ này phải được dùng.

Bổ từ 'l'

Bổ từ này có thể được dùng để hiển thị số nguyên như: long int hay một tham số kiểu double. Mã định dạng tương ứng cho nó là %ld.

Bổ từ 'h'

Bổ từ này được dùng để hiện thị kiểu short integer. Mã định dạng tương ứng cho nó là %hd.

Bổ từ '*'

Bổ từ này được dùng khi người dùng không muốn chỉ trước độ rộng của trường mà muốn chương trình xác định nó. Nhưng khi đi với bổ từ này, một tham số được yêu cầu phải chỉ ra độ rộng trường cụ thể.

Chúng ta hãy xem những bổ từ này hoạt động thế nào. Ðầu tiên, chúng ta xem xét tác động của nó đối với những dữ liệu kiểu số nguyên.

Ví dụ 6.2:

/* Chương trình này trình bày cách dùng bổ từ trong printf() */

#include <stdio.h>

void main()

{

printf("The number 555 in various forms:

");

printf("Without any modifier:

");

printf("[%d]

", 555);

printf("With - modifier:

");

printf("[%-d]

", 555);

printf("With digit string 10 as modifier:

");

printf("[%10d]

", 555);

printf("With 0 as modifier:

");

printf("[%0d]

", 555);

printf("With 0 and digit string 10 as modifiers:

");

printf("[%010d]

", 555);

printf("With -, 0 and digit string 10 as modifiers:

");

printf("[%-010d]

", 555);

}

Kết quả như dưới đây:

The number 555 in various forms:

Without any modifier:

[555]

With - modifier:

[555]

With digit string 10 as modifier:

[ 555]

With 0 as modifier:

[555]

With 0 and digit string 10 as modifiers:

[0000000555]

With -, 0 and digit string 10 as modifiers:

[555 ]

Chúng ta đã dùng ký hiệu '[' và ']' để chỉ ra nơi trường bắt đầu và nơi kết thúc. Khi chúng ta dùng %d mà không có bổ từ, chúng ta thấy rằng nó dùng cho một trường có cùng độ rộng với số nguyên. Khi dùng %10d chúng ta thấy rằng nó dùng 10 khoảng trắng cho trường và số được canh lề phải theo mặc định. Nếu ta dùng bổ từ -, số sẽ được canh trái trong trường đó. Nếu dùng bổ từ 0, chúng ta thấy rằng số sẽ thêm vào 0 thay vì là khoảng trắng.

Bây giờ chúng ta hãy xem bổ từ dùng với số thực.

Ví dụ 6.3:

/* Chương trình này trình bày cách dùng bổ từ trong printf() */

#include <stdio.h>

void main()

{

printf("The number 555.55 in various forms:

");

printf("In float form without modifiers:

");

printf("[%f]

", 555.55);

printf("In exponential form without any modifier:

");

printf("[%e]

", 555.55);

printf("In float form with - modifier:

");

printf("[%-f]

", 555.55);

printf("In float form with digit string 10.3 as modifier

");

printf("[%10.3f]

", 555.55);

printf("In float form with 0 as modifier:

");

printf("[%0f]

", 555.55);

printf("In float form with 0 and digit string 10.3");

printf("as modifiers:

");

printf("[%010.3f]

", 555.55);

printf("In float form with -, 0 ");

printf("and digit string 10.3 as modifiers:

");

printf("[%-010.3f]

", 555.55);

printf("In exponential form with 0");

printf(" and digit string 10.3 as modifiers:

");

printf("[%010.3e]

", 555.55);

printf("In exponential form with -, 0");

printf(" and digit string 10.3 as modifiers:

");

printf("[%-010.3e]

", 555.55);

}

Kết quả như sau:

The number 555.55 in various forms:

In float form without modifiers:

[555.550000]

In exponential form without any modifier:

[5.555500e+02]

In float form with - modifier:

[555.550000]

In float form with digit string 10.3 as modifier

[ 555.550]

In float form with 0 as modifier:

[555.550000]

In float form with 0 and digit string 10.3 as modifiers:

[000555.550]

In float form with -, 0 and digit string 10.3 as modifiers:

[555.550 ]

In exponential form with 0 and digit string 10.3 as modifiers:

[05.555e+02]

In exponential form with -,0 and digit string 10.3 as modifiers:

[5.555e+02]

Theo mặc định cho %f, chúng ta có thể thấy rằng có 6 con số cho phần thập phân và mặc định cho %e là một con số tại phần nguyên và 6 con số phần bên phải dấu chấm thập phân. Chú ý cách thể hiện 2 số cuối cùng trong ví dụ trên, số các con số bên phải dấu chấm thập phân là 3, dẫn đến kết quả không được làm tròn.

Bây giờ, chúng ta hãy xem bổ từ dùng với chuỗi số. Chú ý cách mở rộng trường để chứa toàn bộ chuỗi. Hơn nữa, chú ý cách đặc tả độ chính xác .4 trong việc giới hạn số ký tự được in.

Ví dụ 6.4:

/* Chương trình trình bày cách dùng bổ từ với chuỗi*/

#include <stdio.h>

void main()

{

printf("A string in various forms:

");

printf("Without any format command:

");

printf("Good day Mr. Lee.

");

printf("With format command but without any modifier:

");

printf("[%s]

", "Good day Mr. Lee.");

printf("With digit string 4 as modifier:

");

printf("[%4s]

", "Good day Mr. Lee.");

printf("With digit string 19 as modifier:

");

printf("[%19s]

", "Good day Mr. Lee.");

printf("With digit string 23 as modifier:

");

printf("[%23s]

", "Good day Mr. Lee.");

printf("With digit string 25.4 as modifier:

");

printf("[%25.4s]

", "Good day Mr.Lee.");

printf("With - and digit string 25.4 as modifiers:

");

printf("[%-25.4s]

", "Good day Mr.shroff.");

}

Kết quả như sau:

A string in various forms:

Without any format command:

Good day Mr. Lee.

With format command but without any modifier:

[Good day Mr. Lee.]

With digit string 4 as modifier:

[Good day Mr. Lee.]

With digit string 19 as modifier:

[ Good day Mr. Lee.]

With digit string 23 as modifier:

[ Good day Mr. Lee.]

With digit string 25.4 as modifier:

[ Good]

With - and digit string 25.4 as modifiers:

[Good ]

Những ký tự ta nhập tại bàn phím không được lưu ở dạng các ký tự. Thật sự chúng lưu theo dạng các số dưới dạng mã ASCII (Bộ mã chuẩn Mỹ cho việc trao đổi thông tin - American Standard Code for Information Interchange). Các giá trị của một biến được thông dịch dưới dạng ký tự hay một số tùy vào kiểu của biến đó. Ví dụ sau mô tả điều này:

Ví dụ 6.5:

#include <stdio.h>

void main()

{

int a = 80;

char b= 'C';

printf("

This is the number stored in 'a' %d",a);

printf("

This is a character interpreted from 'a' %c",a);

printf("

This is also a character stored in 'b' %c",b);

printf("

Hey! The character of 'b' is printed as a number! %d", b);

}

Kết quả như dưới đây:

This is the number stored in 'a' 80

This is a character interpreted from 'a' P

This is also a character stored in 'b' C

Hey! The character of 'b' is printed as a number!67

Kết quả này mô tả việc dùng các đặc tả định dạng và việc thông dịch của mã ASCII. Mặc dù các biến a và b đã được khai báo là các biến kiểu int và char, nhưng chúng đã được in như là ký tự và số nhờ vào việc dùng các bộ định dạng khác nhau. Ðặc điểm này của C giúp việc xử lý dữ liệu được linh hoạt.

Khi dùng câu lệnh printf() để cho ra một chuỗi dài hơn 80 ký tự trên một dòng, khi xuống dòng ta phải ngắt mỗi dòng bởi ký hiệu \ như được trình bày trong ví dụ dưới đây:

Ví dụ 6.6:

/* Chương trình trình bày cách dùng một chuỗi dài các ký tự*/

#include <stdio.h>

void main()

{

printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\aaaaaaaaaaaaaaaaaaaaaaaaaaaaa");

}

Kết quả như sau:

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

aaaaaaaaaaaaa

Trong ví dụ trên, chuỗi trong câu lệnh printf() có 252 ký tự. Trong khi một dòng văn bản chứa 80 ký tự, do đó chuỗi được mở rộng thành 3 hàng trong kết quả như trên.

6.2.2 scanf()

Hàm scanf() đư­ợc sử dụng để nhập dữ liệu. Khuôn dạng chung của hàm scanf() như sau:

scanf(<Chuỗi các định dạng>, <Danh sách các tham số>);

Ðịnh dạng đ­ược sử dụng bên trong câu lệnh printf() cũng đư­ợc sử dụng cùng cú pháp trong các câu lệnh scanf().

Những lệnh định dạng, bao gồm bổ từ và danh sách tham số được bàn luận cho printf() thì cũng hợp lệ cho scanf(), chúng tuân theo một số điểm khác biệt sau:

Sự khác nhau trong danh sách tham số giữa printf() và scanf()

Hàm printf() dùng các tên biến, hằng số, hằng chuỗi và các biểu thức, nhưng scanf() sử dụng những con trỏ tới các biến. Một con trỏ tới một biến là một mục dữ liệu chứa đựng địa chỉ của nơi mà biến được cất giữ trong bộ nhớ. Những con trỏ sẽ đ­ược bàn luận chi tiết ở chương sau. Khi sử dụng scanf() cần tuân theo những quy tắc cho danh sách tham số:

Nếu ta muốn nhập giá trị cho một biến có kiểu dữ liệu cơ bản, gõ vào tên biến cùng với ký hiệu & trư­ớc nó.

Khi nhập giá trị cho một biến thuộc kiểu dữ liệu dẫn xuất (không phải thuộc bốn kiểu cơ bản char, int, float, double), không sử dụng & trư­ớc tên biến.

Sự khác nhau trong lệnh định dạng giữa printf() và scanf()

1.Không có tùy chọn %g.

2.Mã định dạng %f và %e có cùng hiệu quả tác động. Cả hai nhận một ký hiệu tùy chọn, một chuỗi các con số có hay không có dấu chấm thập phân và một trường số mũ tùy chọn.

Cách thức hoạt động của scanf()

scanf() sử dụng những ký tự không được in như ký tự khoảng trắng, ký tự phân cách (tab), ký tự xuống dòng để quyết định khi nào một trường nhập kết thúc và bắt đầu. Có sự tương ứng giữa lệnh định dạng với những trường trong danh sách tham số theo một thứ tự xác định, bỏ qua những ký tự khoảng trắng bên trong. Do đó, đầu vào có thể được trải ra hơn một dòng, miễn là chúng ta có ít nhất một ký tự phân cách, khoảng trắng hay hàng mới giữa các trường nhập vào. Nó bỏ qua những khoảng trắng và ranh giới hàng để thu đư­ợc dữ liệu.

Ví dụ 6.7:

Chương trình sau mô tả việc dùng hàm scanf().

#include <stdio.h>

void main()

{

int a;

float d;

char ch, name[40];

printf("Please enter the data

");

scanf("%d %f %c %s", &a, &d, &ch, name);

printf("

The values accepted are: %d, %f, %c, %s", a, d, ch, name);

}

Kết quả như sau:

Please enter the data

12 67.9 F MARK

The values accepted are:12, 67.900002, F, MARK

Dữ liệu đầu vào có thể là:

12 67.9

F MARK

hoặc như:

12

67.9

F

MARK

cũng được nhận vào các biến a, d, ch, và name.

Xem ví dụ khác:

Ví dụ 6.8:

#include <stdio.h>

void main()

{

float x;

char c;

.........

scanf("%3d %5f %c", &i, &x, &c);

}

Nếu dữ liệu nhập vào là:

21 10.345 F

Khi chư­ơng trình được thực thi, thì 21 sẽ gán tới i, 10.34 sẽ gán tới x và ký tự 5 sẽ đư­ợc gán cho c. Còn lại là đặc tính F sẽ bị bỏ qua.

Khi ta chỉ rõ một chiều rộng trường bên trong scanf(), thí dụ %10s, rồi sau đó scanf() chỉ thu nhận tối đa 10 ký tự hoặc tới ký tự khoảng trắng đầu tiên (bất cứ ký tự nào đầu tiên). Ðiều này cũng áp dụng cho các kiểu int, float và double.

Ví dụ d­ưới đây mô tả việc sử dụng hàm scanf() để nhập vào một chuỗi gồm có những ký tự viết hoa và khoảng trắng. Chuỗi sẽ có chiều dài không xác định nhưng nó bị giới hạn trong 79 ký tự (thật ra, 80 ký tự bao gồm ký tự trống (null) đư­ợc thêm vào nơi cuối chuỗi).

Ví dụ 6.9:

#include <stdio.h>

void main()

{

char line[80]; /* line[80] là một mảng lưu 80 ký tự */

..........

scanf("%[ ABCDEFGHIJKLMNOPQRSTUVWXYZ]", line);

..........

}

Mã khuôn dạng %[] có nghĩa những ký tự đư­ợc định nghĩa bên trong [] có thể đ­ược chấp nhận như­ những ký tự chuỗi hợp lệ. Nếu chuỗi BEIJING CITY đư­ợc nhập vào từ thiết bị nhập chuẩn, khi chương trình được thực thi, toàn bộ chuỗi sẽ đư­ợc gán cho mảng một khi chuỗi chỉ toàn là ký tự viết hoa và khoảng trắng. Nếu chuỗi đư­ợc viết là­ Beijing city, chỉ ký tự đơn­ B đư­ợc gán cho mảng, khi đó thì ký tự viết thường đầu tiên (trong trư­ờng hợp này là 'e') đ­ược thông dịch như ký tự đầu tiên bên ngoài chuỗi.

Ðể chấp nhận bất kỳ ký tự nào đến khi gặp ký tự xuống dòng, chúng ta sử dụng mã định dạng %[^

], điều này ngụ ý rằng chuỗi đó sẽ chấp nhận bất kỳ ký tự nào trừ "

" (ký tự xuống dòng). Dấu mũ (^) ngụ ý rằng tất cả các ký tự trừ những ký tự nằm sau dấu mũ đó sẽ đư­ợc chấp nhận nh­ư ký tự hợp lệ.

Ví dụ 6.10:

#include <stdio.h>

void main()

{

char line[80];

.................

scanf("%[^

]", line);

...........

}

Khi hàm scanf() được thực thi, một chuỗi có chiều dài không xác định (nhưng không quá 79 ký tự) sẽ được nhập vào từ thiết bị nhập chuẩn và được gán cho mảng. Sẽ không có giới hạn nào trên các ký tự của chuỗi, ngoại trừ tất cả chúng chỉ nằm trên một hàng. Ví dụ chuỗi sau:

All's well that ends well!

Có thể được nhập vào từ bàn phím và được gán cho mảng.

Bổ từ * cho kết quả khác nhau trong scanf(). Dấu * được dùng để chỉ rằng một trường sẽ được bỏ qua luôn hay tạm bỏ qua.

Ví dụ xét chương trình:

#include <stdio.h>

void main()

{

char item[20];

int partno;

float cost;

.........

scanf("%s %*d %f", item, &partno, &cost);

.........

}

Nếu các mục dữ liệu tương ứng là:

battery 12345 0.05

thì battery sẽ được gán cho item và 0.05 sẽ được gán cho cost nhưng 12345 sẽ không được gán cho partno bởi vì dấu * ngăn chặn việc gán.

Bất cứ ký tự khác trong scanf() mà không là mã định dạng trong chuỗi điều khiển phải được nhập vào chính xác nếu không sẽ phát sinh lỗi. Ðặc điểm này được dùng để chấp nhận dấu phân cách phẩy (,).

Ví dụ chuỗi dữ liệu

10, 15, 17

và lệnh nhập vào

scanf("%d, %f, %c", &intgr, &flt, &ch);

Chú ý rằng dấu phẩy trong chuỗi chuyển đổi tương ứng dấu phẩy trong chuỗi nhập và vì vậy nó sẽ có chức năng như dấu phân cách.

Ký tự khoảng trắng trong chuỗi điều khiển thường được bỏ qua mặc dù nó sẽ phát sinh trở ngại khi dùng với mã định dạng %c. Nếu chúng ta dùng bộ định dạng %c thì một khoảng trắng được xem như là một ký tự hợp lệ.

Xét đoạn mã sau:

int x, y;

char ch;

scanf("%2d %c %d",&x, &ch, &y);

printf("%d %d %d

",x, ch, y);

ta nhập vào:

14 c 5

14 sẽ được gán cho x, ký tự ch nhận ký tự khoảng trắng (số 32 trong hệ thập phân), do vậy y được gán giá trị của ký tự 'c' tức là số 99 trong hệ thập phân.

Xét đoạn mã sau:

#include <stdio.h>

void main()

{

char c1, c2, c3;

..............

scanf("%c%c%c",&c1, &c2, &c3);

....................

}

Nếu dữ liệu nhập vào là:

a b c

(với khoảng trắng giữa các ký tự), thì kết quả của phép gán:

c1 = a, c2 = <Khoảng trắng>, c3 = b

Ở đây chúng ta có thể thấy c2 chứa một khoảng trắng vì chuỗi nhập có chứa ký tự khoảng trắng. Ðể bỏ qua các ký tự khoảng trắng này và đọc ký tự tiếp theo không phải là ký tự khoảng trắng, ta nên dùng tập chuyển đổi %1s.

scanf("%c%1s%1s",&c1, &c2, &c3);

Khi đó kết quả sẽ khác đi với cùng dữ liệu nhập vào như trước và kết quả đúng như ý định của ta:

c1 = a, c2 = b, c3 = c

6.3 Bộ nhớ đệm Nhập và Xuất (Buffered I/O)

Ngôn ngữ C bản thân nó không định nghĩa các thao tác nhập và xuất. Tất cả thao tác nhập và xuất được thực hiện bởi các hàm có sẵn trong thư viện hàm của C. Thư viện hàm C chứa một hệ thống hàm riêng mà nó điều khiển các thao tác này. Ðó là:

Bộ nhớ đệm Nhập và Xuất - được dùng để đọc và viết các ký tự ASCII

Một vùng đệm là nơi lưu trữ tạm thời, nằm trên bộ nhớ máy tính hoặc trên thẻ nhớ của bộ điều khiển thiết bị (controller card). Các ký tự nhập vào từ bàn phím được đưa vào bộ nhớ và đợi đến khi người dùng nhấn phím return hay enter thì chúng sẽ được thu nhận như một khối và cung cấp cho chương trình.

Bộ nhớ đệm nhập và xuất có thể được phân thành:

Thiết bị nhập/xuất chuẩn (Console I/O)

Tập tin đệm nhập/xuất (Buffered File I/O)

Thiết bị nhập/xuất chuẩn liên quan đến những hoạt động của bàn phím và màn hình của máy tính. Tập tin đệm nhập/xuất liên quan đến những hoạt động thực hiện đọc và viết dữ liệu vào tập tin. Chúng ta sẽ nói về Thiết bị nhập/xuất.

Trong C, Thiết bị nhập/xuất chuẩn là một thiết bị luồng. Các hàm trong Thiết bị nhập/xuất chuẩn hướng các thao tác đến thiết bị nhập và xuất chuẩn của hệ thống.

Các hàm đơn giản nhất của Thiết bị nhập/xuất chuẩn là:

getchar() - Ðọc một và chỉ một ký tự từ bàn phím.

putchar() - Xuất một ký tự đơn ra màn hình.

6.3.1 getchar()

Hàm getchar() được dùng để đọc dữ liệu nhập vào, chỉ một ký tự tại một thời điểm từ bàn phím.Trong hầu hết việc thực thi của C, khi dùng getchar(), các ký tự nằm trong vùng đệm cho đến khi người dùng nhấn phím xuống dòng. Vì vậy nó sẽ đợi cho đến khi phím Enter được gõ. Hàm getchar() không có tham số, nhưng vẫn phải có cặp dấu ngoặc đơn. Nó đơn giản lấy về ký tự tiếp theo và sẵn sàng đưa ra cho chương trình. Chúng ta nói rằng hàm này trả về một giá trị có kiểu ký tự.

Chương trình sau trình bày cách dùng hàm getchar().

Ví dụ 6.11:

/* Chương trình trình bày cách dùng getchar() */

#include <stdio.h>

void main()

{

char letter;

printf("

Please enter any character: ");

letter = getchar();

printf("

The character entered by you is %c. ", letter);

}

Kết quả như sau:

Please enter any character: S

The character entered by you is S.

Trong chương trình trên 'letter' là một biến được khai báo là kiểu char do vậy nó sẽ nhận vào ký tự.

Một thông báo:

Please enter any character:

sẽ xuất hiện trên màn hình. Ta nhập vào một ký tự, trong ví dụ là S, qua bàn phím và nhấn Enter. Hàm getchar() nhận ký tự đó và gán cho biến có tên là letter. Sau đó nó được hiển thị trên màn hình và ta có được thông báo.

The character entered by you is S.

6.3.2 putchar()

putchar() là hàm xuất ký tự trong C, nó sẽ xuất một ký tự lên màn hình tại vị trí con trỏ màn hình. Hàm này yêu cầu một tham số. Tham số của hàm putchar() có thể thuộc các loại sau:

Hằng ký tự đơn

Ðịnh dạng (Escape sequence)

Một biến ký tự.

Nếu tham số là một hằng nó phải được bao đóng trong dấu nháy đơn. Bảng 6.5 trình bày vài tùy chọn cho putchar() và tác động của chúng.

Tham số

Hàm

Tác dụng

Biến ký tự

putchar(c)

Hiện thị nội dung của biến ký tự c

Hằng biến ký tự

putchar('A')

Hiển thị ký tự A

Hằng số

putchar('5')

Hiển thị con số 5

Ðịnh dạng (escape sequence)

putchar('\t')

Chèn một ký tự khoảng cách (tab) tại vị trí con trỏ màn hình

Ðịnh dạng (escape sequence)

putchar('

')

Chèn một mã xuống dòng tại vị trí con trỏ màn hình

Bảng 6.5: Những tùy chọn cho putchar() và tác dụng của chúng

Chương trình sau trình bày về hàm putchar():

Ví dụ 6.12:

/* Chương trình này trình bày việc sử dụng hằng và định dạng trong hàm putchar() */

#include <stdio.h>

void main()

{

putchar('H'); putchar('

');

putchar('\t');

putchar('E'); putchar('

');

putchar('\t'); putchar('\t');

putchar('L'); putchar('

');

putchar('\t'); putchar('\t'); putchar('\t');

putchar('L'); putchar('

');

putchar('\t'); putchar('\t'); putchar('\t');

putchar('\t');

putchar('O');

}

Kết quả như sau:

H

E

L

L

O

Khác nhau giữa getchar() và putchar() là putchar() yêu cầu một tham số trong khi getchar() thì không.

Ví dụ 6.13:

/* Chương trình trình bày getchar() và putchar() */

#include <stdio.h>

void main()

{

char letter;

printf("You can enter a character now: ");

letter = getchar();

putchar(letter);

}

Kết quả như sau:

You can enter a character now: F

F

Tóm tắt bài học

Trong C, Nhập và Xuất được thực hiện bằng cách dùng các hàm. Bất cứ chương trình nào trong C đều có quyền truy cập tới ba tập tin chuẩn. Chúng là tập tin nhập chuẩn (stdin), tập tin xuất chuẩn (stdout) và bộ lỗi chuẩn (stderr). Thông thường tập tin nhập chuẩn là bàn phím (keyboard), tập tin xuất chuẩn là màn hình (screen) và tập tin lỗi chuẩn cũng là màn hình.

Tập tin tiêu đề <stdio.h> chứa các macro của nhiều hàm nhập và xuất (input/output function) được dùng trong C.

Thiết bị nhập/xuất chuẩn (Console I/O) liên quan đến những hoạt động của bàn phím và màn hình của máy tính. Nó chứa các hàm định dạng và không định dạng.

Hàm nhập xuất định dạng là printf() và scanf().

Hàm nhập xuất không định dạng là getchar() và putchar().

Hàm scanf() được dùng cho dữ liệu nhập vào có định dạng, trong khi hàm printf() được dùng để xuất ra dữ liệu theo một định dạng cụ thể.

Chuỗi điều khiển của printf() và scanf() phải luôn tồn tại bên trong dấu nháy kép "". Chuỗi này sẽ chứa một tập các lệnh định dạng. Mỗi lệnh định dạng chứa ký hiệu %, một tùy chọn các bổ từ và các dạng kiểu dữ liệu xác định.

Sự khác nhau chính giữa printf() và scanf() là hàm scanf() dùng địa chỉ của biến chứ không phải là tên biến.

Hàm getchar() đọc một ký tự từ bàn phím.

Hàm putchar(ch) gởi ký tự ch ra màn hình.

Sự khác nhau giữa getchar() và putchar() là putchar() có một tham số trong khi getchar() thì không.

Kiểm tra tiến độ học tập

1.Các hàm nhập và xuất có định dạng là _________ và ________.

A. printf() và scanf()

B. getchar() và putchar()

C. puts() và gets()

D. Không câu nào đúng

2.Hàm scanf() dùng _________ tới các biến chứ không dùng tên biến.

A. Hàm

B. Con trỏ

C. Mảng

D. Không câu nào đúng

3.___________ xác định định dạng cho các giá trị của biến sẽ được nhập và in.

A. Văn bản

B. Bộ định dạng

C. Tham số

D. Không câu nào đúng

4._______ được dùng bởi hàm printf() để xác định các đặc tả chuyển đổi.

A. %

B. &

C. *

D. Không câu nào đúng

5.getchar() là một hàm không có bất cứ tham số nào. (True/False)

6.Một ___________ là một nơi lưu trữ tạm trong bộ nhớ.

A. ROM (Bộ nhớ chỉ đọc)

B. Thanh ghi

C. Vùng đệm

D. Không câu nào đúng

7.Ðịnh dạng (Escape sequence) có thể được đặt bên ngoài chuỗi điều khiển của printf().

(True/False)

Bài tập tự làm

1. A. Hãy dùng câu lệnh printf() để :

a)Xuất ra giá trị của biến số nguyên sum.

b)Xuất ra chuỗi văn bản "Welcome", tiếp theo là một dòng mới.

c)Xuất ra biến ký tự letter.

d)Xuất ra biến số thực discount.

e)Xuất ra biến số thực dump có 2 vị trí phần thập phân.

1. B. Dùng câu lệnh scanf() và thực hiện:

a) Ðọc giá trị thập phân từ bàn phím vào biến số nguyên sum.

b) Ðọc một giá trị số thực vào biến discount_rate.

2Viết một chương trình xuất ra giá trị ASCII của các ký tự 'A' và 'b'.

3.Xét chương trình sau:

#include <stdio.h>

void main()

{

int breadth;

float length, height;

scanf("%d%f%6.2f", breadth, &length, height);

printf("%d %f %e", &breadth, length, height);

}

Sửa lỗi chương trình trên.

4.Viết một chương trình nhập vào name, basic, daper (phần trăm của D.A), bonper (phần trăm lợi tức) và loandet (tiền vay bị khấu trừ) cho một nhân viên. Tính lương như sau:

salary = basic + basic * daper/100 + bonper * basic/100 - loandet

Bảng dữ liệu:

name

basic

daper

loandet

MARK

2500

55

33.33

250.00

Tính salary và xuất ra kết quả dưới các đầu đề sau (Lương được in ra gần dấu đôla ($)):

Name Basic Salary

Viết một chương trình yêu cầu nhập vào tên, họ của bạn và sau đó xuất ra tên, họ theo dạng là họ, tên.

Bạn đang đọc truyện trên: AzTruyen.Top

Tags: #vqg