175
1 Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụn Một hệ thống bao gồm phần cứng, phần mềm và phần sụn, tất cả được ghép nối nhau. Phần cứng là những phần của hệ thống mà ta có thể tiếp xúc đến, thí dụ như các linh kiện, ốc vít và đai ốc, vỏ máy, các dây dẫn điện .vv… Phần mềm là các chương trình chạy trên phần cứng lập trình được và sự thay đổi hoạt động của chúng phụ thuộc vào các đầu vào của hệ thống. Các đầu vào này có thể được lựa chọn từ một bàn phím phần cứng ghép nối hoặc từ một thiết bị ngoại vi. Chương trình không thể tồn tại mà không có một vài dạng phần cứng lập trình được như một bộ vi xử lý hoặc một bộ điều khiển. Phần sụn là một thiết bị phần cứng lập trình được bằng cách sử dụng phần mềm. Thiết bị có thể xem như phần sụn tiêu biểu là các bộ EEPROM (bộ nhớ chỉ đọc có thể lập trình được hoặc xóa được bằng điện) và các thiết bị ghép nối, được lập trình bằng cách sử dụng các thanh ghi. Trong hầu hết các ứng dụng, phần cứng hoạt động chuyên dụng hoạt động nhanh hơn phần cứng hoạt động kết hợp với phần mềm, mặc dù các hệ thống chạy phần mềm chương trình có khẳ năng dễ dàng sử đổi và đòi hỏi thới gian nghiên cứu và phát triển ít hơn. 1.2. Lịch sử ngôn ngữ C Dennis Ritchie phát triển ngông ngữ C lần đầu tiên tại phòng thí nghiệm Bell của công ty AT & T (Hoa kỳ). Mục tiêu ông ta đặt ra khi đó là sử dụng một ngôn ngữ bậc cao để xây dựng hệ điều hành UNIX theo một phương châm dễ dàng trong việc bảo trì, hiệu quả và dễ di chuyển (portable). Để đạt được mục đích này ông đã thiết kế một ngôn ngữ có nhiều ảnh hưởng, quan trong nhất là ngôn ngữ BCPL. Ảnh hưởng của ngôn ngữ này lên C được tiếp tục một cách gián tiếp thông qua ngôn ngữ B. Thoạt đầu ngôn ngữ C được ứng dụng chủ yếu trong lập trình hệ thống. Các chương trình dịch, các hệ điều hành và các tiện ích đều được viết bằng ngôn ngữ C. Ngôn ngữ này tỏ ra ưu việt hơn trong lĩnh vực này và ngày nay đã được ứng dụng trong nhiều lĩnh vực và các hệ thống máy tính khác nhau. Sau khi được chấp nhận, không giống như các ngôn ngữ khác, ngôn ngữ C đã có xu hướng hình thành những nhóm tách biệt.

Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

Embed Size (px)

Citation preview

Page 1: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

1

Chương 1 Mở đầu

1.1. Phần cứng, phần mềm, và sụn

Một hệ thống bao gồm phần cứng, phần mềm và phần sụn, tất cả được ghép nối

nhau. Phần cứng là những phần của hệ thống mà ta có thể tiếp xúc đến, thí dụ như các

linh kiện, ốc vít và đai ốc, vỏ máy, các dây dẫn điện .vv… Phần mềm là các chương

trình chạy trên phần cứng lập trình được và sự thay đổi hoạt động của chúng phụ thuộc

vào các đầu vào của hệ thống. Các đầu vào này có thể được lựa chọn từ một bàn phím

phần cứng ghép nối hoặc từ một thiết bị ngoại vi. Chương trình không thể tồn tại mà

không có một vài dạng phần cứng lập trình được như một bộ vi xử lý hoặc một bộ điều

khiển. Phần sụn là một thiết bị phần cứng lập trình được bằng cách sử dụng phần mềm.

Thiết bị có thể xem như phần sụn tiêu biểu là các bộ EEPROM (bộ nhớ chỉ đọc có thể

lập trình được hoặc xóa được bằng điện) và các thiết bị ghép nối, được lập trình bằng

cách sử dụng các thanh ghi. Trong hầu hết các ứng dụng, phần cứng hoạt động chuyên

dụng hoạt động nhanh hơn phần cứng hoạt động kết hợp với phần mềm, mặc dù các hệ

thống chạy phần mềm chương trình có khẳ năng dễ dàng sử đổi và đòi hỏi thới gian

nghiên cứu và phát triển ít hơn.

1.2. Lịch sử ngôn ngữ C

Dennis Ritchie phát triển ngông ngữ C lần đầu tiên tại phòng thí nghiệm Bell của

công ty AT & T (Hoa kỳ). Mục tiêu ông ta đặt ra khi đó là sử dụng một ngôn ngữ bậc

cao để xây dựng hệ điều hành UNIX theo một phương châm dễ dàng trong việc bảo trì,

hiệu quả và dễ di chuyển (portable). Để đạt được mục đích này ông đã thiết kế một

ngôn ngữ có nhiều ảnh hưởng, quan trong nhất là ngôn ngữ BCPL. Ảnh hưởng của

ngôn ngữ này lên C được tiếp tục một cách gián tiếp thông qua ngôn ngữ B.

Thoạt đầu ngôn ngữ C được ứng dụng chủ yếu trong lập trình hệ thống. Các chương

trình dịch, các hệ điều hành và các tiện ích đều được viết bằng ngôn ngữ C. Ngôn ngữ

này tỏ ra ưu việt hơn trong lĩnh vực này và ngày nay đã được ứng dụng trong nhiều

lĩnh vực và các hệ thống máy tính khác nhau. Sau khi được chấp nhận, không giống

như các ngôn ngữ khác, ngôn ngữ C đã có xu hướng hình thành những nhóm tách biệt.

Page 2: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

2

Tới năm 1983, ngôn ngữ này đã phát triển đến giai đoạn cần có biện pháp để tiêu

chuẩn hóa. Để đạt được mục đích này ủy ban X3J11 của ANSI (Viện tiêu chuẩn quốc

gia hoa kỳ) được thành lập để chuẩn hóa ngôn ngữ này và xây dựng lên tiêu chuẩn

ANSC-C. Hầu hết các sản phẩm C hiện nay đều tuân theo tiêu chuẩn này mà không

theo những quy định lúc ban đầu.

1.3. Sơ lược về cấu trúc máy tính

Các phần chính của máy tính cơ bản là một đơn vị xử lý trung tâm (hoặc thường

gọi là bộ vi xử lý), bộ nhớ và các mạch vào ra. Các phần tử đó được nối với nhau qua

đường bus chính: bus địa chỉ, bus điều khiển và bus dữ liệu. Hình 1.1. mô tả một hệ

thống cơ bản. Các các thiết bị ngoại vi như bàn phím, màn hình, ổ đĩa, có thể nối trực

tiếp với các bus: dữ liệu, địa chỉ và điều khiển, hoặc ghép với các mạch vào ra.

Hìn

h

1 - 1

:

Sơ đồ khối một hệ thống máy tính đơn giản

Thông thường bộ nhớ bao gồm: bộ nhớ truy cập ngẫu nhiên (RAM: Ramdom

Acesses Memory) và bộ nhớ chỉ đọc ROM (ROM: Read only Memory). Bố nhớ ROM

cất dữ thông tin để sử dụng lâu dài, trong khi bộ nhớ truy cập ngẫu nhiên không phải là

bộ nhớ dữ liệu để sử dụng lâu dài và dữ liệu sẽ mất đi khi ngắt nguồn nuôn.

Bộ RAM được sử dụng để chạy các chương trình ứng dụng và cất giữ thông ti một các

tạm thời.

Bộ vi xử lý là bộ điều khiển chính của máy tính. Nó đem về từ bộ nhớ các lệnh

nhị phân, rồi giải mã các lệnh đó thành một dãy các tác động đơn giản và thực hiện các

Bus dữ liệu

Bus địa chỉ

Bus điều

Bộ nhớ (RAM hoặc

ROM)

CPU hoặcMicro-proc

essorMạch giao

Page 3: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

3

tác động theo các bước kế tiếp nhau. Các bước này được đồng bộ hóa nhờ một đồng hồ

hệ thống. Để truy cập lên một vị trí trong bộ nhớ, hay thường gọi là ô nhớ, bộ vi xử lý

đặt địa chỉ ô nhớ lên bus địa chỉ. Nội dụng của địa chỉ này được đặt lên bus dữ liệu và

bộ vi xử lý đọc dữ liệu từ bus dữ liệu. Để cất dữ liệu vào bộ nhớ, bộ vi xử lý đặt dữ

liệu lên bus dữ liệu. Sau đó, địa chỉ của ô nhớ trong bộ nhớ được đặt lên bus địa chỉ và

dữ liệu lại được đọc từ bus dữ liệu và địa chỉ bộ nhớ cần có.

1.4. Biên dịch, liên kết, và viết một chương trình chấp hành

Bộ vi xử lý chỉ hiểu được thông tin dưới dạng nhị phân và thao tác trên một dãy

các lệnh nhị phân hay thường gọi là mã máy. Khi viết chương trình lớn trực tiếp dưới

dạng mã máy sẽ gặp rất nhiều khó khăn, nên các ngôn ngữ bậc cao đã được phát triển

để dẽ sử dụng thay mã máy. Một ngôn ngữ bậc thấp rất giống với mã máy và thường

kéo theo việc sử dụng các macro từ khóa để thay thế cho các chỉ thị mã máy. Ngôn ngữ

bậc cao có cú pháp gần như viết tiếng Anh và như vậy làm cho chương trình càng trở

lên đẽ đọc và dễ sửa đổi. Trong đó, các chương trình hoạt động thực tại của của phần

cứng như thế nào, người viết chương trình không thể nhìn thấy được. Khi đó một

chương trình dịch sẽ chuyển chương trình ngôn ngữ bậc cao ra mã máy. Các ngôn

ngưc bậc cao có thể kết ra là: C, Basic, COBOL, FORTRAN và PASCAL. Một ví dụ

về ngôn ngữ bậc thấp là hợp ngữ (Assembly) dung cho 80386.

Hình 1-2 giới thiệu các công đoạn để tạo ra một ngôn ngữ mã máy từ một chương

trình mã nguồn trong ngôn ngữ C.

Hình 1-2: Các quá trình soạn thảo, biên dịch và liên kết

Tệp chấp

Tệp mã đốiTệp mã

Các thư viện vàmã đối tượng

khác

Bộ liên kết(Thêm vào các

thông tin bổ

Trình biên dịch(Biến đổi mã

nguồn thành mã

Bộ soạn thảo(tạo và sửa

đổi mã

Page 4: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

4

Bộ soạn thảo dùng để viết và sửa đổi một tệp mã nguồn C; Sau đó một chương

trình dịch sẽ chuyển đổi mã nguồn này sang một dạng mã mà bộ vi xử lý có thể hiểu

được, cụ thể là mã máy. Tệp tin do chương trình dịch tạo ra được đặt tên là tệp mã đối

tượng. Tệp tin này không thể thực hiện được vì nó không có đầy đủ các thông tin cần

có để chạy một chương trình. Giai đoạn cuối cùng của quá trình là liên kết; qua trình

này đòi hỏi phải đưa thêm mã máy vào chương trình để nó có thể sử dụng các thiết bị

như bàn phím, màn hình,… Một bộ liên kết sẽ kết nối tệp mã đối tượng với các tệp mã

đối tượng khác và với các thư viện để tạo ra một chương trình chấp hành được (thực

thi được). Các thư viện này chứa các module mã đối tượng khác đã được biên dịch

sang mã nguồn.

Nếu quá trình dịch hoặc liên kết phát sinh lỗi hoặc những cảnh báo thì mã nguồn

phải được sửa đổi để loại trừ các lỗi. Quá trình biên dịch/liên kiết phải được tiến hành

một lần nữa. Các thông báo trong quá trình dịch hoặc liên kết không làm dừng chương

trình hoặc bộ kết nối để tránh tạo ra một đầu ra, nhưng các lỗi thì sẽ làm ngừng. Vì

vậy, tất cả các lỗi trong quá trình biên dịch hoặc liên kết phải được loại bỏ, trong khi

đối với các lời cảnh báo thì yêu cầu đặt ra chỉ là lên loại bỏ.

Turbo C phiên bản 2.0 là một gói phần mềm phát triển được tích hợp để thích hợp với

các hệ thống nhúng trên cơ sở máy tính cá nhận. Nó bao gồm: bộ soạn thảo, chương

trình dịch, bộ liên kết và trình gỡ rối (được sử dụng để chạy thử chương trình). Bộ khởi

động được dùng để viết và sửa đổi tệp mã nguồn và được khởi động bằng cách thay

cho tệp chấp hành TC.EXE.

1.5. Ứng dụng của ngôn ngữ C trong lĩnh vực điện tử - viễn thông

Ngôn ngữ C có đặc tính ưu việt hơn các ngôn ngữ lập trình khác là nó gần với mã

máy, do đó tất cả các ứng dụng được viết bằng C đều chạy rất nhanh hơn khi viết trên

cac ngôn ngữ lập trình bậc cao khác và được sử dụng rất rộng rãi.

Trong lĩnh vực điện tử viễn thông C được sử dụng để lập trình điều khiển ghép nối với

máy tính, lập trình cho vi điều khiển, lập trình các hệ thống nhúng, lập trình hệ thống,

và lập trình mạng,…

Page 5: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

5

Chương 2 CÁC KHÁI NIỆM CƠ BẢN

2.1. Tập ký tự dung trong ngôn ngữ C

Mọi ngôn ngữ lập trình đều được xây dựng từ một bộ ký tự nào đó. Các kí tự

này được nhóm lại với nhau nhiều cách để lập lên các từ. Các từ lại liên kết với nhau

theo một quy tắc nào đó để tạo lên câu lệnh. Một chương trình bao gồm nhiều câu lệnh

và diễn đạt một thuật toán để giải một bài toán. Ngôn ngữ C được xây dựng dựa trên

bộ ký tự sau:

26 chữ cái hoa: A B C… Z

26 chữ cái thường: a b c … z

10 chữ số: 0 1 2 … 9

Các kí tự toán học như: + - * / = ( )

Ký tự gạch nối: _

Các ký hiệu đặc biệt khác nhau như: . , : ; {} ? ! \ & | % # $ …

Dấu cách dung để tách các từ

Khi viết chương trình không được sử dụng bất kỳ kí hiệu nào khác ngoài tập kí hiệu

nói trên.

Ví dụ: Khi giải một phương trình bậc hai:

ax2 + bx + c =0

cần tính biệt thức:

∆ = b2 -4ac

Kí tự ∆ không cho phép dung trong ngôn ngữ C, vìa vậy ta phải dùng một kí hiệu khác

như d hoặc delta.

2.2. Từ khóa

Từ khóa là những từ có một ý nghĩa hoàn toàn xác định. Chúng được sử dụng để khai

báo các kiểu dữ liệu, để viết các toán tử và các câu lệnh. Sau đây là các từ khóa của

TURBO C:

asm break case cdecl

Page 6: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

6

char const continue default

do double else enum

extern far float for

goto huge if int

interrupt long near pascal

register return short signed

sizeof static struct switch

typedef union unsigned void

volatile while

Ý nghĩa và cách sử dụng của chúng sẽ được lần lượt giới thiệu ở các mục.

Chú ý:

- Không được dùng các từ khóa để đặt tên cho các hằng, biến, mảng, hàm….

- Từ khóa phải được viết bằng chữ thường. Chẳng hạn không được viết INT mà phải

viết int.

2.3. Tên

Tên là một khái niệm rất quan trọng, nó dùng để xác định các đối tượng khác nhau

trong một chương trình. Chúng ta có tên hằng, tên biến, tên mảng, tên hàm, tên con trỏ,

tên tệp, tên cấu trúc, tên nhãn,… Tên được đặt theo quy tắc sau:

Tên là một dãy kí tự: chữ, số và dấu gạch nối. Ký tự đầu tiên phải là chữ hoặc dấu

gạch nối. Tên không được trùng với từ khóa. Độ dài cực đại của tên mặc định là 32,

nhưng có thể đặt lại một giá trị từ 1 đến 32 trong chức năng: Option – Compiler –

Source – Identifier length trong môi trường phát triển kết hợp của C.

Ví dụ:

Tên đúng:

x_1, delte_1,…

Tên sai:

3abc: kí tự đầu tiên là số

r#1 : sử dụng kí tự #

f(x): sử dụng dấu ngoạc tròn

Chú ý:

Page 7: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

7

- Trong C phân biệt chữ hoa và chữ thường.

- Chữ hoa thường dùng để đặt tên cho các hằng, chữ thường thường được dùng để đặt

tên cho hầu hết các đối tượng như biến, mảng, hàm, cấu trúc (tuy nhiên điều này không

bắt buộc).

2.4. Kiểu dữ liệu, các kiểu dữ liệu cơ bản

Các biến trong chương trình có thể được lưu trữ dưới dạng các số hoặc dưới dạng các

kí tự. Thí dụ, điện trở của một đoạn đây đồng được lưu trữ như một con số (một giá trị

thực) còn tên của một linh kiện được lưu trữ dưới dạng kí tự.

Ngôn ngữ C có bốn kiểu dữ liệu cơ bản: char (ký tự đơn), int(số nguyên có dâu),

float(dấu phẩy động đơn chính xác), double (dấu phẩy động kép chính xác). Các kiểu

dữ liệu mở rộng: short, long, unsigned int. Miền giá trị của các kiểu dữ liệu như sau:

Kiểu Bộ nhớ Phạm vi

Char 1 -128 đến 127

unsigned char 1 0 đến 255

Int 2 -32.768 đến 32.767

unsigned int 2 0 đến 65535

long int 4 - 2.147.483.648 đến 2.147.483.647.

unsigned long int 4 0 đến 4294967295

Float 4 - 3.4.x10-38 đến +3.4x1038

Double 8 - 1.7.x10-311 đến +1.7x10311

long double 10 - 3.4.x10-4932 đến +1.1x104932

Bảng 2.1. Một số giới hạn tiêu biểu cho các kiểu dữ liệu

Số nguyên là một giá trị bất kỳ không chứa dấu thập phân; vùng giá trị của số nguyên

phụ thuộc vào số byte được lưu trữ số đó. Một giá trị đấu phẩy động là một số bất kỳ

và có thể chứa một đấu phấy thập phân; giá trị này luôn được mô ta trong khuôn mẫu

có dấu. Lại một lần nữa, vùng giá trị phụ thuộc vào số byte được sử dụng.

Page 8: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

8

2.5. Định nghĩa kiểu bằng typedef

Công dụng: Từ khóa typedef dùng để đặt tên cho một kiểu dữ liệu. Tên kiểu sẽ dùng

để khai báo dữ liệu sau này. Nên chọn một tên vừa ngắn gọn vừa dễ nhớ.

Cú pháp: Đặt từ khóa typedef vào trước một khai báo thông thường, khi đó tên dữ liệu

trở thành tên kiểu. Ví dụ câu lệnh:

typedef int ng;

sẽ đặt tên kiểu int là ng. Sau đó có thể dùng ng để khai báo cho các biến, mảng nguyên

như sau:

ng x,y,z a[10], b[10];

một số ví dụ khai báo :

typedef float mt50[50];

typedef int m_20_30[20][30];

typedef enum {T1, T2, T3} T;

sau này có thể dùng các khai báo:

mt:x,y /* các mảng thực một chiều 50 phần tử */

m_20_30 a,b; /* các mảng nguyên 2 chiều kích cỡ 20x30*/

T t: /* t là biến enum */

2.6. Hằng

Hằng là các đại lượng mà các giá trị của nó không thay đổi trong suốt quá trình tính

toán. Dưới đây là bảy loại hằng được sử dụng trong ngôn ngưc C.

2.6.1. Hằng đấu phẩy độngHằng này được viết theo 2 cách:

Cách một (dạng thập phân): số nguyên, dấu chấm thập phân và phần phân. Ví dụ:

214.35 -456.48 244.0

Chú ý: phần nguyên hay phần thập phân có thể vắng mặt nhưng dấu chấm không thể

thiếu, ví dụ cho phép viết:

.33 15.

Page 9: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

9

Cách 2: (dạng khoa học hay dạng mũ): Số được tách thành 2 phần định trị và phần bậc.

Phần định trị là một số nguyên hoặc sô thực dạng thập phân, phần bậc là số nguyên.

Hai phần này cách nhau bởi ký tự e hoặc E. Ví dụ:

123.456E-4 (biểu diễn giá trị 0.0123456)

0.12E3 (biễu diễn giá trị 1230.0)

-49.5e (biểu diễn giá trị -0.495)

2.6.2. Hằng intLà số nguyên có giá trị trong khoảng -32767 đến 32768. Ví dụ:

-45 635

2.6.3. Hằng longHằng này được viết theo cách them L hoặc l vào đuôi:

-4567L hoặc -4567l

Một số nguyên vượt ra ngoài miền xác định của int cùng được xem là hằng long. Ví

dụ:

4563946L và 4563946l

Là hai hằng long có cùng gia trị.

2.6.4. Hằng int hệ 8Hằng này được viết theo cách:

0c1c2c3….

Trong đó, ci là một số nguyên trong khoảng từ 0 đến 7. Hằng nguyên hệ 8 luôn nhận

giá trị dương. Ví dụ:

0345

Là một hằng nguyên hệ 8. Giá trị của nó trong hệ 10 là

3*8*8 + 4*8 + 5 = 229

2.6.5. Hằng nguyên hệ 16Trong hệ này sử dụng 16 kí tự trong khoảng từ 0 đến 16. Để tránh nhầm lẫn giữa số 11

và hai số 1 người ta dùng quy ước sau:

Cách viết Ý nghĩa

A hoặc a 10

Page 10: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

10

B hoặc b 11

C hoặc c 12

D hoặc d 13

E hoặc e 14

F hoặc f 15

Hằng hệ số 16 có dạng:

0xc1c2c3… hoặc 0Xc1c2c3…

Trong đó các ci là một chữ số trong khoảng từ 0 đến 16. Ví dụ:

0xa9, 0Xa9, 0xA9, 0XA9

Là 4 hằng số hệ 16 như nhau. Giá trị của chúng trong hệ 10 là 10*16 + 9 = 169

2.6.6. Hằng kí tựLà một kí tự riêng biết được viết trong 2 dấu nháy đơn, ví dụ ‘a’. Giá trị của ‘a’ chính

là mã ASCII của chữ a. Như vậy giá trị của ‘a’ là 97. Hằng kí tự có thể tham gia vào

các phép toán như mọi số nguyên khác. Ví dụ:

‘9’ – ‘0’ = 57 – 48 = 9

Hằng kí tự có thể được viết theo cách

‘\c1c2c3’

Trong đó c1c2c3 là một số hệ 8 mà giá trị của nó bằng mã ASCII của kí tự cần biểu

diễn. Ví dụ, chữ a có mã hệ 10 là 97 đổi ra hệ 8 là 0141. Vậy hằng kí tự ‘a’ có thể viết

dưới dạng ‘\141’

2.6.7. Hằng xâu kí tựLà một dãy ký tự bất kỳ đặt trong hai cặp đấu nháy kép: Ví dụ:

“Ha Noi”

“Hai Phong”

“” /* xâu rỗng*/

Xâu kí tự được lưu trữ trong máy dưới dạng một mảng các phần tử là các ký tự riêng

biệt. Trình biên dịch sẽ tự động thêm vào kí tự xóa \0 vào cuối mỗi xâu để đánh dấu sự

kết thúc xâu.

Page 11: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

11

Cần phân biệt ‘a’ và “a”, ‘a’ là hằng kí tự được lưu trữ trong một byte, còn “a” là một

hằng xâu kí tự được lưu trữ trong một mảng gồm hai phần tử: phần tử thứ nhất chứa

chữ a còn phần tử thứ 2 chứa \0.

2.6.8. Khai báoVí dụ có khai báo sau:

define MAX 1000

Khi đó tên MAX được sử dụng trong chương trình đều được thay bằng 1000. MAX là

tên hằng (hay macro)

2.7. Biến

2.7.1. Khai báoMọi biến cần phải khai báo trước khi sử dụng. Việc khai báo biến được thực hiện theo

mẫu sau:

Type tên_biến ;

Ví dụ:

main()

{

int a, b, c; /* khai báo 3 biến kiểu int*/

long m, n ; /* khai báo 2 biến kiểu long*/

char c ; /* khai báo 1 biến kiểu char*/

float x, y ; /* khai báo 2 biến kiểu float*/

double k, h ; /* khai báo 2 biến kiểu double*/

….

}

Biến kiểu int chỉ nhận được các giá trị kiểu int. Các biến khác cũng có ý nghĩa tương

tự, chẳng hạn biến kiểu char chỉ chứa được một kí tự. Để lưu trữ một dãy kí tự cần

phải sử dụng một mảng các kí tự.

2.7.2. Vị trí của các khai báoCác khai báo cần đặt ngay sau dấu { đầu tiên của thân hàm và cần đứng trước mọi câu

lệnh khác.

Page 12: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

12

Ví dụ:

main()

{

int a, b, c ;

a=35

}

Có thể khởi tạo giá trị ban đầu cho biến bằng cách gán cho biến một giá trị khi khai

báo. Ví dụ:

int a, b = 20, c, d=40;

float e= - 35.1 ;

2.7.3. Lấy địa chỉ của biếnMỗi biến được cấp phát một vùng nhớ gồm một số byte liên tiếp. Số hiệu của byte đầu

chính là địa chỉ của biến. Địa chỉ biến dùng trong một số hàm nhứ hàm scanf. Để nhận

địa chỉ biến ta dùng phép toán:

&tên_biến ;

2.8. Mảng

2.8.1. Khái niệm và cách khai báoMỗi biến chỉ có thể chứa một giá trị. Để chứa một dãy số hay một bảng số ta có thể

dùng nhiều biến nhưng cách này không tiện lợi. Vì vậy trong những trường hợp này sử

dụng mảng là rất tiện lợi.

Mảng có thể hiểu là một tập hợp nhiều phần tử có cùng một kiểu giá trị và có chung

một tên. Mỗi phần tử của mảng có vai trò như một biến và chứa được một giá trị. Có

bao nhiêu kiểu biến thì cũng có bấy nhiêu kiểu dữ liệu mảng. Khi khai báo mảng ta cấn

quan tâm đến các yếu tố:

- kiểu mảng (int, float, double,…)

- Tên mảng

- Số chiều và kích thước mảng

Khái niệm về kiểu mảng và tên mảng cũng giống như khái niệm về kiểu biến và tên

biến. Số chiều và kích thước của mảng được giải thích thông qua ví dụ sau:

Page 13: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

13

int a[10], b[4][2];

Khai báo trên sẽ xác định 2 mảng kiểu int a và b. Mảng a là mảng một chiều và kích

thước bằng 10. Mảng có 10 phần tử được đánh số như sau: a[0], a[1], a[2]…a[9]. Mỗi

phần tử a[i] chứa một giá trị kiểu int và mảng a có thể biểu diễn được một dãy 10 số

nguyên.

Mảng b là mảng hai chiều, một chiều có kích thức 4 và một chiều có kích thước 2.

Mảng có 8 phần tử, chúng được đánh số và sắp xếp như sau:

b[0][0] b[0]1[]

b[1]0[] b[1][1]

b[2][0] b[2][1]

b[3][0] b[3][1]

Mỗi phần tử b[i][j] chứa một giá trị kiểu int và mảng b có thể biểu diễn được một

bảng số nguyên 4 dòng, 2 cột.

Chú ý:

- Các phần tử của mảng được cấp phát một khoảng nhớ liên tiếp nhau trong bộ

nhớ (hay các phần tử của mảng có địa chỉ liên tiếp nhau).

- Trong bộ nhớ, các phần tử của mảng hai chiều được sắp xếp theo hàng.

2.8.2. Chỉ số mảng Một phần tử của mảng được xác định nhờ các chỉ số của nó. Chỉ số của mảng

phải co giá trị int không vượt quá kích thước của chiều tương ứng. Số chỉ số phải bằng

số chiều của mảng.

Giả sử a, b, đã được khai báo như trên và giả sử i, j là các biến nguyên trong đó

i=2, j=1. Khi đó:

a[j+i-1] là a[2];

b[j+i][2-i] là b[3][0];

Chú ý:

- Biểu thức dùng làm chỉ số có thể thực nhưng khi đó phần nguyên của biểu thức sẽ là

chỉ số của mảng. Ví dụ: a[2.4] =a[2].

- Khi chỉ số vượt ra ngoài kích thước của mảng, máy vẫn không báo lỗi, nhưng sẽ truy

nhập đến một vùng nhớ bên ngoài mảng và có thể làm rối lọa chương trình.

Page 14: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

14

2.8.3. Lấy địa chỉ phần tử mảng. Để lấy được địa chỉ của mảng một chiều ta dùng toán tử &. Chẳng hạn:

&a[i]

Nhưng đối với mảng nhiều chiều thì không thể lấy địa chỉ bằng phép toán tử &

nghĩa là không thể viết: &b[i][j].

Môt chú ý quan trọng là tên mảng biểu diễn địa chỉ đầu của mảng. Như vậy ta có:

a = &a[0];

2.9. Xâu

2.9.1. Khái niệm và cách khai báo Xâu là một mảng một chiều chứa các kí tự. Một kí tự đơn lẻ trong xâu được lưu

trữ bằng cách sử dụng 8 bít.

Khai báo:

char Ten_xau[kich_thuoc];

Tên xâu là địa chỉ bộ nhớ dùng cho kí tự đầu tiên trong xâu, kích thước là số kí

tự được lưu trữ trong bộ nhớ dành cho xâu.

Các xâu có xu hướng thay đổi về độ dài vì thế cần khai báo xâu với số cực đại của các

kí tự cần có. Chương trình sẽ tự động thêm vào kí tự “\0” để đánh dấu kết thúc xâu.

Một vài thí dụ về các xâu:

- Tên các máy tính như: “IBM C”, “MaccroVAX”

- Tên các bộ vi xử lý như: “Intel i80386”, “Motorola MC6800”

- Tên các phần tử trong mạch điện trở như: “Điện trở 1”, “Vòng khóa pha”, “Bộ

khuyêch đại vi sai”

2.9.2. Nhập vào một xâu Với một xâu được khai báo:

char str1[11];

thì số lớn nhất của các kí tự có thể hiển thị được trong xâu được lưu trữ chỉ bằng 10

bởi vì kí tự “\0” (NULL) được dùng để kết thúc xâu.

Gán str1=”Res_1” ta thấy hình ảnh sắp xếp bộ nhớ của xâu này như sau:

Page 15: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

15

Hình 2.1. Thí dụ về sắp xếp xâu

Như vậy, kí tự đầu tiên của xâu là str1[0] và kí tự kết thúc của xâu là str1[size-1].

Cũng giống với các mảng, có khả năng vượt tràn qua chỗ kết thúc của xâu. Điều này

làm cho dữ liệu bị đọc ra hoặc viết vào những vùng nhớ của bộ nhớ không được gán

cho mục đích này. Xâu đã được định kích thước luôn chứa ít nhất số lượng cực đại của

các kí tự được nhập vào. Nếu như một xâu được đọc vào từ bàn phím thì số lớn nhất

của các kí tự được nhập vào từ bàn phím có thể bị hạn chế bởi bộ đệm bàn phím. Một

macro BUFSIZ, được định nghĩa trong stdio.h có thể được sử dụng để xác định kích

thước của nó.

Thí dụ:

#include<stdio.h>

int main(void)

{

char instr[BUFSIZ];

do

{

printf(“Enter a name (enter ‘X’ to exit)>>”);

gets(instr);

printf(“Name enter is %s\n”, instr);

}while (instr[0]!=’X’);

return(0);

}

Thí dụ trên chỉ ra rằng xâu đã được khai báo instr có thể chứa một số cựa đại của

các kí tự có thể có thể hiển thị BUFSIZ-1 (một ký tự dùng cho NULL). Hàm gets() đọc

một dòng văn bản cho đến khi phím RETURN được nhấn . Hàm này chấp nhận đấu

trống giữa các từ, trong khi hàm scanf() giới hạn mỗi xâu bằng các dấu trống. Chương

str[10]str[2]str[1]str[0]

\0‘1’‘-’‘s’‘e’‘R’

Page 16: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

16

trình sẽ tiếp tục nhắc xâu văn bản chừng nào mà mà kí tự dầu tiên của xâu nhập vào là

kí tự ‘X’.

Khi chạy thí dụ trên ta được kết quả:

Enter a name (enter ‘X’ to exit)>> C for engineering programming

Name entered is C for engineering programming

Enter a name (enter ‘X’ to exit)>> lap trinh C trong KTDT

Name entered is lap trinh C trong KTDT

Enter a name (enter ‘X’ to exit)>> X

Name entered is X

Tìm hiểu xem Bufsize có kích thước bao nhiêu?

2.9.3. Gán xâu Giống như với các mảng, xâu có thể được nạp bằng các kí tự khi sử dụng cách

đánh số mảng như sau:

name [0] =’R’

name [1] =’e’

name [2] =’s’

name [3] =’-’

name [4] =’1’

name [5] =’\0’

Cách đánh số này sẽ được nạp kí tự ‘R’ vào vị trí đầu tiên ‘e’ vào vị trí thứ 2,…

Thí dụ sau đây cho thấy các ký tự được riêng lẻ được nạp như thế nào vào trong một

mảng các kí tự. Địa chỉ được chuyể giao tới hàm printf() đầu tiên chỉ tới chỗ bắt đầu

của xâu này. Có thể in một phần của xâu bằng cách chuyển giao tới địa chỉ cơ sở khác.

Chẳng hạn, để in “s_1” thì địa chỉ chuyển giao tới printf() là &instr[2].

Hàm strcpy() được chứa trong thư viện chuẩn. Nó sao chép xâu đối thứ hai vào xâu đối

số đầu tiên.

Thí dụ:

#include<stdio.h>

#include<string.h>

Int main(void)

Page 17: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

17

{

Char instr[BUFSIZ];

instr [0] =’R’

instr [1] =’e’

instr [2] =’s’

instr [3] =’_’

instr [4] =’1’

instr [5] =’\0’

printf(“String is %s \n”,instr);

printf(“Part of string is %s \n”,&instr[2]);

strcpy(instr,”Res_2”);

printf(“String is %s \n”,instr);

return(0);

}

Kết quả chạy chương trình như sau:

String is Res_1

Part of string is s_1

String is Res_2

2.9.4. Hàm xâu chuẩn Các hàm chuẩn thao tác với xâu nằm trong thư viện chuẩn string.h của C. Tất cả

các hàm xâu đều trả lại một giá trị; chẳng hạn, strlen() trả lại một giá trị là chiều dài

của xâu và các hàm strcat(), strupr(), strlwr() và strcpy() đều trả lại những con trỏ xâu

kết quả. Con trỏ này có thể được sử dụng nếu cần, nhưng xâu kết quả cũng được

chuyển giao ngược lại như là đối số đầu tiên của các hàm này. Hàm strcmp() trả lại chỉ

số 0 nếu nhứ hai xấu là đồng nhất.

Tên hàm Thư viện Ý nghĩa Giá trị trả về

int strcmp(char

*str1, char*

str2);

string.h So sánh hai xâu str1 và

str2.

Bằng 0 nếu hai xâu bằng

nhau.

Nhỏ hơn 0 nếu str1<str2

Lớn hơn 0 nếu str1>str2

Page 18: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

18

int strlen(char *

str)

string.h Xác định số các kí tự

trong xâu

Trả về số kí tự trong xâu

char *strcat(char

*str1, char *str2)

string.h Nối hai xâu str1 và str2 Một con trỏ vào xâu kết

quả

char *strlwr(char

*str1)

string.h Biếu đổi một xâu chữ

hoa thành một xâu chữ

thường

Một con trỏ vào xâu kết

quả

char *strupr(char

*str1)

string.h Biếu đổi một xâu chữ

thường thành một xâu

chữ hoa

Một con trỏ vào xâu kết

quả

c h a r

* s t r c p y ( c h a r

*str1, char *str2)

string.h Sao chép str1 vào str2 Một con trỏ vào xâu kết

quả

int sprintf(char

*str, *format_str,

arg1,…)

stdio.h Tương tự như hàm

printf như lối ra dẫn tới

xâu str

Số các kí tự nối ra

int scanf(char

*str, *format_str,

arg1,…)

stdio.h Tương tự như hàm

scanf như lối vào dẫn

tới xâu str

Số các trường được quét

xong

Page 19: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

19

Chương 3 CẤU TRÚC CHƯƠNG TRÌNH VÀ CÁC LỆNH VÀO RA CƠ BẢN

3.1. Lời chú thích

Trong quá trình lập trình, người lập trình thường sử dụng những chú thích để giải

thích về các khai báo, các lệnh, các hàm chức năng, thuật toán, …. giúp cho chương

trình dễ đọc hơn. Các lời chú thích này cần đặt trong cặp dấu:

/* ghi chú thích ở đây */

Trong trường hợp người lập trình muốn chú thích trên một dòng thì sử dụng cặp dấu //.

Thí dụ:

clrscr(); // Lệnh này dùng để xóa màn hình

Khi chạy chương trình máy tính sẽ bỏ qua các lời chú thích trong chương trình.

3.2. Lệnh và khối lệnh

3.2.1. Lệnh Một chương trình gồm nhiều câu lệnh, mỗi câu lệnh thực hiện một công việc nào đó

như nhập, xuất dữ liệu, tính toán giá trị các biểu thức, điều kiển hoạt động chương

trình. Mỗi câu lệnh có thể viết trên một hay nhiều dòng nhưng phải được kết thúc bởi

dấu chấm phẩy (;).

Câu lệnh được chia làm hai loại: câu lệnh đơn và câu lệnh có cấu trúc. Câu lệnh đơn

chỉ gồm một lệnh duy nhất như: lệnh gán, lệnh nhập hay xuất dữ liệu, lệnh gọi hàm,

lệnh nhảy…. Câu lệnh có cấu trúc có thể là lệnh ghép gồm một khối lệnh đặt giữa cặp

dâu {}, lệnh lựa chọn (if, case) hay các lệnh lặp (for, while, do...while).

3.2.2. Khối lệnh3.2.2.1. Định nghĩaKhối lệnh là một dãy các câu lệnh được bao bởi cặp dấu { và }. Ví dụ:

{

a=2 ; b= 3;

}

Các khai báo biến, mảng không những có thể viết ở đầu một hàm mà có thể viết ở đầu

mỗi khối lệnh.

Page 20: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

20

{

int a,b,c[50] ;

float x, y, z;

x=5.7; y=a*x ;

a=b=3 ;

printf(“\nx=%5.2f\nz=%5.2f”, y,z);

}

3.2.2.2 Sự lồng nhau của các khối lệnh

Một khối lệnh có thể chứa nhiều khối lệnh khác trong thân nó. Sự lồng nhau như vậy là

không hạn chế. Thân hàm cũng là một khối lệnh và đó là một khối lệnh cực đại vì nó

không bị chứa bên trong một khối lệnh nào.

2.10.3. Phạm ví hoạt động của các biến và mảngKhi máy bắt đầy làm việc với một khối lệnh thì các biến và các mảng khai báo bên

trong khối lệnh đó mới được hình thành và được cấp phát bộ nhớ. Các biến này chỉ

tong tại trong thới gian máy làm việc với khối lệnh và chúng sẽ lập tức biến mất ngay

sau khi máy thoát ra khỏi khối lệnh đó. Như vây:

- Giá trị của một biến hay một mảng khai báo bên trong một khối lệnh không thể

đưa ra để sử dụng ở bất ký chỗ nào bên ngoài khối lệnh.

- Ở bất kỳ chỗ nào bên ngoài khối lệnh ta không thể can thiệp đến các biến và các

mảng được khai báo bên trong khối lệnh.

- Nếu một biến a được khai báo bên trong một khối lệnh trùng tên với một biến a

đã được khai báo bên ngoài khối lệnh thì không làm thay đổi giá trị của biến a

đã được khai báo ngoài khối lệnh trước đó.

{

int a ;

{

int a ;

}

}

Page 21: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

21

Trong trường hợp này máy sẽ cấp phát hai ô nhớ khác nhau cho hai biến này và

thời gian tồn tại của hai biến này cũng khác nhau.

- Nếu một biến được khai báo ở ngoài một khối lệnh và không trùng tên với các

biến khai báo bên trong khối lệnh thì nó có thể được sử dụng cả bên trong cũng

như bên ngoài khối lệnh.

3.3. Cấu trúc cơ bản của một chương trình

Thông thường một chương trình được chia thành những nhiệm vụ nhỏ được gọi là các

hàm. Có những đoạn mã được phân định rõ rang để thực hiện những tác động riêng

biết. Hàm chính main() là đoạn chương trình cơ bản dùng để điều khiển tiến trình của

chương trình những chương trình con.

Các tên hàm có thể phân biệt đến 31 ký tự (các tên với nhiều hơn con số này sẽ phụ

thuộc và độ hoàn thiện của chương trình dịch). Tên hàm được đặt theo quy tắc đặt tên

đã trình bày.

Tất cả các chương trình C đều có một hàm chính main(), định nghĩa điểm nhập vào

chương trình và bằng các hàm được gọi, điều khiển toàn bộ tiến trình của chương trình.

Hàm này có thể được đặt ở bất kỳ chỗ nào trong chương trình nguồn, nhưng thường

được đặt ở phần đầu tập tin định vị (để dễ tím hơn). Xét thí dụ sau:

/*chương trình 3.1*/

#include <stdio.h>

int main(void)

func6(){

func4(){

func5(){

func3(){

func2(){

func1(){

main(){

Page 22: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

22

{ /* Điểm bắt đầu chương trình */

puts (“C for Electronic Engineering”);

/*C trong kỹ thuật điện tử */

return(0);

} /*Điểm kết thúc chương trình*/

Trong thí dụ trên hàm puts() để hiện thị xâu “C for Electronic Engineering”. Từ khóa

int đặt trước hàm main() xác định rằng chương trình trả lại một giá trị cho hệ điều hành

(hoặc chương trình gọi). Trong trường hợp này, giá trị trả lại bằng 0 (return(0)). Bình

thường thì giá trị trả lại khác 0 được sử dụng khi do chương trình bị thoát ra do có lỗi,

giá trị hiện tại của giá trị này cho ta một chỉ dẫn để biết vì sao chương trình thoát ra.

Từ void bên trong dấu ngoặc đơn của hàm main() xác định rằng không có trao đổi dữ

liệu giữ chương trình và hệ điều hành, thí dụ không có giá trị được chuyển giao vào

chương trình.

Khi chạy chương trình máy tính sẽ tìm hàm main() và lần lượt thực hiện các các câu

lệnh trong thân hàm hàm main() bắt đầu từ dấu { của hàm cho đến dấu } kết thúc của

hàm main() thì kết thúc chương trình.

3.4. Một số chú khi viết chương trình

- Mỗi câu lệnh có thể viết trên một dòng hay nhiều dòng và phải được kết thúc bởi dấy

(;). Ngược lại, nhiều câu lệnh có thể viết trên cùng một dòng.

Muốn viết một xâu hằng kí tự trên nhiều dòng thì phải đặt thêm kí tự / trước khi xuống

dòng để báo cho TURBO C biết một chuỗi kí tự vẫn còn tiếp tục ở dòng dưới.

Thí dụ:

{

printf(“\n Xin chao/

\nTURBO C”) ;

}

và cũng có thể viết:

{

int a, b;

a=2; b=3;

Page 23: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

23

}

- Các lời chú thích được viết trong cập dấu /* */.

- Khi sử dụng một hàm nào đó cần phải biết hàm đó nằm trong tệp thư viện nào của

chương trình và phải khai báo tệp thư viện đó bằng lệnh:

#include “Tên thư viện”

ở phần đầu của chương trình. Ở cuối câu lệnh #include không có đâu chấm phẩy (;).

Thí dụ, khi sử dụng lệnh printf để xuất dữ liệu ra màn hình, hàm này nằm trong thư

việc stdio.h trong thư mục của C. Do đó phải khai báo:

#include “stdio.h”

- Một chương trình chỉ có thể chỉ có duy nhất một hàm chính (main) và có thể chứa

thêm nhiều hàm khác nữa.

3.5. Vào số liệu từ bàn phím

Thông thường bàn phím là đầu vào chuẩn để nhập vào chương trình. Để vào số

liệu từ bàn phím ta sử dụng ba hàm chuẩn cơ bản scanf(), gets() và getchar(). Các hàm

này đều nằm trong thư viện chuẩn stdio.h của C.

3.5.1. Hàm scanf()Cú pháp:

Scanf(“format”, &arg1, &arg2,…,&argn)

Thí dụ:

{

int a,b;

float x,y;

scanf(“%d%d%f%f”,&a&b&x&y);

}

Khi gặp câu lệnh này máy tính sẽ dừng để đợi người dùng nhập vào số liệu từ bàn

phím. Trong thí dụ trên cần nhập vào 4 giá trị, 2 giá trị nguyên 2 giá trị thực. Các giá

trị này phải cách nhau một hoặc vài dấu cách hoặc dấu xuông dòng.

Một số chú ý đối với câu lệnh scanf:

Page 24: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

24

- Không dùng tên biến như trong câu lệnh printf mà phải dùng địa chỉ của biến. phép

toán &.

- Mỗi biến ứng với một đặc tả. Như vậy số đặc tả bằng số biến.

- Dùng đặc tả %d đối với biến nguyên và %f đối với biến kiểu thực.

3.5.3. Hàm gets()Cú pháp:

gets(str): Đọc xâu văn bản từ bàn phím vào biến str; những kí tự này được đọc

cho đến khi người dùng nhấn phím Enter.

3.5.2. Hàm getchar()Cú pháp:

ch=getchar()

Đọc ký tự đơn từ bàn phím và biến ch

3.6. Đưa kết quả lên màn hình

Thông thường màn hình là thiết bị đầu ra chuẩn để xuất dữ liệu ra từ chương

trình. Để xuất dữ liệu ra màn hình ta dùng các hàm printf(), puts(), putchar(). Các hàm

này đều nằm trong thư viện chuẩn stdio.h của C.

3.6.1. Hàm printf() Cú pháp:

printf(“format”,arg1, arg2, arg3….argn);

Hàm printf gửi một xâu đã đinh dạng tới đầu ra chuẩn (màn hình). Xâu này có thể

hiện thị các biến đã được định dạng và những kí tự điều khiển đặc biệt như ‘\n’ –

xuống dòng, đấu tab ‘\t’,...

Thí dụ sử dụng hàm printf như sau:

#include<stdio.h>

int main(void)

{

int a, b;

a=5, b=6;

printf(“Gia trị của a=%d và cua b=%d”, a,b);

Page 25: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

25

}

3.6.2. Hàm puts()Hàm này xuất một xâu văn bản ra màn hình và đưa con trỏ xuống dòng và các biến

không được định dạng có thể được sử dụng.

Thí dụ sử dụng hàm puts như sau:

#include<stdio.h>

int main(void)

{

puts(“C for Electronic Engineering”);

}

3.6.3. Hàm putchar() Hàm này xuất một kí tự đơn ch ra màn hình.

3.7. Đưa kết quả ra máy in

Để đưa kết quả ra máy in cần sử dụng hàm fprintf và đưa thêm tham số stdprn vào

trước chuỗi điều khiển

Cú pháp:

fprintf(stdprn,chuỗi điều khiển, arg1, arg2,…, argn)

Tham số stdprn chỉ ra rằng: thiết bị đưa ra là máy in, chuỗi điều khiển và các

tham số arg1 đến argn có ý nghĩa hoàn toàn tương tự như hàm printf.

3.8. Thí dụ.

3.8.1. Thí dụ 1

Viết chương trình tính điện trở tương đương của 3 điện trở mắc song song:#include <stdio.h>int main(void){float R1,R2,R3,R_td;

R1=1.0e3; /* 1 kohms */ R2=500.0; /* 500 ohms */

Page 26: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

26

R3=250.0; /* 250 ohms */

puts("Chuong trinh tinh dien tro tuong duong cua"); puts("3 dien tro mac song song");

R_td=1/(1/R1+1/R2+1/R3);

printf("R1=%6.3f, R2=%6.3f, R3=%6.3f\n",R1,R2,R3); printf("Điện trở tương đương là: %6.3f ohms\n",R_equ); getch(); return(0);}

3.8.2. Thí dụ 2:

Viết chương trình tính tần số cộng hưởng của mạch LC mắc nối tiếp:

#include <stdio.h>#include <math.h> /* Su dung ham sqrt() */#define PI 3.14159int main(void){float L=1e-3,C=1e-6,f_res; /* L là 1 mH, C là 1 uF */

puts("Chuong trinh tinh tan so cong huong cua"); puts("mach L-C noi tiep ");

f_res=1/(2*PI*sqrt(L*C));

printf("C=%.3e F, L=%.3e H\n" ,C,L); printf("Tan so cong huong la: %.3f Hz\n",f_res); getch(); return(0);}

Page 27: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

27

Chương 4 BIỂU THỨC

Toán hạng có thể xem là một đại lượng có một giá trị nào đó. Theo nghĩa đó, toán hạng

bao gồm hằng, biến, phần tử mảng và hàm. Biểu thức được lập nên từ các toán hạng và

các phép tính để tạo nên những giá trị mới. Biểu thức dùng để diễn đạt một công thức,

một quy trình tính toán, vì vậy nó là thành phần không thể thiếu trong một chương

trình.

4.1. Biểu thức

Biểu thức là sự kết hợp giữa các phép toán và các toán hạng để diễn đạt một công thức

nào đó. Khi viết biểu thức nên dùng các dấu ngoặc tròn để thể hiện đúng trình tự tính

toán trong biểu thức. Mỗi biểu thức sẽ có một giá trị và nói chung cái gì có giá trị đều

được xem là biểu thức. Trong C đưa ra nhiều quan niệm mới về biểu thức như biểu

thức gán, biểu thức điều kiện.

Biểu thức được phân loại theo kiểu giá trị: nguyên, thực. Trong các mệnh để logic,

biểu thức được phân thành đúng (giá trị khác 0) và sai (giá trị bằng 0).

Biểu thức thường được dùng trong các trường hợp:

- Vế phải của câu lệnh gán

- Làm tham số thực của hàm (như hàm printf)

- Làm chỉ số

- Trong các toán tử if, switch, for, while, do while.

Thí dụ: Tính tần số cộng hưởng của mạch LC song song.

Trong mạch LC song song hiệu ứng xảy ra trái ngược với trường hợp mạch LC nối

tiếp. Ở tần số thấp thì trở kháng thấp do diện cảm bị tụ diện làm cho ngắn mạch. Ở tần

số cao, tụ điện lại làm ngắn mạch cuộn cảm và cũng dẫn đến trở kháng thấp. Ở tần số

riêng mạch cộng hưởng đẫn đến trở kháng vào rất cao. Hiện tượng này làm cho dòng

trong mỗi mạch nhánh của mạch gần như bằng nhau và ngược chiều; như vậy có dòng

vào rất nhỏ. Hiện tượng này được gọi là cộng hưởng song song. Một cuộn cảm không

tổn hao có điện trở bằng 0 và không có dòng vào (nếu như tụ điện không có tổn hao).

Page 28: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

28

Đương nhiện mạch cổng hưởng song song hoàn hảo không có trong thực tế vì thường

có một điện trở trong cuộn cảm và tụ điện cũng có đôic hút tổn hao. Hình 4.1 giới

thiệu sơ đồ mạch diện song song:

Tần số mạch cổng hưởng được xác định bằng phương trình:

Fch =

][4

121

2

2

HzL

rLC

-

/*Chương trình tính tần số mạch cộng hưởng*/

#include<stdio.h>

#include<math.h> /*chứa hàm sqrt()*/

#define PI= 3.14159;

int main(void)

{

float L=1.0e-3, C=1.0e-6, r=1.0, f _ch ;

/*L tinh bang 1mH, c tinh bang 1uF, dien tro la ohm*/

puts(“Chuong trinh tinh tan so cong huong”);

puts(“cua mot mach LC song song cong huong”);

f_ch=1/(2*PI)*sqrt(1/(L*C) – (r*r)/(4*L*L)) ;

printf(“Tan so cua mach cong huong la: %.3f Hz”, f_ch);

}

Kết quả chương trình khi chạy như sau:

Chuong trinh tinh tan so cong huong

cua mot mach LC cong huong

cL

r

v(f)

Hình 4.1 Mạch LC mắc song song

Page 29: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

29

Tan so cua mach cong huong la: 5.032 328Hz

4.2. Lệnh gán

Lệnh gán (assignment statement) dùng để gán giá trị của một biểu thức cho một biến.

Cú pháp:

<Tên biến> = <biểu thức>

Thí dụ:

int main() {

int x,y;

x =10; /*Gán hằng số 10 cho biến x*/

y = 2*x; /*Gán giá trị 2*x=2*10=20 cho x*/

return 0;

}

Nguyên tắc khi dùng lệnh gán là kiểu của biến và kiểu của biểu thức phải giống nhau,

gọi là có sự tương thích giữa các kiểu dữ liệu. Chẳng hạn thí dụ sau cho thấy một sự

không tương thích về kiểu:

int main() {

int x,y;

x = 10; /*Gán hằng số 10 cho biến x*/

y = “Xin chao”;

/*y có kiểu int, còn “Xin chao” có kiểu chuỗi kí tự */

return 0;

}

Khi biên dịch chương trình này, C sẽ báo lỗi "Cannot convert ‘char *’ to ‘int’" tức là C

không thể tự động chuyển đổi kiểu từ char * (chuỗi ký tự) sang kiểu int.

Tuy nhiên trong đa số trường hợp sự tự động biến đổi kiểu để có sự tương thích về

kiểu sẽ được thực hiện. Thí dụ:

int main() {

int x,y;

float r;

char ch;

Page 30: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

30

r = 9000;

x = 10; /* Gán hằng số 10 cho biến x */

y = 'd'; /* y có kiểu int, còn ‘d’ có kiểu char*/

r = 'e'; /* r có kiểu float, ‘e’ có kiểu char*/

ch = 65.7; /* ch có kiểu char, còn 65.7 có kiểu float*/

return 0;

}

Trong nhiều trường hợp để tạo ra sự tương thích về kiểu, ta phải sử dụng đến cách thức

chuyển đổi kiểu một cách tường minh. Cú pháp của phép toán này như sau:

(Tên kiểu) <Biểu thức>

Chuyển đổi kiểu của <Biểu thức> thành kiểu mới <Tên kiểu>. Chẳng hạn:

float f;

f = (float) 10 / 4; /* f lúc này là 2.5*/

Chú ý:

- Khi một biểu thức được gán cho một biến thì giá trị của nó sẽ thay thế giá trị cũ mà

biến đã lưu giữ trước đó.

- Trong câu lệnh gán, dấu = là một toán tử; do đó nó có thể được sử dụng là một thành

phần của biểu thức. Trong trường hợp này giá trị của biểu thức gán chính là giá trị của

biến.

Thí dụ:

int x, y;

y = x = 3; /* y lúc này cũng bằng 3*/

Có thể gán trị cho biến lúc biến được khai báo theo cách thức sau:

<Tên kiểu> <Tên biến> = <Biểu thức>;

Thí dụ:

int x = 10, y=x;

4.3. Các phép toán

Trong C có 4 nhóm toán tử chính yếu sau đây:

4.3.1. Các phép toán số học+ : cộng ; – : trừ ; * : nhân ; / : chia.

Page 31: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

31

Các phép toán này áp dụng trên các kiểu dữ liệu char, int, float, double (kể cả các kiểu

mở rộng của nó).

Phép toán chia lấy phần dư % áp dụng trên các kiểu char, int, long.

Thứ tự ưu tiên: Đảo dấu +, – ( ) *, / , % +, –

4.3.2. Các phép toán quan hệPhép toán Mô tả

> 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ác

-- Làm giảm 1 đơn vị

++ Làm tăng một đơn vị

Thứ tự ưu tiên: > , >= , < , <= = = , !=

Kết quả của phép toán quan hệ là số nguyên kiểu int, bằng 1 nếu đúng, bằng 0

nếu sai.

Phép toán quan hệ ngoài toán hạng được sử dụng là số còn được sử dụng với kiểu

dữ liệu char.

Các toán tử số học được có mức ưu tên cao hơn các phép toán quan hệ

Thí dụ:

4 >= 4 → có giá trị 1 (đúng)

3 == 5 → có giá trị 0 (sai)

2 <= 1 → có giá trị 0 (sai)

6 != 4 → có giá trị 1 (đúng)

6 – 3 < 4 → có giá trị 1 (đúng), tương đương (6 – 3) < 4

–2 * (–4) < 3 + 2 → có giá trị 0 (sai)

4.3.3. Các phép toán logic

Page 32: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

32

Phép toán Ý nghĩa

! NOT

&& AND

|| OR

Bảng chân trị cho các toán tử logic:

A B A&&B A||B !A

0 0 0 0 1

0 1 0 1 1

1 0 0 1 0

1 1 1 1 0

Các toán tử quan hệ và logic đều có độ ưu tiên thấp hơn các toán tử số học. Do đó

một biểu thức như: 10 > 1+ 12 sẽ được xem là 10 > (1 + 12) và kết quả là sai (0).

Ta có thể kết hợp vài toán tử lại với nhau thành biểu thức như sau:

10>5&&!(10<9)||3<=4 cho kết quả là đúng

Thứ tự ưu tiên của các toán tử quan hệ và logic:

Cao nhất: !

> >= < <=

= = !=

&&

Thấp nhất: ||

4.3.5. Một số toán tử khác4.3.5.1. Toán tử ? cùng với :

C có một toán tử rất mạnh và thích hợp để thay thế cho các câu lệnh của

If-Then-Else.

Cú pháp:

E1 ? E2 : E3

Trong đó E1, E2, E3 là các biểu thức.

Page 33: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

33

Ý nghĩa: Trước tiên E1 được ước lượng, nếu đúng E2 được ước lượng và nó trở thành

giá trị của biểu thức; nếu E1 sai, E3 được ước lượng và trở thành giá trị của biểu thức.

Thí dụ:

X = 10

Y = X > 9 ? 100 : 200

Thì Y được gán giá trị 100, nếu X nhỏ hơn 9 thì Y sẽ nhận giá trị là 200. Đoạn mã này

tương đương cấu trúc if như sau:

X = 10

if (X < 9) Y = 100

else Y = 200

4.3.5.2. Toán tử con trỏ & và *

Một con trỏ là địa chỉ trong bộ nhớ của một biến. Một biến con trỏ là một biến

được khai báo riêng để chứa một con trỏ đến một đối tượng của kiểu đã chỉ ra nó. Con

trỏ sẽ được tìm hiểu kỹ hơn trong chương sau con trỏ. Trong C có hai toán tử được sử

dụng để thao tác với các con trỏ:

Toán tử thứ nhất là &, là một toán tử quy ước trả về địa chỉ bộ nhớ của hệ số của

nó.

Thí dụ: m = &count

Câu lệnh trên nghĩa là đặt vào biến m địa chỉ bộ nhớ của biến count. Chẳng hạn,

biến count ở vị trí bộ nhớ 2000, giả sử count có giá trị là 100. Sau câu lệnh trên m sẽ

nhận giá trị 2000.

Toán tử thứ hai là *, là một bổ sung cho &; đây là một toán tử quy ước trả về giá

trị của biến được cấp phát tại địa chỉ theo sau đó. Thí dụ:

q = *m

sẽ đặt giá trị của count vào q. Bây giờ q sẽ có giá trị là 100 vì 100 được lưu trữ tại

địa chỉ 2000.

4.3.5.3. Toán tử dấu phẩy ,

Toán tử dấu , được sử dụng để kết hợp các biểu thức lại với nhau. Bên trái của

toán tử dấu “,” luôn được xem là kiểu void. Điều đó có nghĩa là biểu thức bên phải trở

thành giá trị của tổng các biểu thức được phân cách bởi dấu phẩy. Thí dụ:

Page 34: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

34

x = (y=3,y+1);

Trước hết gán 3 cho y rồi gán 4 cho x. Cặp dấu ngoặc đơn là cần thiết vì toán tử

dấu , có độ ưu tiên thấp hơn toán tử gán.

4.3.5.3. Toán tử (), [ ]

Trong C, cặp dấu ngoặc đơn là toán tử để tăng độ ưu tiên của các biểu thức bên

trong nó.

Các cặp dấu ngoặc vuông thực hiện thao tác truy xuất phần tử trong mảng.

4.4. Bảng tổng kết về độ ưu tiên của các phép toán Độ ưu tiên của các phép toán theo thứ tự từ trên xuống dưới từ trái qua phải

Độ ưu

tiên

Các phép toán Trình tự kết hợp

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

( ) [ ]

! ~ & * – ++ – – (type) sizeof

* / %

+ –

<< >>

< <= > >=

== !=

&

^

|

&&

||

? :

= += –= *= /= %= <<= >>= &= ^= |=

,

Trái sang phải

Phải sang trái

Trái sang phải

Trái sang phải

Trái sang phải

Trái sang phải

Trái sang phải

Trái sang phải

Trái sang phải

Trái sang phải

Trái sang phải

Trái sang phải

Phải sang trái

Phải sang trái

Trái sang phải

Bảng tổng kết về độ ưu tiên

Page 35: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

35

4.5. Chuyển đổi kiểu dữ liệu Khi các kiểu dữ liệu hỗn hợp trong một phép toán với hai toán hạng, quy tắc sau

đây xác định kiểu dữ liệu kết quả:

1. Kiểu ký tự bất kỳ như unsigned char, char, short int biến đổi sang kiểu nguyên

2. Hoặc nếu một trong hai toán hạng là long double thì toán hạng kia sẽ biến đổi

sang long double.

3. Hoặc nếu một trong hai toán hạng là double, thì toán hạng kia sẽ biến đổi sang

double

4. Hoặc nếu một trong hai toán hạng là float, thì toán hạng kia sẽ biến đổi sang

float.

5. Hoặc nếu một trong hai toán hạng là unsigned long, thì toán hạng kia sẽ biến

đổi sang unsigned long.

6. Hoặc nếu một trong hai toán hạng là long int thì toán hạng cong lại sẽ biến đổi

sang long int.

7. Hoặc nếu một trong hai toán hạng là unsigned int thì toán hạng kia sẽ biến đổi

thành unsigned int.

8. Cả hai toán hạng đều là kiểu int.

Một kiểu dữ liệu có thể bị thay đổi một cách tạm thời khi sử dụng một kỹ thuật ép

kiểu (casting). Để ép kiểu, đặt kiểu cần chuyển về trong cặp dấu ngoặc đơn trước toán

hạng hoặc biểu thức cần chuyển. Thí dụ như trong chương trình sau:

#include <stdio.h>

int main(void)

{

float a;

int b,c;

b=6; c=11;

a = c / b;

printf("a = %f",a);

return(0);

}

Page 36: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

36

4.6. Các phép toán xử lý bít và ứng dụng trong điện tử viễn thong

4.6.1. Các hệ đếm

Trong cuộc sống hàng ngày chúng ta thường dùng các hệ cơ số 10 để biểu diễn

các giá trị số. Điều này rất tự nhiên vì từ xa xưa con người bình thường đã biết dùng 10

ngón tay của mình để như là một công cụ tính toán sơ đẳng.

Trong thế giới máy tính thì khác, do máy tính được cấu tạo nên từ các mạch điện

tử và các mạch này chỉ có hai có hai trạng thái có điện và không có điện. Do đó để

biễu diễn một giá trị số trong máy tính người ta sử dụng hệ đếm cơ số hai hay hệ đếm

nhị phân (Binary number system). Trong hệ đếm này chỉ tồn tại hai chữ số 0 và 1

tương ứng với hai trạng thái có điện và không có diện của các mạch điện tử.

Nếu dùng hệ cơ số hai để biểu diễn các số có giá trị lớn sẽ gặp bất tiện là số hệ

hai thu được quá dài, thí dụ:

255 = 1111 1111

Để viết kết quả biễu diễn các số cho gọn lại người ta sử dụng các các hệ đếm khác

như hệ cơ số 16 (thập lục, hexa) và hệ cơ số 8(bát phân). Bảng sau đây trình bày một

số hệ đếm cơ bản:

Hệ đếm Cơ số Số kí số và kí tự Dạng kí số và kí tự

Nhị phân (Binary) 2 2 0,1

Bát phân (Octal) 8 8 0,1,2,3,4,5,6,7

Thập phân (Decimal) 10 10 0,1,2,3,4,5,6,7,8,9

Thập lục phân

(Hexadecimal)

16 16 0,1,2,3,4,5,6,7,8,9

A,B,C,D,E,F

Bảng 4.1. Các hệ đếm cơ bản

Ngoài ra, hệ đếm BCD còn được sử dụng để biểu diễn các số từ 0 đến 9 với 4

bit (4 bit=1 nibble) nhị phân.

Page 37: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

37

4.6.2. Chuyển đổi số giữa các hệ đếm

4.6.2.1. Chuyển đổi giữa hệ thập phân và hệ nhị phân

a. Chuyển từ hệ thập phân sang hệ nhị phân:

Quy tắc: Lấy phần nguyên chia cho 2 và ghi lại phần dư, tiếp tục lấy thương

chia cho 2 và ghi lại phần dư. Tiếp tục làm như vậy cho đến khi thương bằng 0. Sau đó

viết các số dư theo chiều từ phép chia cuối cùng đến phép chia đầu tiên. Thí dụ:

Hình 4.2.

Cách đổi một

số hệ mười

sang hệ hai

Quy tắc đổi số

thập phân hệ

mười sang hệ

hai: Lấp số

cần đổi nhân

với 2, tích gồm phần nguyên và phần lẻ. Lấy phần lẻ nhân tiếp với 2 cho đến khi nào

tích thu được bằng 1 thì dừng lại. Chọn riêng phần nguyên của các tích thu được và

viết theo thứ tự từ phép nhân đầu tiên đến phép nhân cuối cùng. Thí dụ:

Hìn

h

4.3. Các đổi một số thập phân hệ mười sang hệ hai

b. Chuyển từ hệ nhị phân sang hệ thập phân:

,250,50,0

(0,125)10 = (0,001)2

0,125 x 2 =0,250 x 2 = 0,50 x 2 =

001

(33)10 = (100001)2

01

210

220

240

280

2161

233

Page 38: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

38

Để chuyển từ hệ nhị phân sang thập phân ta tính tổng các tích số giữa các hệ số

với các trọng số 2i tại từng vị trí thứ i.

Thí dụ:

(1110,11)2 = 1.23 + 1.22 + 1.21 + 0.20 + 1.2-1 + 1.2-2

= 8 + 4 + 2 + 0,5 + 0,25 = (14,75)10

4.6.2.2. Chuyển đổi giữa thập lục hoặc hệ bát phân sang hệ nhị phânQuy tắc: Nhóm 4 bit (hoặc 3 bit cho hệ bát phân) bắt đầu từ bit ngoài cùng bên phải,

tính gia trị số học theo quy luất giá trị riêng cho từng nhóm. Viết các giá trị này liên

tiếp nhau.

Thí dụ:

Cho số nhị phân: 11110101 chuyển sang hệ thập lục và hệ bát phân như sau:

(11 110 101) à 3 6 5 à trong hệ bát phân là số 365

(1111 0101) à 15 5 à D5 à trong hệ thập lục là số D5

Khi cần chuyển ngược lại làm tương tự. Thí dụ:

(120)8 = (001 010 000)2

(120)16 = (0001 0010 0000)2

4.6.3. Các phép toán bit

4.6.3.1. Phép toán ANDKí hiệu: &Ý nghĩa: Nhân logic trên các bit. Phép toán này thực hiện trên từng cặp bit tương ứng

của các toán hạng theo quy tắc trong bảng sau:

A B A | B

0 0 0

0 1 0

1 0 0

1 1 1

Bảng 4-2. Bảng chân lý phép toán AND trên bit

Page 39: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

39

4.6.3.2. Phép toán ORKí hiệu: |Ý nghĩa: Cộng logic trên các bit. Phép toán này thực hiện trên từng cặp bit tương ứng

của các toán hạng theo quy tắc trong bảng sau:

A B A | B

0 0 0

0 1 1

1 0 1

1 1 1

Bảng 4-3. Bảng chân lý phép toán OR trên bit

4.6.3.3. Phép toán XORKí hiệu: ^Ý nghĩa: Phép cộng logic trên các bit. Thực hiện trên từng cặp bit tương ứng của các

toán hạng theo quy tắc trong bảng sau.

A B A ^ B

0 0 0

0 1 1

1 0 1

1 1 0

Bảng 4-4. Bảng chân lý phép toán OR trên bit

4.6.3.4. Phép toán NOT

Kí hiêu: ~

Ý nghĩa: phép đảo bit, đổi các giá trị trong mỗi bit của toán hạng x từ 0->1, 1->0.

4.6.3.5. Phép toán dịch trái/phải

- x SHR i : Phép dịch phải, cho giá trị có được từ số nguyên x sau khi dịch sang

phải i bit; các số 0 sẽ lấp đầy các kết quả bên trái nếu là số nguyên dương; nếu không

phải là số nguyên dương thì số 1 sẽ lấp đầy các kết quả bên trái.

Thí dụ:

Page 40: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

40

5 >> 2 = 1 ( 0101 >> 2 = 0001)

- x SHL i : Phép dịch trái, cho giá trị có được từ số nguyên x sau khi dịch sang trái i

bit; các số 0 sẽ lấp đầy các kết quả ở bên phải.

5 << 2 = 20

4.7. Thí dụ

Viết chương trình nhập vào hai số ở hệ hexa và thực hiện các phép toán AND,

OR, XOR, NAND và NOR giữa hai số.

#include <stdio.h>

int main(void)

{

int value1, value2;

/* & - bitwise AND operator */

/* | - bitwise OR operator */

/* ^ - bitwise XOR operator */

/* ~ - bitwise NOT operator */

printf("Nhap vao hai so he hexa >>> ");

scanf("%x %x",&value1,&value2);

printf("Ket qua AND hai so: %x\n",value1 & value2);

printf("Ket qua OR hai so: %x\n",value1 | value2);

printf("Ket qua XOR hai so: %x\n",value1 ^ value2);

printf("Ket qua NAND hai so: %x\n",~(value1 & value2));

printf("Ket qua NOR hai so: %x\n", ~(value1 | value2));

getch();

return(0);

}

Page 41: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

41

Chương 5 CÁC LỆNH ĐIỀU KHIỂN

5.1. Các lệnh lựa chọn

5.1.1. Cấu trúc rẽ nhánh if Cấu trúc rẽ nhánh là một cấu trúc được dùng rất phổ biến trong các ngôn ngữ lập

trình nói chung. Câu lệnh if cho phép lựa chọn một trong hai nhánh tùy thuộc vào giá

trị của biểu thức điều kiện là đúng (true) hay sai (false) hoặc khác không hay bằng

không. Trong C, có hai dạng: dạng không đầy đủ và dạng đầy đủ.

a. Dạng không đầy đủ

Cú pháp:

if (<Biểu thức điều kiện>)

<Công việc>

Lưu đồ cú pháp:

Hình 5-1. Lưu đồ cú pháp câu lệnh if dạng không đầy đủ

<Công việc> có thể là 1 câu lệnh hay 1 khối lệnh.

Máy tính thực hiện kiểm tra Biểu thức điều kiện trước. Nếu điều kiện đúng (!= 0)

thì thực hiện câu lệnh hoặc khối lệnh liền sau điều kiện. Nếu điều kiện sai thì bỏ qua

lệnh hoặc khối lệnh liền sau điều.

Đúng

Sai

Thoát

BT điều

Công việc

Page 42: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

42

Thí dụ: Yêu cầu người dùng nhập vào giá trị của 2 số a và b, nếu a lớn hơn b thì

in ra thông báo “Gia trị của a lớn hơn giá trị của b”, sau đó hiển thị giá trị cụ thể của 2

số lên màn hình.

#include <stdio.h>

#include<conio.h>

int main ()

{

int a,b;

printf("Nhap vao gia tri cua 2 so a, b!");

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

if (a>b)

{

printf("\n Gia tri cua a lon hon gia tri cua b");

printf("\n a=%d, b=%d",a,b);

}

getch();

return 0;

}

Giải thích:

Nếu người dùng nhập vào giá trị của a lớn hơn giá trị của b thì khối lệnh:

{

printf("\n Gia tri cua a lon hon gia tri cua b");

printf("\n a=%d, b=%d",a,b);

}

sẽ được thực hiện, ngược lại khối lệnh này không được thực hiện.

b. Dạng đầy đủ

Cú pháp:

if (<Biểu thức điều kiện>)

<Công việc 1>

else

<Công việc 2>

Page 43: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

43

Lưu đồ cú pháp:

Hình 5-2. Lưu đồ cú pháp câu lệnh if dạng đầy đủ

Công việc 1, công việc 2 được thể hiện là 1 câu lệnh hay 1 khối lệnh.

Trước tiên Biểu thức điều kiện được kiểm tra trước. Nếu điều kiện đúng thì thực

hiện công việc 1. Nếu điều kiện sai thì thực hiện công việc 2.

Các lệnh phía sau công việc 2 không phụ thuộc vào điều kiện.

Thí dụ: Yêu cầu người thực hiện chương trình nhập vào một số thực a. In ra màn hình

kết quả nghịch đảo của a khi a ≠ 0, khi a =0 in ra thông báo “Khong the tim duoc

nghich dao cua a”

#include <stdio.h>

#include <conio.h>

int main ()

{

float a;

printf("Nhap a = "); scanf("%f",&a);

if (a !=0 )

printf("Nghich dao cua %f la %f",a,1/a);

else

printf("Khong the tim duoc nghich dao cua a");

getch();

Đúng

Sai

Thoát

BT điều

Công việc 2Công việc 1

Page 44: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

44

return 0;

}

Giải thích:

- Nếu chúng ta nhập vào a ≠ 0 thì câu lệnh printf("Nghich dao cua %f là

%f",a,1/a)được thực hiện, ngược lại câu lệnh printf(“Khong the tim duoc nghich dao

cua a”) được thực hiện.

- Lệnh getch() luôn luôn được thực hiện.

c. Một số thí dụ về câu lệnh if

Thí dụ 5.1: Viết chương trình biến đổi một số nguyên dạng thập phân sang dạng nhị

phân 8 bít không dấu. Một kỹ thuật được gọi là che bit được sử dụng để nhận biết các

bit riêng lẻ bằng cách đặt các bit khac, khác với bit được quan tâm; bằng 0. Kỹ thuất

này sử dụng toán tử xử lý bit &, dẫn đến kết quả bằng 0 đối với một bit nếu như một

trong những toán hạng bit bằng 0; Nói cách khác, nếu như một trong các toán hạng bit

bằng 1 nõ sẽ dấn đến giá trị của bit toán hạng khác. Hình 5.1 là một thí dụ về việc che

bit nhỏ thứ 3 (b2). Trong trường hợp này mặt nạ được sử dụng là 0x04; mặt nạ này sau

đó được liên kết AND theo từng bit. Chỉ co hai kết quả khả dĩ từ phép tính này: là 0

(nếu b2=0) hoặc 4 (nếu b2=1). Dấu x có nghĩa là không cần quan tâm đến trạng thái,

trong đó một bit có thể nhận giá trị nhị phân bất kỳ (nghĩa là 0 hoặc 1).

Chương trình sử dụng lệnh if với các giá trị che bit 0x80 (1000 0000), 0x04 (0100

0000)… để xác định xem bit nào trong 8 bit thấp hơn của số nguyên thập phân được

đặt. Lệnh printf() hiển thị từng bit trên một dòng khi ở đó không có kí tự dòng mới

(‘\n’) ở cuối mỗi xâu định dạng cho hàm printf(). Bit có giá trị cao nhất được hiển thị

trước tiên khi xuất hiện ở phía bên trái màn hình.

#include<stdio.h>

#include<conio.h>

int main(void)

{

int i;

clrscr();

printf("Nhap vao mot so he thap phan (0->255): ");

scanf("%d", &i);

Page 45: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

45

if (i>=0 && i<=255){

printf("Dang nhi phan cua so vua nhap:" );

if (i & 0x80) printf("1"); else printf("0");

if (i & 0x40) printf("1"); else printf("0");

if (i & 0x20) printf("1"); else printf("0");

if (i & 0x10) printf("1"); else printf("0");

if (i & 0x08) printf("1"); else printf("0");

if (i & 0x04) printf("1"); else printf("0");

if (i & 0x02) printf("1"); else printf("0");

if (i & 0x01) printf("1"); else printf("0");

}

else

printf("so khong hop le");

getch();

return(0);

}

Kết quả:

Nhap vao mot so he thap phan (0->255):255

Dang nhi phan cua so vua nhap:11111111

Thí dụ 5.2. Viết chương trình nhập vào hai số ở hệ hexa rồi thực hiện các phép toán

xử lý bít trên hai số đó.

#include<stdio.h>

#define OR 1

#define AND 2

#define EX-OR 3

#define NOR 4

#define NAND 5

int main(void)

{

int value1, value2,option;

puts(“Nhap vao hai so he hex: “);

scanf(“%x, %x” value1,value2);

Page 46: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

46

puts(“Nhap vao phep toan can thao tac“);

printf(“1-OR\n2-AND\n3-EXOR\n4-NOR\n5-NAND”);

scanf(“%d”, &option);

if (option= = OR)

printf(“ Ket qua: %x (hex):”,value1 | value2);

else if (option = = AND)

printf(“ Ket qua: %x (hex):”,value1 & value2);

else if (option = = EX-OR)

printf(“ Ket qua: %x (hex):”,value1 ^ value2);

else if (option = = NOR)

printf(“ Ket qua: %x (hex):”,~(value1 | value2));

else if (option = = NAND)

printf(“ Ket qua: %x (hex):”,~(value1 & value2));

return(0);

}

5.1.2. Câu lệnh switch Cấu trúc lựa chọn cho phép lựa chọn một trong nhiều trường hợp. Trong C, đó là

câu lệnh switch.

a. Cú pháp và lưu đồ thuật toán

Cú pháp:

switch (<Biểu thức>)

{

case giá trị 1:

<công việc 1>;

break;

case giá trị n:

<công việc n;>

break;

default :

<công việc n+1>;

Page 47: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

47

break;]

}

Lưu đồ cú pháp:

Hình 5-3. Lưu đồ cú pháp câu lệnh switch

Giải thích:

- Tính giá trị của biểu thức trước.

- Nếu giá trị của biểu thức bằng giá trị 1 thì thực hiện công việc 1 rồi thoát.

- Nếu giá trị của biểu thức khác giá trị 1 thì so sánh với giá trị 2, nếu bằng giá trị 2 thì

thực hiện công việc 2 rồi thoát.

- Tiếp tục so sánh tới giá trị n.

Page 48: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

48

- Nếu tất cả các phép so sánh trên đều sai thì thực hiện công việc mặc định của trường

hợp default.

Lưu ý:

- Biểu thức trong switch() phải có kết quả là giá trị kiểu số nguyên (int, char,

long, …).

- Các giá trị sau case cũng phải là kiểu số nguyên.

- Không bắt buộc phải có default.

b. Một số thí dụ

Thí dụ 5.3: Xác định màu của các vòng màu trên điện trở.

Thông thường các giá trị điện trở có thể đọc được thông qua hệ thống các vòng màu

hay còn gọi là mã màu, như minh họa trong bảng 5.1.

Digital Colors Hệ số nhân

Silver 0,01

Vàng nhũ 0,1

0 Black 1

1 Brown 10

2 Red 100

3 Oragne 1000=1k

4 Yellow 10k

5 Green 100k

6 Blue 1000k=1M

7 Violet 10M

8 Grey

9 While

Bảng quy ước mã màu điện trở

Chương trình 5.3 sử dụng lệnh switch để xác định màu của các vòng màu trên

than điện trở đối với một giá trị điện trở được nhập vào. Biến sử dụng color đã được

khai báo như một unsigned int (số nguyên không dâu) bởi vì giá trị nhập vào luôn

dương. Muốn thế hàm scan() có kí hiệu %u để chỉ rõ khuôn mẫu.

Page 49: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

49

#include<stdio.h>

#include<conio.h>

int main(void)

{

unsigned int colour;

clrscr();

printf("Nhap vao so cac vong mau (0->9): ");

scanf("%u", &colour);

printf("Vong mau la mau: ");

switch (colour)

{

case 0: printf("BLACK"); break;

case 1: printf("BROWN"); break;

case 2: printf("RED"); break;

case 3: printf("OREANGE"); break;

case 4: printf("YELLOW"); break;

case 5: printf("GREEN"); break;

case 6: printf("BLUE"); break;

case 7: printf("VIOLET"); break;

case 8: printf("GREY"); break;

case 9: printf("WHILE"); break;

}

getch();

return(0);

}

Kết quả khi chạy chương trình:

Nhap vao so cac vong mau (0->9): 5

Vong mau la mau: GREEN

Có thể sử dụng #define để định nghĩa các màu sắc và chương trình trên có thể được

viết lại như sau:

#include<stdio.h>

#include<conio.h>

Page 50: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

50

#define BLACK 0

#define BROWN 1

#define RED 2

#define OREANGE 3

#define YELLOW 4

#define GREEN 5

#define BLUE 6

#define VIOLET 7

#define GREY 8

#define WHILE 9

int main(void)

{

unsigned int colour;

clrscr();

printf("Nhap vao so cac vong mau (0->9): ");

scanf("%u", &colour);

printf("Vong mau la mau: ");

switch(colour)

{

case BLACK: printf("BLACK"); break;

case BROWN: printf("BROWN"); break;

case RED: printf("RED"); break;

case OREANGE: printf("OREANGE"); break;

case YELLOW: printf("YELLOW"); break;

case GREEN: printf("GREEN"); break;

case BLUE: printf("BLUE"); break;

case VIOLET: printf("VIOLET"); break;

case GREY: printf("GREY"); break;

case WHILE: printf("WHILE"); break;

Page 51: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

51

default:

printf("Khong co mau sac");

}

getch();

return(0);

}

Một khuôn mẫu mới để định nghĩa một dãy giá trị số nguyên là sử dụng kiểu dữ

liệu enum. Chương trình 5.3. có thể được viết lại như sau:

Khai báo enum thiết lập một dãy các giá trị số nguyên. Nếu như dãy này bắt đầu

từ 0 thì không cần phải thiết lập trạng thái ban đầu của giá trị, bằng không thì tham số

đầu tiên phải được khở tạo. Một mẫu khai báo enum cho dưới đây, khai báo này khởi

tạo dãy từ -2 (silver) và dãy sẽ là nhũ bạc, -1 tương ứng với vãng nhũ đen =0, …trắng

(while)=9 và biến được khai báo là color.

Một ưu điểm của việc sử dụng kiểu dữ liệu enum là biến chỉ có thể lấy các tên

của các sự khai báo, thí dụ: color=2 là sai, trong khi đó color=RED lại là đúng.

Biện pháp này hoàn thiện khả năng kiểm tra lỗi của chương trình bằng cách hạn

chế khẳ năng sử dụng của biến.

#include<stdio.h>

int main(void)

{

enum(AND=1, OR, NAND, NOR, NOT) gate_type;

puts(“Nhap vao cong logic can co: ”);

puts(“1 – 2 loi vao cong AND”);

puts(2 – 2 loi vao cong OR”);

puts(“3 – 2 loi vao cong NAND”);

puts(“4 – 2 loi vao cong NOR”);

puts(“5 – 2 loi vao cong NOT”);

scantf(“%d”, &gate_type);

printf(“Cac cua cua TTL tuong ung la: ”);

switch(colour)

{

Page 52: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

52

case AND: printf(“7408”); break;

case OR: printf(“7432”); break;

case NAND: printf(“RED”); break;

case NOR: printf(“OREANGE”); break;

case NOT: printf(“YELLOW”); break;

default:

printf(“Khong co mau sac”);

}

return(0);

}

5.2. Lệnh lặp

Một quá trình được tái diễn lại, hoặc lặp đi lặp lại cho phép xếp một tập các lệnh

thành một vòng kín. Tập các lệnh hay công việc được thực hiện trong vòng lặp cho đến

khi thỏa mãn một điều kiện cụ thể nào đó. Trong C Có ba dạng lặp đi lặp lại là các

vòng while, do và for.

5.2.1. Vòng lặp For Vòng lặp For cho phép chấp hành một khối mã dành cho một chức năng điều

khiển dược đặt trước.

Cú pháp:

for (Biểu thức 1; biểu thức 2; biểu thức 3)

<Công việc>

Lưu đồ cú pháp:

Page 53: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

53

Hình 5-4: Lưu đồ cú pháp câu lệnh for

Giải thích:

<Công việc>: được thể hiện là 1 câu lệnh hay 1 khối lệnh. Thứ tự thực hiện của

câu lệnh for như sau:

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

B2: Tính giá trị của biểu thức 2.

- Nếu giá trị của biểu thức 2 là sai (=0): thoát khỏi câu lệnh for.

- Nếu giá trị của biểu thức 2 là đúng (!=0): <Công việc> được thực hiện.

B3: Tính giá trị của biểu thức 3 và quay lại B2.

Một số lưu ý khi sử dụng câu lệnh for:

- Khi biểu thức 2 vắng mặt thì nó được coi là luôn luôn đúng

- Biểu thức 1: thông thường là một phép gán để khởi tạo giá trị ban đầu cho biến điều

kiện.

- Biểu thức 2: là một biểu thức kiểm tra điều kiện đúng sai để dừng vòng lặp (biểu thức

logic).

- Biểu thức 3: thông thường là một phép gán để thay đổi giá trị của biến điều kiện.

- Trong mỗi biểu thức có thể có nhiều biểu thức con. Các biểu thức con được

phân biệt bởi dấu phẩy.

Đúng

Sai

Bắt đầu

Thoát

Tính giá trị

Kiểm tra

Thực hiện

Tính giá trị

Page 54: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

54

Thí du5.4: Viết chương trình nhập vào một số nguyên n. Tính tổng của các số nguyên

từ 1 đến n.

#include <stdio.h>

#include<conio.h>

int main ()

{ unsigned int n,i,tong;

clrscr();

printf("\n Nhap vao so nguyen duong n:"); scanf("%d",&n);

tong=0;

for (i=1; i<=n; i++)

tong+=i;

printf("\n Tong tu 1 den %d =%d ",n,tong);

getch();

return 0;

}

Nếu nhập vào số 9 thì kết quả như sau:

Thí dụ 5.5. Viết chương trình chuyển đổi một số nguyên hệ thập phân sang hệ nhị

phân.

Giải thuật: Trước hết chương trình sẽ che bit có trọng số cao nhất (0x80) và xác

định nếu giá trị là true (nghĩa là bit khác 0) hoặc false (nghĩa là bit bằng 0). Nếu bit

được đặt thí số một được hiển thị; ngược lại số 0 sẽ được hiển thị. Sau đó mặt nạ bit

đổi chỗ về phía dưới một vị trí bằng cách sử dụng toán tử xử lý bit SHIFT phải (>>);

phép toán che một bit lại được thực hiện một lần nữa. Quá trị này sẽ tiếp tục cho đến

khi vòng lặp dạt tới bit có trọng số thấp nhất (cụ thể là 0x01 hay 0000 0001). Sau đó

vòng lặp kết thúc và chương trình dừng lại.

Vòng for sử dụng phép toán bit>>=1 (hay bit=bit>>1) để di chuyển mặt nạ bit đi

một vị trí sang bên phải.

#include <stdio.h>

Page 55: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

55

int main(void)

{

int val, bit;

printf("Nhap vao mot so he thap phan >>");

scanf("%d",&val);

printf("Dang nhi phan cua so vua nhap la: ");

for (bit=0x80; bit>0; bit>>=1)

{

if (bit & val) printf("1");

else printf("0");

}

return(0);

}

Kết quả khi chạy chương trình:

Nhap vao mot so he thap phan >> 8

Dang nhi phan cua so vua nhap la: 00001000

Thí dụ 5.6: Viết chương trình xác định điện áp sut trên điện trở ở nhưngc khoảng thời

gian cho trước.

Quá trình quá độ mạch RC với một bước nhảy điện áp đặt vào ở thời điểm t=0.

Khi một bước nhảy điện áp với biên độ V vôn được đặt vào mạch này sẽ gây ra một

dòng điện có hàm mũ.

Hình 5-5. Mạch RC với điện áp nhảy bậc ở lối vào tại t=0

Biểu thức sau đây cho phép xác định dòng điện tức thời trong mạch:

RCt

eREi

- .

t=0

i

E

R

C vc

Page 56: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

56

và điện áp sụt trên điện trở sẽ bằng:

RCt

R eEV-

.

Người dùng nhập vào thới gian kết thúc và các số khoảng thời gian cần có;

chương trình xác định điện áp ở mỗi bước thới gian. Nó sử dụng hàm số mũ exp()

trong tệp math.h

Kết quả chạy thử chương trình cho thấy điện áp sụt trên điện trở bắt đầu từ một

cực đại tại thời điểm t=0. Nguyên do là điện áp trên tụ điện thoạt đầu bằng 0. Khi tụ

điện được nạp điện, diện áp trên tụ sẽ tăng lên cho đến khi nó gần bằng với điện áp đặt

vào. Dòng điện trong mạch cũng đồng thời cực đại khi bước nhảy điện áp được đặt

vào. Sau đó điện áp sẽ giảm dần tới giá trị gần như bằng 0 với tốc độ được xác định

bởi hằng số thời gian, bằng tích số giữa R và C.

#include <math.h> /* required for exp() */

#include <stdio.h>

int main(void)

{

float R,C,tend,t,E,Vr;

int tsteps;

puts("Chuong trinh tinh dien ap sut tren mot mach RC ");

printf("Nhap R,C >> ");

scanf("%f %f",&R,&C);

printf("Nhap vao thoi gian ket thuc va so khoang thoi gian>>");

scanf("%d %f",&tsteps,&tend);

printf("Nhap vao die nap o moi buoc thoi gian >>");

scanf("%f",&E);

puts(" TIME VOLTAGE");

for (t=0;t<tend;t+=tend/tsteps)

{

Vr=E*exp(-t/(R*C));

printf("%8.4f %8.2f\n",t,Vr);

}

return(0);

Page 57: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

57

}

5.2.2. Vòng lặp while Vòng lặp while giống như vòng lặp for, dùng để lặp lại một công việc nào đó cho

đến khi điều kiện sai.

Cú pháp:

while (Biểu thức điều kiện)

<Công việc>

Lưu đồ cú pháp:

Hình 5-6: Lưu đồ cú pháp vòng lặp while

Giải thích:

- <Công việc>: được thể hiện bằng 1 câu lệnh hay 1 khối lệnh.

- Máy tính kiểm tra Biểu thức điều kiện trước.

- Nếu điều kiện sai (=0) thì thoát khỏi lệnh while.

- Nếu điều kiện đúng (!=0) thì thực hiện công việc rồi quay lại kiểm tra điều kiện tiếp.

Lưu ý:

Trong thân vòng lặp phải có ít nhất một câu lệnh làm thay đổi giá trị của biểu

thức điều kiện để chương trình thoát khỏi vòng lặp sau một số lần lặp hữu hạn.

5.2.3. Vòng lặp do… while Vòng lặp do … while giống như vòng lặp for, while, dùng để lặp lại một công việc nào

đó khi điều kiện còn đúng.

Đúng

Sai

Công việc

Thoát

Page 58: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

58

Cú pháp:

do

<Công việc>

while (<Biểu thức điều kiện>)

Lưu đồ cú pháp:

Hình 5-7: Lưu đồ cú pháp câu lệnh while

Giải thích:

- <Công việc>: được thể hiện bằng 1 câu lệnh hay 1 khối lệnh.

- Trước tiên công việc được thực hiện trước, sau đó mới kiểm tra Biểu thức điều kiện.

- Nếu điều kiện sai thì thoát khỏi lệnh do …while.

- Nếu điều kiện còn đúng thì thực hiện công việc rồi quay lại kiểm tra điều kiện tiếp.

Thí dụ 5-7: Viết chương trình nhập vào một danh sách các điện trở cho đến khi người

dùng nhập vào kí tự ‘k’ thì dừng chương trình

#include<stdio.h>

#include<conio.h>

typedef struct{

char ten[20];

float giatri;

} DT;

void main(){

Sai

Đúng

Thoát

Điều kiện

Công việc

Page 59: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

59

DT m[10];char ch;

int i=0; float t;

clrscr();

do{

puts("Nhap ten:"); gets(m[i].ten);

puts("nhap gia tri:");scanf("%f",&t); m[i].giatri=(float)t;

fflush(stdin);

puts("co tiep tuc nua ko(c/k)");

ch=getchar();

i++;

fflush(stdin);

}while(ch!='k');

// hien danh sach vua nhap

for(int j=0; j<i;j++)

printf("%10s %5.2f\n",m[j].ten,m[j].giatri);

getch();

}

Page 60: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

60

Chương 6 HÀM

6.1. Khái niệm Hàm Các hàm là các đoạn mã có thể nhận biết bằng một giao diện đã được định nghĩa.

Các hàm được gọi từ bất kỳ chỗ nào của chương trình và cho phép các chương trình

lớn được chia ra thành các công việc dễ quản lý hơn, mỗi một trong chúng có thể được

kiểm tra một các độc lập. Đồng thời các hàm tỏ ra có ích trong các thư viện xây dựng

các đoạn chương trình mà các chương trình khác sử dụng. Có tồn tại một vài thư viện

chuẩn như thư viện toán học, thư viện đầu vào, đầu ra.

Hình 6.1. Mô tả hàm như một “hộp đen” lý tưởng

Một hàm có thể được quan niệm như một “hộp đen” với một tập đầu vào đầu ra.

Bằng chức năng của mình nó xử lý các đầu vào theo cách “đọc chính tả” và cung cấp

một vài đầu ra. Trong đa số các trường hợp, hoạt động của hộp đen là vô hình đối với

phần còn lại của chương trình. Một chương trình cấu trúc theo kiểu module bao gồm

một số hộp đen làm việc độc lập với tất cả các hộp đen khác, và từng cái sử dụng các

biến khai báo bên trong nó và các tham số bất kỳ được gửi cho nó.

Hàm có hai loại: hàm chuẩn và hàm do người dùng định nghĩa.

6.1.1. Hàm người dùng Hàm người dùng là những hàm do người lập trình tự tạo ra nhằm đáp ứng nhu

cầu xử lý của mình.

Các lối vào khác làkhông thể

Tham sốvàoGiao diện

của hàm

Page 61: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

61

6.1.2. Khai báo Cấu trúc của một hàm tự định nghĩa:

<kiểu trả về> Tên hàm ([<kiểu tham số> <tham số>][,<kiểu tham số><tham

số>][…])

{

[Khai báo biến cục bộ]

<Các câu lệnh trong thân hàm>

[return [<Biểu thức>];]

}

Giải thích:

- Kiểu trả về: là kiểu dữ liệu của kết quả trả về, có thể là : int, byte, char, float, void…

Một hàm có thể có hoặc không có kết quả trả về. Trong trường hợp hàm không có kết

quả trả về ta nên sử dụng kiểu kết quả là void.

- Kiểu tham số: là kiểu dữ liệu của tham số.

- Tham số: là tham số truyền dữ liệu vào cho hàm, một hàm có thể có hoặc không có

tham số. Tham số này gọi là tham số hình thức, khi gọi hàm người lập trình phải truyền

cho nó các tham số thực. Nếu có nhiều tham số, mỗi tham số phân cách nhau dấu phẩy

(,).

- Bên trong thân hàm có thể khai báo các biến cụ bộ, các biến này chỉ tồn tại bên trong

hàm khi hàm đang được thực thi và sẽ bị xóa sau khi ra khỏi hàm.

- Lệnh return để trả về kết quả thông qua tên hàm.

6.1.3. Gọi hàm Một hàm khi định nghĩa sẽ không được thực thi cho đến khi có một lời gọi đến

hàm đó.

Cú pháp gọi hàm:

<Tên hàm>([Danh sách các tham số])

6.1.4. Nguyên tắc hoạt động của hàm Trong chương trình, khi gặp một lời gọi hàm thì hàm bắt đầu thực hiện bằng

cách chuyển các lệnh thi hành đến hàm được gọi.

Page 62: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

62

- Nếu hàm có tham số, trước tiên các tham số sẽ được gán giá trị thực tương ứng.

- Chương trình sẽ thực hiện tiếp các câu lệnh trong thân hàm bắt đầu từ lệnh đầu tiên

đến câu lệnh cuối cùng.

- Khi gặp lệnh return hoặc dấu } cuối cùng trong thân hàm, chương trình sẽ thoát khỏi

hàm để trở về chương trình gọi nó và thực hiện tiếp tục những câu lệnh của chương

trình này.

6.2. Chuyển giao tham số Việc truyền tham số cho hàm trong C là truyền theo giá trị; nghĩa là các giá trị

thực (tham số thực) không bị thay đổi giá trị khi truyền cho các tham số hình thức.

Khi chương trình con được gọi để thi hành, các tham trị được cấp ô nhớ và nhận

giá trị là bản sao giá trị của tham số thực. Do đó, mặc dù tham trị cũng là biến, nhưng

việc thay đổi giá trị của chúng không có ý nghĩa gì đối với bên ngoài hàm, không ảnh

hưởng đến chương trình chính, nghĩa là không làm ảnh hưởng đến tham số thực tương

ứng.

Nếu muốn sau khi kết thúc chương trình con giá trị của các tham số truyền vào

thay đổi thì phải đặt tham số hình thức là các con trỏ, còn tham số thực tế là địa chỉ

của các biến.

Thí dụ: Khai báo hàm:

void doiChoi(int *a, int *b){

}

void main(){

int x,y;

doiCho(&a, &b);

}

Lúc này mọi sự thay đổi trên vùng nhớ được quản lý bởi con trỏ là các tham số

hình thức của hàm thì sẽ ảnh hưởng đến vùng nhớ đang được quản lý bởi tham số thực

tế tương ứng. Cách này thường áp dụng cách này đối với các dữ liệu đầu ra của hàm.

6.3. Giá trị trả lại Lệnh return dùng để thoát khỏi một hàm và có thể trả về một giá trị nào đó.

Cú pháp:

Page 63: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

63

return ; /*không trả về giá trị*/

return <biểu thức>; /*Trả về giá trị của biểu thức*/

return (<biểu thức>); /*Trả về giá trị của biểu thức*/

Nếu hàm có kết quả trả về thì bắt buộc phải sử dụng câu lệnh return để trả về kết quả

cho hàm.

6.4. Kiểu hàm Kiểu của hàm có thể là một trong các kiểu cơ sở như: int, float, … hoặc là kiểu

do người dùng định nghĩa.

6.5. Thí dụ Lập trình hiển thị bảng chân lý của mạch logic có phương trình sau: CCABAZ ))..((

#include <stdio.h>#define FALSE 0#define TRUE 1int AND(int,int);int NAND(int,int);int NOR(int,int);int OR (int,int);int NOT(int);int main(void){

int a,b,c,z; puts(" A B C Z"); puts(" ***************************");

for (a=FALSE;a<=TRUE;a++) for (b=FALSE;b<=TRUE;b++) for (c=FALSE;c<=TRUE;c++) { z=NAND(OR(NOR(a,b),AND(a,c)),c); printf("%6d %6d %6d %6d\n",a,b,c,z); } return(0);}int AND(int x,int y){ if ( x && y ) return(TRUE); else return(FALSE);}int NAND(int x,int y){

Page 64: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

64

if ( x && y ) return(FALSE); else return(TRUE);}int OR(int x,int y){ if ( x || y ) return(TRUE); else return(FALSE);}int NOR(int x,int y){ if ( x || y ) return(FALSE); else return(TRUE);}int NOT(int x){ if (x) return(FALSE); else return(TRUE);}

Page 65: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

65

Chương 7 CON TRỎ

Giới thiệu

Các biến đã khai báo và sử dụng trước đây đều là biến có kích thước và kiểu dữ

liệu xác định. Người ta gọi các biến kiểu này là biến tĩnh. Khi khai báo biến tĩnh, một

lượng ô nhớ cho các biến này sẽ được cấp phát mà không cần biết trong quá trình thực

thi chương trình có sử dụng hết lượng ô nhớ này hay không. Mặt khác, các biến tĩnh

dạng này sẽ tồn tại trong suốt thời gian thực thi chương trình dù có những biến mà

chương trình chỉ sử dụng 1 lần.

Một số hạn chế có thể gặp phải khi sử dụng các biến tĩnh:

- Cấp phát ô nhớ dư, gây ra lãng phí ô nhớ.

- Cấp phát ô nhớ thiếu, chương trình thực thi bị lỗi.

Để tránh những hạn chế trên, ngôn ngữ C cung cấp một loại biến đặc biệt gọi là

biến động với các đặc điểm sau:

- Chỉ phát sinh trong quá trình thực hiện chương trình chứ không phát sinh lúc

bắt đầu chương trình.

- Khi chạy chương trình, kích thước của biến, vùng nhớ và địa chỉ vùng nhớ

được cấp phát cho biến có thể thay đổi.

- Sau khi sử dụng xong có thể giải phóng để tiết kiệm chỗ trong bộ nhớ.

Tuy nhiên các biến động không có địa chỉ nhất định nên không thể truy cập đến chúng

được. Vì thế, ngôn ngữ C lại cung cấp một loại biến đặc biệt nữa để khắc phục tình

trạng này, đó là biến con trỏ (pointer) với các đặc điểm:

- Biến con trỏ không chứa dữ liệu mà chỉ chứa địa chỉ của dữ liệu hay chứa địa

chỉ của ô nhớ chứa dữ liệu.

- Kích thước của biến con trỏ không phụ thuộc vào kiểu dữ liệu, luôn có kích

thước cố định là 2 byte.

Page 66: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

66

7.1. Con trỏ và địa chỉ

7.1.1. Khai báo biến con trỏ Cú pháp:

<Kiểu> * <Tên con trỏ>

Ý nghĩa: Khai báo một biến có tên là Tên con trỏ dùng để chứa địa chỉ của các

biến có kiểu Kiểu.

Thí dụ 1: Khai báo 2 biến a,b có kiểu int và 2 biến pa, pb là 2 biến con trỏ kiểu

int.

int a, b, *pa, *pb;

Khai báo biến f kiểu float và biến pf là con trỏ float

float f, *pf;

7.1.2. Gán địa chỉ của biến cho biến con trỏ Toán tử & dùng để định vị con trỏ đến địa chỉ của một biến đang làm việc.

Cú pháp: <Tên biến con trỏ>=&<Tên biến>

Giải thích: Gán địa chỉ của biến <Tên biến> cho con trỏ <Tên biến con trỏ>.

Thí dụ: Gán địa chỉ của biến a cho con trỏ pa, gán địa chỉ của biến b cho con trỏ pb.

pa=&a; pb=&b;

Lúc này, hình ảnh của các biến trong bộ nhớ được mô tả:

Khi gán địa chỉ của biến tĩnh cho con trỏ cần phải lưu ý kiểu dữ liệu của chúng.

Thí dụ sau đây không đúng do không tương thích kiểu:

int Bien_Nguyen;

float *Con_Tro_Thuc;

...

Con_Tro_Thuc=&Bien_Nguyen;

Page 67: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

67

Phép gán ở đây là sai vì Con_Tro_Thuc là một con trỏ kiểu float (nó chỉ có thể

chứa được địa chỉ của biến kiểu float); trong khi đó, Bien_Nguyen có kiểu int.

7.1.3. Nội dung của ô nhớ con trỏ chỉ tới Để truy cập đến nội dung của ô nhớ mà con trỏ chỉ tới thì sử dụng cú pháp:

*<Tên biến con trỏ>

Với cách truy cập này thì *<Tên biến con trỏ> có thể coi là một biến có kiểu

được mô tả trong phần khai báo biến con trỏ.

Thí dụ sau đây cho phép khai báo, gán địa chỉ cũng như lấy nội dung vùng nhớ

của biến con trỏ:

int x=100;

int *ptr;

ptr=&x;

int y= *ptr;

Khi gán địa chỉ của một biến cho một biến con trỏ, mọi sự thay đổi trên nội

dung ô nhớ con trỏ chỉ tới sẽ làm giá trị của biến thay đổi theo (thực chất nội dung ô

nhớ và biến chỉ là một).

7.1.4. Cấp phát vùng nhớ cho biến con trỏ

Trước khi sử dụng biến con trỏ phải cấp phát vùng nhớ cho biến con trỏ này

quản lý. Việc cấp phát được thực hiện nhờ các hàm malloc(), calloc() trong thư viện

alloc.h.

Cú pháp:

void *malloc(size_t size): Cấp phát vùng nhớ có kích thước là size.

void *calloc(size_t nitems, size_t size): Cấp phát vùng nhớ có kích thước là

nitems*size.

Thí dụ: Giả sử có khai báo:

int a, *pa, *pb;

pa = (int*)malloc(sizeof(int));

- Cấp phát vùng nhớ có kích thước bằng với kích thước của một số nguyên

pb= (int*)calloc(10, sizeof(int));

- Cấp phát vùng nhớ có thể chứa được 10 số nguyên

Page 68: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

68

- Lúc này hình ảnh trong bộ nhớ như sau:

Khi sử dụng hàm malloc() hay calloc() phải ép kiểu vì nguyên mẫu các hàm này

trả về con trỏ kiểu void.

7.1.5. Cấp phát lại vùng nhớ cho biến con trỏ

Trong quá trình thao tác trên biến con trỏ, nếu cần cấp phát thêm vùng nhớ có

kích thước lớn hơn vùng nhớ đã cấp phát thì sử dụng hàm realloc(). Cú pháp:

void *realloc(void *block, size_t size)

Ý nghĩa: - Cấp phát lại 1 vùng nhớ cho con trỏ block quản lý, vùng nhớ này có kích

thước mới là size; khi cấp phát lại thì nội dung vùng nhớ trước đó vẫn tồn tại.

- Kết quả trả về của hàm là địa chỉ đầu tiên của vùng nhớ mới. Địa chỉ này có

thể khác với địa chỉ được chỉ ra khi cấp phát ban đầu.

Thí dụ: Trong ví dụ trên, có thể cấp phát lại vùng nhớ do con trỏ pa quản lý như sau:

int a, *pa;

pa=(int*)malloc(sizeof(int)); /*Cấp phát vùng nhớ có kích thước 2 byte*/

realloc(pa, 6); /* Cấp phát lại vùng nhớ có kích thước 6 byte*/

7.1.6. Giải phóng vùng nhớ cho biến con trỏ

Một vùng nhớ đã cấp phát cho biến con trỏ, khi không còn sử dụng nữa phải thu

hồi lại vùng nhớ này nhờ hàm free(). Cú pháp:

void free(void *block)

Ý nghĩa: Giải phóng vùng nhớ được quản lý bởi con trỏ block.

Thí dụ: Ở ví dụ trên, sau khi thực hiện xong giải phóng vùng nhớ cho 2 biến con trỏ pa

& pb:

free(pa);

free(pb);

Page 69: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

69

7.2. Con trỏ và hàm

7.2.1. Con trỏ làm tham số hình thức trong hàmViệc truyền tham số cho hàm trong C là truyền theo giá trị; nghĩa là các giá trị

thực (tham số thực) không bị thay đổi giá trị khi truyền cho các tham số hình thức.

Nếu muốn sau khi kết thúc chương trình các giá trị của các tham số truyền vào

thay đổi thì phải đặt tham số hình thức là các con trỏ, còn tham số thực là địa chỉ của

các biến.

Lúc này mọi sự thay đổi trên vùng nhớ được quản lý bởi con trỏ là các tham số

hình thức của hàm thì sẽ ảnh hưởng đến vùng nhớ đang được quản lý bởi tham số thực

tương ứng (cần để ý rằng vùng nhớ này chính là các biến ta cần thay đổi giá trị).

7.2.2. Thí dụ

7.2.2.1. Đổi chỗ nội dung hai biếnThí dụ này minh họa việc sử dụng hàm con trỏ làm tham các tham số hình thức

trong hàm. Xây dựng một hàm swap để thực hiện đổi chỗ nội dung hai biến. Sau khi

hàm này được thực hiện sẽ đổi chỗ nội dung của hai biến.

#include <stdio.h>void swap(int *,int *);int main(void){int a,b;

a=5; b=6; swap(&a,&b); /* send address of a and b */ printf("a= %d b = %d \n",a,b); return(1);}void swap(int *ptr1,int *ptr2){ /* ptr1 and ptr2 are pointers (addresses). */int temp; temp = *ptr1; *ptr1 = *ptr2; *ptr2 = temp;}

Page 70: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

70

7.2.2.2. Điện trở tương tương của các điện trở mắc song song Thí dụ này sẽ sử dụng các con trỏ để để xác định diện trở tương đương của hai

mạch mắc song song.

#include<stdio.h>

void get_values(float *r1,float *r2);void get_parallel_res(float r1,float r2,float *r_e);void print_results(float r1,float r2,float r_e);

int main(void){float R1,R2,R_equ;

get_values(&R1,&R2); get_parallel_res(R1,R2,&R_equ); print_results(R1,R2,R_equ); return(1);}

void get_values(float *r1,float *r2){ do { printf("Enter R1 >>"); scanf("%f",r1); /*r1 la mot con tro do do khong can &r1 */ if (*r1<0) puts("KHONG HOP LE: nhap lai"); } while (*r1<0);

do { printf("Nhap R2 >>"); scanf("%f",r2); if (*r2<0) puts("KHONG HOP LE: nhap lai"); } while (*r2<0);}

void get_parallel_res(float r1,float r2,float *r_e){ *r_e=1/(1/r1+1/r2);}

void print_results(float r1,float r2,float r_e){ printf("Cac dien tro song song %8.3f and %8.3f ohm\n",r1,r2); printf("Dien tro tuong duong la %8.3f ohm\n",r_e);}

Page 71: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

71

7.3. Con trỏ và mảng một chiềuGiữa mảng và con trỏ có một sự liên hệ rất chặt chẽ. Những phần tử của mảng

có thể được xác định bằng chỉ số trong mảng, bên cạnh đó chúng cũng có thể được xác

lập qua biến con trỏ.

7.3.1 Truy cập các phần tử mảng theo dạng con trỏ Để truy cập các phần tử mảng sử dụng các quy tắc sau:

&<Tên mảng>[0] tương đương với <Tên mảng>

&<Tên mảng> [<Vị trí>] tương đương với <Tên mảng> + <Vị trí>

<Tên mảng>[<Vị trí>] tương đương với *(<Tên mảng> + <Vị trí>)

7.3.2. Truy xuất từng phần tử đang được quản lý bởi con trỏ theo dạng mảng <Tên biến>[<Vị trí>] tương đương với *(<Tên biến> + <Vị trí>)

&<Tên biến>[<Vị trí>] tương đương với (<Tên biến> + <Vị trí>)

Trong đó <Tên biến> là biến con trỏ, <Vị trí> là 1 biểu thức số nguyên.

7.3.3. Con trỏ chỉ đến phần tử mảng Giả sử con trỏ ptr chỉ đến phần tử a[i] nào đó của mảng a thì:

ptr + j chỉ đến phần tử thứ j sau a[i], tức a[i+j]

ptr - j chỉ đến phần tử đứng trước a[i], tức a[i-j]

7.4. Con trỏ và mảng nhiều chiều Có thể sử dụng con trỏ thay cho mảng nhiều chiều như sau: Giả sử có mảng 2

chiều và biến con trỏ như sau:

int a[n][m];

int *contro_int;

Thực hiện phép gán contro_int=a;

Khi đó phần tử a[0][0] được quản lý bởi contro_int;

a[0][1] được quản lý bởi contro_int+1;

a[0][2] được quản lý bởi contro_int+2;

...

a[1][0] được quản lý bởi contro_int+m;

a[1][1] được quản lý bởi contro_int+m+1;

Page 72: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

72

...

a[n][m] được quản lý bởi contro_int+n*m;

Tương tự như vậy đối với mảng nhiều hơn 2 chiều.

7.5. Các phép toán trên con trỏ

7.5.1. Phép gán con trỏHai con trỏ cùng kiểu có thể gán cho nhau.

Thí dụ:

int a, *p, *q ; float *f;

a = 5 ; p = &a ; q = p ; /* đúng */

f = p ; /* sai do khác kiểu */

Có thể ép kiểu con trỏ theo cú pháp:

(<Kiểu kết quả>*)<Tên con trỏ>

Chẳng hạn, ví dụ trên được viết lại:

int a, *p, *q ; float *f;

a = 5 ; p = &a ; q = p ; /* đúng */

f = (float*)p; /* Đúng nhờ ép kiểu*/

7.5.2. Cộng, trừ con trỏ với một số nguyên Có thể cộng (+), trừ (-) 1 con trỏ với 1 số nguyên N nào đó; kết quả trả về là 1

con trỏ. Con trỏ này chỉ đến vùng nhớ cách vùng nhớ của con trỏ hiện tại N phần tử.

Thí dụ: Cho đoạn chương trình sau:

int *pa;

pa = (int*) malloc(20); /* Cấp phát vùng nhớ 20 byte=10 số nguyên*/

int *pb, *pc;

pb = pa + 7;

pc = pb - 3;

Lúc này hình ảnh của pa, pb, pc như sau:

Page 73: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

73

7.5.3. Con trỏ NULLCon trỏ NULL là con trỏ không chứa địa chỉ nào cả. Ta có thể gán giá trị NULL cho 1

con trỏ có kiểu bất kỳ.

Lưu ý: - Không thể cộng 2 con trỏ với nhau.

- Phép trừ 2 con trỏ cùng kiểu sẽ trả về 1 giá trị nguyên (int). Đây chính là

khoảng cách (số phần tử) giữa 2 con trỏ đó. Chẳng hạn, trong ví dụ trên pc-pa=4.

7.6. Thí dụViết chương trình tính điện trở tương đương của các điện trở mắc song song.

#include<stdio.h>

void get_values(float *r1,float *r2);

void get_parallel_res(float r1,float r2,float *r_e);

void print_results(float r1,float r2,float r_e);

int main(void)

{

float R1,R2,R_equ;

get_values(&R1,&R2);

get_parallel_res(R1,R2,&R_equ);

print_results(R1,R2,R_equ);

return(1);

}

void get_values(float *r1,float *r2)

{

do

{

printf("Nhap R1 >>");

scanf("%f",r1);

if (*r1<0) puts("Gia tri khong hop le: nhap lai");

} while (*r1<0);

do

Page 74: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

74

{

printf("Nhap R2 >>");

scanf("%f",r2);

if (*r2<0) puts("Gia tri khong hop le: Nhap lai");

} while (*r2<0);

}

void get_parallel_res(float r1,float r2,float *r_e)

{

*r_e=1/(1/r1+1/r2);

}

void print_results(float r1,float r2,float r_e)

{

printf("Cac gia tri dien tro %8.3f va %8.3f ohm\n",r1,r2);

printf("Dien tro tuong duong cua mach la %8.3f ohm\n",r_e);

}

Page 75: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

75

Chương 8 CÁC LỆNH GHÉP NỐI VỚI MÁY TÍNH

8.1. Khái niệm Các thiết bị ngoại vi thường được định nghĩa không gian vào ra và được gán một

địa chỉ cổng xác định. Việc truy xuất các địa chỉ có thể được thực hiện trực tiếp bằng

các lệnh đọc hoặc ghi qua cổng vào ra.

8.2. Lệnh đưa dữ liệu ra máy tính Để đưa dữ liệu ra các cổng của máy tính ta sử dụng 2 hàm sau:

- Hàm outportb viết một byte ra cổng máy tính, cú pháp:

void outportb(int portid, unsigned char value);

trong đó:

- portid là địa chỉ cổng

- value là giá trị chứa trong 1 byte được viết ra cổng

- Hàm outport viết một từ ra cổng máy tính, cú pháp:

void outport(int portid, int value);

trong đó:

- portid là địa chỉ cổng

- value là giá trị chứa trong 2 byte được viết ra cổng

8.3. Lệnh lấy dữ liệu từ máy tính Để đọc dữ liệu từ các cổng của máy tính ta sử dụng 2 hàm sau:

- Hàm inportb cho phép đọc 1 byte từ cổng máy tính và trả về một giá trị kiểu

nguyên, cú pháp

int inportb(int portid); trong đó, portid là địa chỉ cổng cần đọc

- Hàm inport cho phép đọc 1 từ (word) từ cổng máy tính và trả về một giá trị kiểu

nguyên, cú pháp:

int inport(int portid); trong đó, pordid là địa chỉ cổng cần đọc

8.3. Một số kỹ thuật ghép nối với máy tính Các thiết bị ngoại vi có thể ghép nối và điều khiển bởi máy tính thông qua các

rãnh cắm PCI, ISA hay các cổng nối tiếp cổng song song, cổng USB,…Mỗi cổng có

Page 76: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

76

các đặc điểm vật lý, không gian địa chỉ riêng trên máy tính. Vì vậy, để ghép nối và

điều khiển thiết bị qua chúng, người dùng phải nắm vững các đặc điểm vật lý (như

chức năng của từng chân, điện áp định mức,…) để thiết kế các mạch điện ghép nối và

viết chương trình khiển phù hợp và an toàn cho máy tính và các thiết bị. Chương này

sẽ tập trung vào giới thiệu về kỹ thuật ghép nối qua cổng song song LPT và cổng nối

tiếp.

8.3.1. Cổng song song và cổng nối tiếp trong máy tính

Máy tính IBM/PC được trang bị 2 loại cổng giao tiếp với bên ngoài là cổng song

song và nối tiếp. Các cổng này là các ghép nối vật lý cho phép nối hệ thống vi tính với

các thiết bị ngoại vi như máy in, modem…

Cổng được gọi là song song vì nó cho phép truyền dữ liệu đồng thời các bít của

một byte dữ liệu trong một nhịp truyền.

Trong khi đó, cổng được gọi là nối tiếp chỉ cho phép truyền lần lượt mỗi nhịp một

bít trong một byte dữ liệu. Như vậy, cổng nối tiếp chỉ cần một dây truyền dữ liệu

nhưng lại phải cần ít nhất 8 nhịp truyền.

8.3.2. Cổng song song LPT (Line printer)8.3.2.1. Các chân của cổng LPT

Cổng song song LPT trong máy tính có đầu nối lọai D-25 theo chuẩn Centronics

trong hình 8.1.

Page 77: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

77

Hình 8.1 – Các đầu nối D-25 của cổng LPT

Trong hình 8.1, các chân từ :

- Các chân số 2 đến chân số 9 là các chân dữ liệu.

- Các chân 1, 14, 16,17 là các chân dẫn tín hiệu điều khiển

- Các chân 10, 11, 12, 13, 15 là chân trạng thái.

- Các chân từ 18 đến chân 25 là các chân nối đất.

Bảng dưới đây là một sơ đồ các dây của một cổng song song tiêu chuẩn.

Chân Mô tả I/O Chân Mô tả I/O1 -Strobe Out 14 -Auto Feed Out2 +Data Bit 0 Out 15 -Error In3 +Data Bit 1 Out 16 -Initialize Printer Out4 +Data Bit 2 Out 17 -Select Input Out5 +Data Bit 3 Out 18 -Data Bit 0 Return (GND) In6 +Data Bit 4 Out 19 -Data Bit 1 Return (GND) In7 +Data Bit 5 Out 20 -Data Bit 2 Return (GND) In8 +Data Bit 6 Out 21 -Data Bit 3 Return (GND) In9 +Data Bit 7 Out 22 -Data Bit 4 Return (GND) In10 -Acknowledge In 23 -Data Bit 5 Return (GND) In11 +Busy In 24 -Data Bit 6 Return (GND) In12 +Paper End In 25 -Data Bit 7 Return (GND) In13 +Select In

Bảng 8-1: Bảng mô tả các chân nối của cổng LPT

Page 78: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

78

Trong quá trình khởi động máy tính, ROM BIOS sẽ kiểm tra 3 nhóm cổng vào/ra

lần lượt từ 3BCh đến 378h và 278h để xem liệu có phần cứng cổng song song nào

được lắp trên đó không.

8.3.2.2. Ghép nối máy in qua cổng LPT

Mỗi cổng LPT có 3 thanh ghi có thể thâm nhập quan 3 địa chỉ cổng vào ra gắn

cho chúng. Mỗi bít trong thanh ghi trạng thái và điều khiển mang các ý nghĩa xác định

khi nhận các giá trị 0 hoặc 1.

Bít Ý nghĩa thanh ghi trạng thái

S7 = 1 nếu máy in không bận

S6 = 0 nếu máy in đã sẵn sang nhận ký tự tiếp theo

S5 = 1 nếu máy in không có giấy

S4 = 1 máy in ở trạng thái on-line

S3 = 0 có lỗi

S2 Không dùng

S1 Không dùng

S0 Không dùng

Bảng 8-2: Ý nghĩa các bit trong thanh ghi trạng thái

Bít Ý nghĩa thanh ghi điều khiển

C7 Không dùng

C6 Không dùng

C5 Không dùng

C4 =1 Chạy ngắt khi ACK=0

C3 = 1 chọn máy in on-line

C2 =0 khởi động máy in

C1 =1 tự động xuống dòng

C0 =1 truyền số liệu tới máy in

Bảng 8-2: Ý nghĩa các bit trong thanh ghi điều khiển

Page 79: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

79

Các thanh ghi này được dùng để gửi số liệu tới máy in, xác định trạng thái máy in

và điều khiển hoạt động của máy in.

- Thanh ghi số liệu được thâm nhập qua địa chỉ cơ sở (vd: 378), hầu hết các máy PC

hiện nay đếu cho phép các thanh ghi này cả viết, cả đọc số liệu theo 2 hướng.

- Thanh ghi trạng thái có địa chỉ bằng địa chỉ cơ sở cộng 1 (vd: 379). Đây là thanh ghi

chỉ đọc. Có thể đọc trược tiếp bằng lệnh vào/ra hoặc dùng ngắt 17, hàm 02h trong

BIOS.

- Thanh ghi điều khiển được thâm nhập thông qua địa chỉ cơ sở công 3 (vd: 37Ah) và

có thể dùng cho cả đọc lẫn viết dữ lieu ra thanh ghi.

Vì tốc độ truyền ký tự của cổng song song nhanh hơn tốc độ in của máy in nên

thông tin ở đây phải có móc nối như hình 8-2. Khởi đâu, PC đặt các số liệu lên bus sau

đó kích hoạt đường xuống mức thấp để thông tin cho máy in biết rằng số liệu đãSTB

ổn định trên bus. Khi máy in xử lý xong dữ liệu, nó sẽ trả lại tín hiệu xuống mứcACK

thấp để ghi nhận. Máy tính sẽ đợi cho đến khi đường Busy trên máy in xuống thấp thì

sẽ đưa số liệu lên bus.

Hình 8-2: Trao đổi thông tin với máy tính qua cổng LPT

8.3.3. Ghép nối qua cổng nối tiếp Thông tin nối tiếp dùng trong rất nhiều ứng dụng ghép nối với máy vi tính như

chuột, modem, máy vẽ,…Vì dữ liệu được truyền song song trên các bus máy tính nên

để truyền nối tiếp được, bên phát dùng các thanh ghi dịch song song – nối tiếp để biến

Busy

ACK

STB

Số liệuỔn định

Page 80: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

80

đổi các byte song song thành một chuỗi bít nối tiếp nhau cho ra cổng và bên thu phải

dùng các thanh ghi dịch nối tiếp – song song để biến đổi ngược lại.

Thông tin nối tiếp được phân biệt thành 2 loại: truyền nối tiếp đồng bộ và không

đồng bộ. Khi truyền nối tiếp đồng bộ, ngoài đường dây truyền số liệu giữa hai trạm

phát và thu cần có một đường đây điều khiển để truyền tín hiệu nhịp để bên thu xác

định được các thời điểm tại đó số liệu trên đường truyền đã ổn định. Trong khi đó,

truyền nối tiếp không đồng bộ chỉ cần một dây truyền trong đó các thông tin đồng bộ

được truyền ngay cùng với các từ dữ liệu. Đó là bít start chỉ thị sự bắt đầu của khối dữ

liệu được truyền và bít stop báo kết thúc khối dữ liệu được truyền cùng với một số bit

phát hiện và sửa lỗi được ghép cùng các bít số liệu để tạo thành một khung truyền

(frame) hay một USD (serial data unit).

Giữa hai cổng thông tin nối tiếp có thể có các phương thức trao đổi thông tin như

sau:

- Nối đơn công (Simplex Connection): số liệu chỉ được truyền theo một hướng.

- Bán song công (Half-Duplex): số liệu truyền theo hai hướng , nhưng mỗi thời

điểm chỉ truyền theo một hướng.

- Song công (Full-Duplex): số liệu được truyền thông thời theo cả hai hướng.

8.3.2.1. Chuẩn thông tin nối tiếp

Do hạn chế về dải tần của đường truyền nên hầu hết các thiết bị đầu cuối số liệu

DTE (Data Terminal Equipment) muốn thông tin với nhau không thể nối trực tiếp với

môi trường truyền dẫn analog được mà phải thông qua các thiết bị thông tin số liệu

DCE (Data Communication Equipment). Thí dụ, việc thông tin giữa hai máy tính hoặc

máy fax là các DTE qua đường điện thoại công cộng phải được nối qua hai thiết bị

DCE là các Modem.

Các tiêu chuẩn chính cho thông tin số liệu nối tiếp hiện nay được xây dựng bởi

các tổ chức ITU(International Telecommunication Union), EIA (Electronics Industry

Association) và ISO (International Standards Organisation).

Chuẩn RS-232C quy định các ghép nối tiếp giữa một DTE và một DCE với

khoảng cách cực đại là 17m đến 20m và tốc độ truyền số liệu cực đại lên đến 20 kbps.

Page 81: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

81

Tín hiệu theo chuẩn RS-232C là lưỡng cực: mức logic 1 có điện thế dương so với

đất (0V), mức 0 có điện thế dương so với đất.

Với tín hiệu ra: mức lôgic cao có điện thế trong dải từ +5V đến +15V

mức lôgic thấp từ -5V đến -15V

Với tín hiệu vào: mức lôgic cao từ +3V đến +15V

mức lôgic thấp từ -3V đến -15V

Với các máy PC mức điện thế điển hình là 12V.

Dữ liệu được truyền lần lượt theo từng nhóm bít. Mỗi nhóm gọi là một khung dữ

liệu hay một SDU. Một khung truyền bao gồm:

1 bit start luôn ở mức lôgic thấp, điện thế dương.

1 hoặc 1,5 bit hoặc 2 bít stop luôn ở mức logic cáo, diện thế âm.

1 hoặc không có một bít kiểm tra chẵn lẻ,

Hoặc 5,6 hoặc 7 bít số liệu.

Thí dụ, khi truyền đại diện các ký tự (với mã ASCII là 7 bit) được truyền trên đường

dây dẫn lần lượt với khoảng thời gian trễn giữa chúng. Trong khoảng thời gian này

đường truyền ở vào trạng thái MARK (mức logic cao). Hình 8.3 là một thí dụ nhận

được trên trên đường truyền TxD hoặc RxD khi truyền các bit thông tin mà hoăc

ASCII cho 2 chữ ‘A’ với bít chẵn lẻ.

Hình 8.3. Tín hiệu trên đường truyền của các bít biểu diễn 2 kí tự ‘A’

Tốc độ truyền số liệu nôi tiếp được đo bằng số bít truyền trong một giây bps.

Trong các hệ thống truyền số liệu với mã nhị phân tốc độ này trùng với tốc độ baud là

số lần thay đổi tín hiệu trạng thái trong một giây. Hầu hết các máy PC đều có cổng

ghép nối thông tin nối tiếp. Cổng sơ cấp gọi là COM1 (hoặc COM3), cổng thứ cấp gọi

là COM2 (hoặc COM4). Có hai loại đầu cắm cho các tín hiệu này là D-25 (25 chân) và

Page 82: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

82

D-9 (9 chân). Các đầu cắm cho các cổng nôi tiếp trên các hộp máy bao giờ cũng là đầu

cắm được (male), đầu cắm nối ra ở các thiết bị ngoại vi là các đầu cắm cái.

Các trên tín hiệu trên các đầu cắm được nối ra các đường dây để các thiết bị DTE và

DCE thông tin với nhau. Ngoài ra dây GND có điện thế 0V, có thể phân thành 2 nhánh

đường dây gồm các đường truyền dữ liệu TxD, RxD và các nhóm các đường tín hiệu

điều khiển (gọi là các tín hiệu móc nối thông tin) gồm các đường còn lại. Bảng sau đây

mô tả tên và chức năng các đường tín hiệu:

Chấn sô

D-25 D-9

Tên Kí hiệu Chức năng

1 - Frame Ground FG Thường được nối với

vỏ bọc kim của cáp

dẫn hoặc đất

2 3 Transmit Data TxD Số liệu được phát từ

DTE (thí dụ PC hoặc

thiết bị đầu cuối) tới

DCE qua đường

TxD

3 2 Receive Data RxD Số liệu thu từ DCE

vào DTE

4 7 Request to send RTS DTE đặt đường này

ở mức tích cự khi

sẵn sàng phát số liệu

5 8 Clear to send CTS DCE đặt đường này

ở mức tích cực để

báo cho DTE rằng

nó sẵn sang nhận số

liệu

6 6 Data set ready DSR Chức năng tương tự

như CTS nhưng

được kích hoạt bởi

Page 83: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

83

DTE khi nó sẵn sàng

nhận số liệu

20 4 Data terminal

Ready

DTR Chức năng tương tự

như RTS nhưng

được kích hoạt bởi

DTE khi nó muốn

phát số liệu

8 1 Data carier

Detect

DCD DCE đặt đường này

ở mức tích cực để

báo cho DTE biết là

đã thiết lập được lien

kết với DCE từ xa

(nhận được song

mang từ bên DCE

đối tác)

22 9 Ring indicator RI DCE báo cho DTE

biết có một cuộc gọi

từ xa vừa gọi đến

7 5 Signal Groud SG GND

Bảng 8.1. Mô tả chức năng của các đường tín hiệu

Các chuẩn RS-422A (có đường truyền cân bằng) và RS-422B có đường truyền

không cân bằng cho phép tăng tốc độ truyền số liệu và khoảng cách thông tin lớn hơn

nữa.

Chuẩn RS-485 là một chuẩn tăng cường của chuẩn RS-422, nó quy định cho các

ghép nối thông tin đa điểm và song công, do vậy rất thích hợp cho các ứng dụng trên

mạng máy tính. Tốc độ truyền cực đại không hạn chế và được đặt bởi thời gian tăng

cường của sường xung, thương cớ 10Mbps. Với độ dài cap 1,2km mạng máy tính dùng

chuẩn RS-485 cho phép nối tới 32 cặp máy thu/phát.

Đường dây dẫn tín hiệu ảnh hưởng đến các xung số liệu ở 3 trường hợp sau:

- Làm suy giảm biên độ xung: do điện trở của đường dây.

Page 84: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

84

- Làm méo dạng xung: do diện dung kí sinh giữa các cặp đường dây, điện trở và

điện cảm của đường dây gây nên méo dạng pha và giảm dải đường truyền.

- Gây ồn do ồn nội (ồn nhiệt của đường dây) và ồn ngoại là các tác nhân bên

ngoài tác động lên đường truyền.

Ngoài đường dây (dây đất), các đường truyền không cân bằng (như theo chuẩn

RS-423) sử dụng đường dây cho tín hiệu số trong khi các đường truyền cân bằng (như

theo chuẩn RS-422) sử dụng hai đường dây cho mỗi tín hiệu như các hình 8.9 và 8.10

chỉ ra. Dòng điện trọng 2 dây dẫn của đường truyền cân bằng ngược pha với nhau. Do

vây, đường truyền này ít chịu ảnh hường của ồn ngoại vì các nguồn này cùng gây nên

trên dây dẫn cường độ ồn bằng nhau về độ lớn nhưng co pha ngược nhau. Khi hoạt

động ở tốc độ cao, các đầu dây dẫn thường được nối với các terminator có trở kháng

bằng trở kháng đặc tính (trở sóng) của đường dây (thí dụ, 50 hay 70). Các mạch ghép

nối theo chuẩn RS-422 có thể tải được tới 10 máy thu. Chúng không có đường dây nối

đất do vậy được dùng rất tốt khi cần phải cách ly giữa hai mạng thông tin. Nếu sử dụng

tốc độ truyền thấp thì có thể tăng được khoảng cách thông tin lên. Thí dụ, trong một

vài trường hợp khi chất lượng cáp dẫn tốt và môi trường ít nhiễu thì có thể sử dụng các

thiết bị với chuẩn RS-232 thông tin với nhau ở khoảng cách lên tới 1 km với tốc độ

1200bps.

Hình 8.4: Đường truyền ghép nối không cân bằng

Hinhg 8.5: Đường truyền ghép cân bằng

EIA RS-232C RS-423A RS-422A RS-485

Page 85: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

85

ITU V.28 V.10/X26 V.11/X27

Tốc độ truyền số liệu 20kbps 300kbps 10Mbps 10Mbps

Khoảng cách cực đại 17-20m 1200m 1200m 1200m

Loại nối tải Không cân bằng Không cân bằng vi

sai

Cân bằng vi sai Cân bằng vi sai

Số cặp thu phát 1 1 1 32

Điện thê điều khiển ± 3V ÷ ± 15V ± 2V ÷ ± 5V ± 2V ÷ ± 5V ± 2V ÷ ± 5V

Số đường dây cho

một tín hiệu

1 2 2 2

Bảng 8.2: Bảng các thông số của các chuẩn ghép nối thông tin cơ bản.

8.3.2.2. Điều khiển thông tin nối tiếp giữa hai thiết bị

Tùy thuộc vào sự tương quan giữa tốc độ truyền và nhận của bên phát và thu mà

có thể cần hoặc không cần phải điều khiển thông tin. Khi máy thu nhận được ký tự thu,

nó lưu giữ trong bộ đệm thu trước khi đọc. Bộ đệm thu điển hình chỉ lưu trữ một ký tự.

Vì vậy, nếu máy thu đọc được các ký tự thu trước khi máy phát truyền ký tự

tiếp theo thì không cần phải điều khiển thông tin. Đó là thông tin không có móc nối

(no-handshaking) . Ngược lại, nếu máy thu không đọc kịp ký tự lưu trong bộ đệm trước

khi máy phát truyền ký tự tiếp theo thì sẽ có hiện tượng ghi đè ký tự, dẫn đến mất

thông tin. Để tránh hiện tượng này thì cần phải có điều khiển thông tin giữa bên phát

và bên thu. Kỹ thuật điều khiển thông tin giữa hai thiết bị được gọi là kỹ thuật móc nối

(handshaking). Đó là việc buộc máy phát ngừng truyền cho đến khi máy thu đọc xong

ký tự được lưu trong bộ đệm thu, tức là cho đến khi bộ thu rỗng.

Có hai kỹ thuật móc nối:

- Móc nối cứng (Hardware Handshaking): Dùng các đường điều khiển móc nối

như CTS, RTS, DTR và DSR.

- Móc nối mềm (Software Handshaking): liên quan tới việc gửi các ký tự điều

khiển đặc biệt như các ký tự từ D1 đến D4 trong bảng mã ASCII để truyền thông tin

móc nối giữa bên phát và bên thu.

Page 86: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

86

Hình 8.3 biểu diễn sơ đồ nối các đường dây giữa bên phát và bên thu trong

thông tin không móc nối. Trong trường hợp này, máy thu có thể đọc số liệu nhận được

từ bộ đệm trước khi ký tự khác được gửi đến bộ đệm. Số liệu được truyền từ đầu TxD

của máy phát và được nhận ở đầu RxD ở bên thu. Khi một DTE (ví dụ như PC) cần

nối với một DTE khác thì đường TxD ở một bên được nối với đường RxD ở bên kia và

ngược lại. Các đầu tín hiệu móc nối ở mỗi bên được nối cặp với nhau (RTS nối trực

tiếp với CTS, DTR nối với DSR) nhằm làm cho các máy luôn ở trạng thái sẵn sàng thu

và truyền.

Hình 8.3 Sơ đồ nối dây giữa hai cổng RS-232C theo kiểu không móc nối

Hình 8.4 là sơ đồ nối dây trong thông tin có móc nối cứng giữa hai DTE với

nhau.

Hình 8.4 Thông tin RS- 232C có móc nối cứng

Các đường điều khiển được dùng ở mức tích cực cao. Khi một máy muốn phát

số liệu, chân RTS được đặt ở mức cao rồi kiểm tra trạng thái chân CTS cho đến khi

chân này ở mức tích cực cao. Chừng nào chân CTS còn ở mức thấp thì bên thu vẫn còn

Page 87: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

87

bận và chưa thể nhận số liệu. Khi máy thu đọc xong số liệu từ bộ đệm thu của nó,

đường RTS nối với CTS của máy phát sẽ tự động nhảy lên mức cao để chỉ thị cho máy

phát biết rằng bên thu đã sẵn sàng nhận số liệu tiếp. Quá trình nhận số liệu cũng giống

như quá trình truyền. Khi DCE muốn phát tới DTE, đầu vào DSR bên máy thu sẽ trở

nên tích cực. Nếu máy thu không thể nhận số liệu nó sẽ đặt chân DTR ở mức không

tích cực. Khi nó ở trạng thái xóa nhận, chân DTR được đặt lên mức cao và máy bên

kia có thể phát số liệu. Khi nối DTE và DCE thì các đường móc nối được nối với nhau

như hình 8.5 sau:

Hình 8.5 Sơ đồ nối DTE với DCE

Hình 8.6 là sơ đồ biểu diễn quá trình thông tin sử dụng kỹ thuật móc nối mềm.

Có hai ký tự mã ASCII được dùng để khởi phát và dừng truyền số liệu.

Hình 8.6 Móc nối mềm sử dụng hai ký tự X-ON và X-OFF

Đó là ký tự DC1 trong bảng mã ASCII có giá trị là 11h, gọi là X-ON và ký tự

DC3 có giá trị là 13h, gọi là X-OFF. Khi máy phát nhận được ký tự X-OFF, nó sẽ

ngưng phát dữ liệu cho đến khi nhận được ký tự X-ON. Loại móc nối này thường được

Page 88: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

88

dùng trong các trường hợp khi máy phát và máy thu có thể xử lý số liệu tương đối

nhanh. Thường thì máy thu phải có một bộ đệm số liệu lớn chứa các ký tự nhận được.

Khi bộ đệm đầy, nó sẽ truyền sang bên phát ký tự X-OFF, sau khi đọc xong nội dung

bộ đệm, nó sẽ truyền ký tự X-ON.

8.3.2.3. Lập trình thông tin nối tiếp

a. Lập trình trực tiếp với các cổng nối tiếp

Các chíp dùng cho mục đích truyền thông nối tiếp trong các máy tính thường

được lắp ngay trên bản mạch chính hoặc trên bản mạch ghép nối vào/ra cắm trên khi

mở rộng của máy, cho phép truyền số liệu với các tốc độ tối đa 9600 bps (PC/XT), hay

19200 bps (máy AT).

Địa chỉ của cổng nối tiếp COM và một số thanh ghi như sau:

Địa chỉ cơ sở Đệm số liệu phát và thu (TxD/RxD) Địa chỉ cơ sở

COM 1 = 3F8h Cho phép ngắtĐịa chỉ cơ sở

+1

COM 2 = 2F8h Nhận dạng ngắt Địa chỉ cơ sở

+2

Định dạng số liệu (LCR)Địa chỉ cơ sở

+3

Điều khiển ModemĐịa chỉ cơ sở

+4

Trạng thái đường truyềnĐịa chỉ cơ sở

+5

Trạng thái ModemĐịa chỉ cơ sở

+6

Lưu trữ đọc/viết tạm thờiĐịa chỉ cơ sở

+7

Các thanh ghi đệm số liệu phát và thu có cùng một địa chỉ bằng địa chỉ cơ sở.

Page 89: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

89

- Thanh ghi trạng thái đường truyền xác định trạng thái của các bộ đệm thu và

phát. Đây là thanh ghi chỉ đọc (tất cả các bit được ghi tự động bởi phần cứng). Định

dạng của từ trạng thái như sau:

0 D6 D5 D4 D3 D2 D1 D0

D7 = 0 D3 = 1: lỗi khung truyềnD6 = 1: bộ đệm phát rỗng D2 = 1: lỗi chẵn lẻD5 = 1: thanh ghi đệm phát rỗng D1 = 1: lỗi tràn số

D4 = 1: phát hiện điểm dừng D0 = 1: số liệu đã sẵnsàng

Khi truyền dữ liệu, một ký tự mới có thể được viết tới bộ đệm trước khi ký tự

trước đọ được gửi. Để tránh điều này, bít D6 được kiểm tra xem bộ đệm đã rỗng hay

chưa. Bộ đệm phát rỗng khi D6 là bit ‘1’. Vậy để gửi một ký tự, ta phải sử dụng vòng

lặp kiểm tra sau:

Kiểm tra bit D6 cho tới khi D6 =1

Gửi ký tự

Do{

Trang_thai = import [địa chỉ LSR] and $40;

} while (trang_thai = $40);

Khi thu số liệu, bit D0 được kiểm tra để xác định xem đã có số liệu trong bộ đệm thu

chưa theo vòng lặp sau:

Kiểm tra bit D0 cho tới khi D0 =1

Đọc ký tự

Do{

Trang_thai = import [địa chỉ LSR] and $01;

} while (trang_thai = $01);

- Thanh ghi định dạng dữ liệu (LCR) khởi tạo các thông số thông tin như số bit

cho một ký tự, số bit chẵn lẻ, số bit stop. Nó có thể được viết hoặc đọc. Định dạng các

bit điều khiển trong thanh ghi như sau:

Page 90: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

90

D7= 0: đọc/ghi máy thu/phát= 1: chốt bộ chia tốc độbaud

D6 = 1: phát một điểm dừng

D5 D4D3

Tổ hợp bit chẵn lẻ= 000: không có= 001: chẵn lẻ - lẻ= 011: chẵn lẻ - chẵn= 101: luôn bằng 1 (mark)= 111: luôn bằng 0 (space)

D2Số bit stop:= 0: 1 bit stop= 1: 1,5 bit stop

D1 D0

Số bit số liệu:= 00: 5 bit= 01: 6 bit=10: 7 bit= 11: 8 bit

Để thâm nhập thanh ghi đệm thu/phát bit D7 phải được xóa bằng 0. Còn khi

muốn khởi tạo bộ chia tốc độ baud, bit được đặt lên 1. Tốc độ baud được đặt bằng việc

nạp một hệ số chia N, 16 bit vào địa chỉ của bộ đệm số liệu thu/phát và địa chỉ tiếp

theo. Giá trị được nạp phụ thuộc vào tần số máy phát sóng thạch anh nối với UART.

Thường tần số này bằng 1,8432 MHz. Do đó:

Tốc độ baud = tần số xung nhịp / (16 x N)

Bảng sau đưa ra một số hệ số chia N (được nạp vào thanh ghi đệm số liệu

TxD/RxD) và các tốc độ baud tương ứng:

Tốc độ Baud Hệ số chia Tốc độ Baud Hệ số chia

110 0417h 2400 0030h

300 0180h 4800 0018h

600 00C0h 9600 000Ch

1200 0060h 19200 0006h

1800 0040h

Để nạp hệ số chia tốc độ baud, trước hết bit D7 của thanh ghi LCR được đặt lên

1. Sau đó byte thấp của hệ số N được nạp vào địa chỉ thanh ghi đệm số liệu rồi byte

cao được nạp tiếp vào địa chỉ lớn hơn 1. Cuối cùng, bit 7 được xóa về 0.

Page 91: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

91

Ví dụ, để có tốc độ 9600 baud với tần số nhịp là 1,8432 MHz ở cổng COM 1,

hệ số chia N = 000Ch nên trước hết số 0Ch (byte thấp) được nạp vào địa chỉ 3F8h, sau

đó số 00h được nạp vào địa chỉ 3F9h. Khi bit D7 được xóa về 0, một thao tác đọc từ

địa chỉ cơ sở sẽ đọc từ bộ đệm RxD và một thao tác viết sẽ viết tới bộ đệm TxD.

8.4. Thí dụ và một số đề án ứng dụng

Đề án 1. Xây dựng chương trình điều khiển bật tắt các bóng điện công suất

40W-220V. (4 sinh viên)

Yêu cầu:

Phần cứng:

- Mạch ghép nối qua cổng LPT

Phần mềm:

- Chương trình viết bằng C thỏa mãn điều khiện:

+ ) Người dùng nhấn vào phim “t” trên bàn phím thì bóng điện được bật

lên.

+) Người dùng nhấn vào phím “o” trên bàn phím thì bóng điện tắt.

Tài liệu báo cáo:

- Bản báo cáo phân tích chi tiết các thiết kế phần cứng, phần mềm, sơ đồ,

nguyên lý hoạt động.

Đề án 2. Viết chương trình thực hiện các phép toán cộng, trừ, nhân, chia các số

nhỏ hơn 10 rồi hiện kết quả lên 1 LED 7 thanh ghép nối qua cổng LPT. (4 sinh

viên).

Yêu cầu:

Phần cứng:

- Mạch ghép nối đèn LED 7 thanh qua cổng LPT

Phần mềm:

- Chương trình điều khiển viết băng C thỏa mãn các điều kiện:

+) Khi người dùng nhập số 1 – thực hiện phép cộng, nhập 2- thực hiện

phép trừ, nhập 3 – thực hiện phép nhân, nhập 4 – thực hiện phép chia.

+) Khi người dùng nhấn một phím số trên bàn phím thì chữ số được hiển

thị trên đèn LED 7 thanh.

Page 92: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

92

+ Chương trình cho phép cộng 2 số và hiển thị kết quả trên đèn LED.

Tài liệu báo cáo:

- Bản báo cáo phân tích chi tiết các thiết kế phần cứng, phần mềm, sơ đồ,

nguyên lý hoạt động.

Đề án 3. Thiết kế mạch điện ghép nối 4 đèn LED 7 thanh với cổng LPT. Viết

chương trình hiển thị đồng hồ hệ thống của máy tính có dạng HH:MM trên 4 đèn

LED. (4 sinh viên)

Yêu cầu:

Phần cứng:

- Mạch ghép nối 4 đèn LED 7 thanh ghép nối với cổng LPT

Phần mềm:

- Chương trình C điều khiển hiển thị giời của hệ thống theo định dạng HH:MM

trên 4 đèn LED.

Tài liệu báo cáo:

- Bản báo cáo phân tích chi tiết các thiết kế phần cứng, phần mềm, sơ đồ,

nguyên lý hoạt động.

Đề án 4. Thiết kế mạch điện mô phỏng đèn giao thông tại một ngã tư sử dụng 2 bộ

đèn, mỗi bộ đèn gồm 1 đèn LED đỏ, một đèn LED xanh, một đèn LED vàng và 1

đèn LED 7 thanh. Viết chương trình điều khiển hệ thống đèn giao thông. (4 sinh

viên)

Yêu cầu:

Phần cứng:

- Mạch ghép nối các bộ đèn qua cổng LPT.

Phần mềm:

- Chương trình C điều khiển hệ thống đèn, LED 7 thanh hiển thị số giây (<10)

dừng lại hoặc được đi cho mỗi hướng của đường.

Tài liệu báo cáo:

Page 93: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

93

- Bản báo cáo phân tích chi tiết các thiết kế phần cứng, phần mềm, sơ đồ,

nguyên lý hoạt động.

Đề án 5. Xây dựng chương trình hiển thị chữ D T V T K 6 lần lượt trên một ma

trận LED 8x8 ghép nối qua cổng LPT của máy tính. (5 sinh viên)

Yêu cầu:

Phần cứng:

- Mạch ghép nối qua cổng LPT

Phần mềm:

- Chương trình C hiển thị lần lượt từng chữ D T V T K 6, mỗi chữ cách nhau

2s.

Tài liệu báo cáo:

- Bản báo cáo phân tích chi tiết các thiết kế phần cứng, phần mềm, sơ đồ,

nguyên lý hoạt động.

Đề án 6. Thiết kế mạch điện ghép nối một mạch phát còi báo động qua cổng LPT.

Viết chương trình điều khiển việc phát còi to dần. (4 sinh viên).

Yêu cầu

Phần cứng:

- Mạch ghép nối qua cổng LPT

Phần mềm:

- Chương trình C điều khiển việc phát còi to dần sau 1 phút thì dừng lại.

Tài liệu báo cáo:

- Bản báo cáo phân tích chi tiết các thiết kế phần cứng, phần mềm, sơ đồ,

nguyên lý hoạt động.

Đề án 7: Thiết kế mạch điện ghép nối 8 đèn LED với cổng LPT của máy tính. Viết

chương trình hiển thị các số từ 00h đến ffh trên dãy dèn LED dưới dạng nhi phân

(5 sinh viên).

Yếu cầu:

Phần cứng:

- Mạch ghép nối qua cổng LPT.

Page 94: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

94

Phần mềm:

- Chương trình C điều khiển việc hiển thị các số từ 00h đên ffh, mỗi số

cách nhau 2s.

Tài liệu báo cáo:

- Bản báo cáo phân tích chi tiết các thiết kế phần cứng, phần mềm, sơ đồ,

nguyên lý hoạt động.

Đề án 8: Thiết kế mạch điện ghép nối một LCD với cổng LPT của máy tính. Viết

chương trình điều khiển hiển thị dòng chữ DTK6 trên LCD.

Yêu cầu:

Phần cứng:

- Mạch ghép nối LCD với cổng LPT với kích thước của LCD tự chọn sao cho

phù hợp.

Phần mềm:

- Chương trình C điều khiển việc hiển thị chữ DTK6 chạy từ trái sang phải trên

LCD.

Tài liệu báo cáo:

- Bản báo cáo phân tích chi tiết các thiết kế phần cứng, phần mềm, sơ đồ,

nguyên lý hoạt động.

Đề án 9: Thiết kế mạch ghép nối một LCD với cổng máy tính. Viết chương trình

hiển thị dòng chữ DTVT trên LCD.

Yêu cầu:

Phần cứng:

- Mạch ghép nối LCD với cổng LPT với kích thước của LCD tự chọn sao cho

phù hợp.

Phần mềm:

- Chương trình C điều khiển việc hiển thị chữ DTVT nhấp nháy cách nhau

một khoảng thời gian 2s.

Tài liệu báo cáo:

Page 95: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

95

- Bản báo cáo phân tích chi tiết các thiết kế phần cứng, phần mềm, sơ đồ,

nguyên lý hoạt động.

Đề án 10. Thiết kế mạch điện dùng 2 LED 7 thanh ghép nối với cổng máy in, rồi

lập trình hiển thị các số từ 00 đến 99. (4 sinh viên)

Yêu cầu:

Phần cứng:

- Mạch ghép nối qua cổng LPT.

Phần mềm:

- Chương trình C điền khiển hiển thị có số từ 00 đến 99, mỗi số cách nhau 2s.

Tài liệu báo cáo:

- Bản báo cáo phân tích chi tiết các thiết kế phần cứng, phần mềm, sơ đồ,

nguyên lý hoạt động.

Đề án 11. Viết chương trình truyền liên tiếp các kí tự từ A đến Z từ một máy tính giữa

hai máy tính qua cổng LPT. (4 sinh viên)

Yêu cầu:

Phần cứng:

- Cáp nối giữa hai máy tính,…

- Cổng LPT mỗi máy tính được ghép nối 16 dèn LED, 8 bóng xanh và 8 bóng đỏ.

Phần mềm:

- Chương trình C điều khiển việc truyền dữ liệu dữa hai máy tính, sao cho khi bên

nhận nhấn một phím ‘t’ trên bàn phím thì máy bên thu lại chuyển thành máy phát.

- Khi máy đang nhận dữ liệu thì các bóng sẽ xanh sáng nếu bít dữ liệu trên thanh ghi

data là 1. Khi máy đang gửi dữ liệu thì các bóng sẽ đỏ sáng nếu bít dữ liệu trên

thanh ghi data là 1.

Tài liệu báo cáo:

- Bản báo cáo phân tích chi tiết các thiết kế phần cứng, phần mềm, sơ đồ,

nguyên lý hoạt động.

Đề án 12. Viết chương trình giải mã một bàn phím có kích thước 4x4 ghép nối qua cổng

LPT. Các phím này biểu diễn các số từ 0 đến 9 và các phép toán +, - , x, /. (4 sinh viên)

Yêu cầu:

Phần cứng:

Page 96: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

96

- Mạch ghép nối qua cổng LPT.

Phần mềm:

- Chương trình C thực hiện quá trình đọc dữ liệu vào ra và điều khiển quá trình tính

toán và hiển thị kết quả lên màn hình.

- Khi nhấn một nút trên bàn phím thì màn hình sẽ hiển thị số, phép toán và kết quả

tương ứng.

Tài liệu báo cáo:

- Bản báo cáo phân tích chi tiết các thiết kế phần cứng, phần mềm, sơ đồ, nguyên lý

hoạt động.

Đề án 13: Viết một chương trình đếm sự xuất hiện của kí tự 'c' trong một file văn bản sử

dụng hoặc là hàm ch=fgetc() hoặc fscan(in, "%c") để đọc các ký tự riêng biệt. Mỗi khi

đếm được một ký tự C thì các đèn LED các đèn LED biểu diễn chữ C sẽ sáng.

Yêu cầu:

Phần cứng:

- Mạch ghép nối qua cổng LPT.

Phần mềm:

- Chương trình C cho phép:

· Nếu người dùng chọn 1: Thực hiện nhập vào một file văn bản với tên

‘DTK6.txt’.

· Nếu người dùng chọn 2: Thực hiện in file và đếm kí tự C và hiển thị trên

các đèn LED biểu diễn chữ C.

Tài liệu báo cáo:

- Bản báo cáo phân tích chi tiết các thiết kế phần cứng, phần mềm, sơ đồ, nguyên lý

hoạt động.

Đề án 14: Thiết kế mạch ghép nối một LCD với cổng máy tính. Viết chương trình

hiển thị dòng chữ DTVT trên LCD.

Yêu cầu:

Phần cứng:

- Mạch ghép nối LCD với cổng LPT với kích thước của LCD tự chọn sao cho

phù hợp.

Phần mềm:

- Chương trình C điều khiển việc hiển thị chữ DTVT chạy từ trên xuống dưới.

Page 97: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

97

Tài liệu báo cáo:

- Bản báo cáo phân tích chi tiết các thiết kế phần cứng, phần mềm, sơ đồ,

nguyên lý hoạt động.

Page 98: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

98

Chương 9 CẤU TRÚC

9.1. Kiểu cấu trúc Kiểu cấu trúc (Structure) là kiểu dữ liệu bao gồm nhiều thành phần có kiểu khác

nhau, mỗi thành phần được gọi là một trường (field).

Sự khác biệt giữa kiểu cấu trúc và kiểu mảng là: các phần tử của mảng là cùng

kiểu còn các phần tử của kiểu cấu trúc có thể có kiểu khác nhau.

Hình 9.1. Minh họa kiểu cấu trúc và kiểu mảng

9.1.1. Định nghĩa kiểu cấu trúc Cách 1:

struct <Tên cấu trúc>

{

<Kiểu> <Trường 1> ;

<Kiểu> <Trường 2> ;

……..

<Kiểu> <Trường n> ;

};

Thí dụ:

struct DienTro{

char name[15];

float value;

Phần tử

Trường

Còn mảng có dạng

Đây là mảng gồm 15 phần tử

0 1 3 4 5 6 7 8 9 10 11 12 13

Đây là cấu trúc có 7 trường

1 2 3 4 5 6 7

Page 99: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

99

}

Cách 2: Sử dụng từ khóa typedef để định nghĩa kiểu:

typedef struct

{

<Kiểu> <Trường 1> ;

<Kiểu> <Trường 2> ;

……..

<Kiểu> <Trường n> ;

} <Tên cấu trúc>;

Trong đó:

- <Tên cấu trúc>: là một tên được đặt theo quy tắc đặt tên; tên này mang ý

nghĩa sẽ là tên kiểu cấu trúc.

- <Kiểu> <Trường i> (i=1..n): mỗi trường trong cấu trúc có thể có kiểu dữ liệu

giống nhau hoặc khác nhau và tên của trường phải là một tên được đặt theo quy

tắc đặt tên.

Thí dụ:

typedef struct {

char name[15];

float value;

} DienTro;

9.2. Khai báo theo một kiểu cấu trúc đã định nghĩa Việc khai báo biến cấu trúc cũng tương tự như khai báo biến thuộc kiểu dữ liệu

chuẩn.

Cú pháp:

- Đối với cấu trúc được định nghĩa theo cách 1:

struct <Tên cấu trúc> <Biến 1> [, <Biến 2>…];

- Đối với các cấu trúc được định nghĩa theo cách 2:

<Tên cấu trúc> <Biến 1> [, <Biến 2>…];

Page 100: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

100

Khởi tạo cấu trúc khi khai báo: Việc khởi tạo cấu trúc có thể được thực hiệntrong lúc khai báo biến cấu trúc. Các trường của cấu trúc được khởi tạo được đặt giữa2 dấu { và }, chúng được phân cách nhau bởi dấu phẩy (,). Ví dụ: Khởi tạo biến cấu trúc DienTro: DienTro DT ={"R1",50.5};

9.3. Truy nhập đến các thành phần cấu trúc Cú pháp:

<Biến cấu trúc>.<Tên trường>

Khi sử dụng cách truy xuất theo kiểu này, các thao tác trên các trường của biến

cấu trúc giống như các thao tác trên các biến của kiểu dữ liệu của trường.

Thí dụ : Viết chương trình cho phép nhập một danh sách linh kiện từ bàn phím

và in danh sách đó lên màn hình:

#include<conio.h>

#include<stdio.h>

#include<string.h>

typedef struct

{

unsigned char Ngay;

unsigned char Thang;

unsigned int Nam;

} NgaySX;

typedef struct

{

char ma_lk[10];

char ten_lk[40];

NgaySX Ngaysx;

char DiaChi_NSX[40];

} LinhKien;

void InLK(LinhKien s)

{

Page 101: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

101

printf("Ma linh kien: |Ten linh kien| Ngay san xuat |Dia chi NSX\n");

printf("%s | %s | %d-%d-%d |%s\n",s.ma_lk,s.ten_lk,

s.Ngaysx.Ngay,s.Ngaysx.Thang,s.Ngaysx.Nam,s.DiaChi_NSX);

}

int main()

{

LinhKien LK, LKMoi;

clrscr();

printf("Nhap ma linh kien: ");

gets(LK.ma_lk);

printf("Nhap ten linh kien: ");gets(LK.ten_lk);

printf("Ngay san xuat: ");scanf("%d",&LK.Ngaysx.Ngay);

printf("Thang san xuat: ");scanf("%d",&LK.Ngaysx.Thang);

printf("Nam san xuat: ");scanf("%d",&LK.Ngaysx.Nam);

fflush(stdin);

printf("Dia chi NSX: ");gets(LK.DiaChi_NSX);

InLK(LK);

getch();

return 0;

}

Chú ý:

- Các biến cấu trúc có thể gán cho nhau.

Chương trình trên dòng s=LK là một ví dụ.

- Với các biến kiểu cấu trúc, không thể thực hiện được các thao tác sau đây:

+ Sử dụng các hàm xuất nhập trên biến cấu trúc.

+ Các phép toán quan hệ, các phép toán số học và logic.

9.4. Mảng cấu trúc Người lập trình có thể sử dụng các kiểu cấu trúc đã được định nghĩa để khai báo

các cấu trúc và mảng các cấu trúc.

Thí dụ chương trình sau đây cho phép nhập vào một mảng các linh kiện:

Page 102: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

102

#include<conio.h>

#include<stdio.h>

#include<string.h>

typedef struct

{

unsigned char Ngay;

unsigned char Thang;

unsigned int Nam;

} NgaySX;

typedef struct

{

char ma_lk[10];

char ten_lk[40];

NgaySX Ngaysx;

char DiaChi_NSX[40];

} LinhKien;

// In danh sach linh kien ra man hinh

void InLK(LinhKien LK[100],int n)

{

printf("Ma linh kien: |Ten linh kien| Ngay san xuat |Dia chi NSX\n");

for(int i=0;i<n;i++)

printf("%s | %s | %d-%d-%d |%s\n",LK[i].ma_lk,LK[i].ten_lk,

LK[i].Ngaysx.Ngay,LK[i].Ngaysx.Thang,LK[i].Ngaysx.Nam,LK[i].DiaChi_NSX);

}

// Nhap vao danh sach linh kien

void NhapLK(LinhKien LK[100], int n){

for (int i=0;i<n;i++){

printf("\nNhap ma linh kien: "); fflush(stdin);

gets(LK[i].ma_lk);

printf("\nNhap ten linh kien: ");gets(LK[i].ten_lk);

Page 103: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

103

printf("\nNgay san xuat: ");scanf("%d",&LK[i].Ngaysx.Ngay);

printf("\nThang san xuat: ");scanf("%d",&LK[i].Ngaysx.Thang);

printf("\nNam san xuat: ");scanf("%d",&LK[i].Ngaysx.Nam);

fflush(stdin);

printf("\nDia chi NSX: ");gets(LK[i].DiaChi_NSX);

}

}

int main()

{

LinhKien LK[100]; int n;

clrscr();

printf("Nhap vao so phan tu mang: "); scanf("%d",&n);

NhapLK(LK,n);

InLK(LK,n);

getch();

return 0;

}

9.5. Phép gán cấu trúc Người lập trình có thể gán hai cấu trúc có cùng kiểu cho nhau. Thí dụ như chương

trình sau:

#include<conio.h>

#include<stdio.h>

#include<string.h>

typedef struct

{

unsigned char Ngay;

unsigned char Thang;

unsigned int Nam;

} NgaySX;

typedef struct

Page 104: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

104

{

char ma_lk[10];

char ten_lk[40];

NgaySX Ngaysx;

char DiaChi_NSX[40];

} LinhKien;

void InLK(LinhKien s)

{

printf("Ma linh kien: |Ten linh kien| Ngay san xuat |Dia chi NSX\n");

printf("%s | %s | %d-%d-%d |%s\n",s.ma_lk,s.ten_lk,

s.Ngaysx.Ngay,s.Ngaysx.Thang,s.Ngaysx.Nam,s.DiaChi_NSX);

}

int main()

{

LinhKien LK, LKMoi; //LK va LKMoi co cung kieu

clrscr();

printf("Nhap ma linh kien: ");

gets(LK.ma_lk);

printf("Nhap ten linh kien: ");gets(LK.ten_lk);

printf("Ngay san xuat: ");scanf("%d",&LK.Ngaysx.Ngay);

printf("Thang san xuat: ");scanf("%d",&LK.Ngaysx.Thang);

printf("Nam san xuat: ");scanf("%d",&LK.Ngaysx.Nam);

fflush(stdin);

printf("Dia chi NSX: ");gets(LK.DiaChi_NSX);

InLK(LK);

LKMoi=LK; //Phep gan cau truc

printf("\n\nLinh kien moi duoc gan la:\n");

InLK(LKMoi); //In linh kien moi duoc gan

getch();

Page 105: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

105

return 0;

}

9.6. Con trỏ cấu trúc và địa chỉ cấu trúc

9.6.1 Khai báo Việc khai báo một biến con trỏ kiểu cấu trúc cũng tương tự như khi khai báo

một biến con trỏ khác, nghĩa là đặt thêm dấu * vào phía trước tên biến.

Cú pháp: struct <Tên cấu trúc> * <Tên biến con trỏ>;

Thí dụ: Ta có thể khai báo một con trỏ cấu trúc kiểu NgayThang như sau:

struct NgayThang *p;

/* NgayThang *p; // Nếu có định nghĩa kiểu */

9.6.2. Sử dụng các con trỏ kiểu cấu trúc Khi khai báo biến con trỏ cấu trúc, biến con trỏ chưa có địa chỉ cụ thể. Lúc này

nó chỉ mới được cấp phát 2 byte để lưu giữ địa chỉ và được ghi nhận là con trỏ chỉ đến

1 cấu trúc, nhưng chưa chỉ đến 1 đối tượng cụ thể. Muốn thao tác trên con trỏ cấu trúc

hợp lệ, cũng tương tự như các con trỏ khác người lập trình phải:

- Cấp phát một vùng nhớ cho nó (sử dụng hàm malloc() hay calloc)

- Hoặc, cho nó quản lý địa chỉ của một biến cấu trúc nào đó.

Thí dụ: Sau khi khởi tạo giá trị của cấu trúc:

struct NgayThang Ngay = {29,8,1986};

p = &Ngay;

lúc này biến con trỏ p đã chứa địa chỉ của Ngay.

9.6.3 Truy cập các thành phần của cấu trúc đang được quản lý bởi con trỏ Để truy cập đến từng trường của 1 cấu trúc thông qua con trỏ của nó, người lập

trình phải sử dụng toán tử dấu mũi tên (->: dấu - và dấu >).

Ngoài ra, ta vẫn có thể sử dụng đến phép toán * để truy cập vùng dữ liệu đang được

quản lý bởi con trỏ cấu trúc để lấy thông tin cần thiết.

Thí dụ sử dụng con trỏ cấu trúc.

#include<conio.h>

#include<stdio.h>

Page 106: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

106

typedef struct

{

unsigned char Ngay;

unsigned char Thang;

unsigned int Nam;

} NgayThang;

int main()

{

NgayThang Ng={29,8,1986};

NgayThang *p;

clrscr();

p=&Ng;

printf("Truy cap binh thuong %d-%d-%d\n",

Ng.Ngay,Ng.Thang,Ng.Nam);

printf("Truy cap qua con tro %d-%d-%d\n",

p->Ngay,p->Thang,p->Nam);

printf("Truy cap qua vung nho con tro %d-%d-%d\n",

(*p).Ngay,(*p).Thang,(*p).Nam);

getch();

return 0;

}

Kết quả:

9.7. Hàm trên các cấu trúca) Cấu trúc làm đối của hàm

Page 107: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

107

Đối của hàm có thể là biến cấu trúc, hoặc mảng cấu trúc hình thức hoặc con trỏ

cấu trúc.

- Khi đối số của hàm là một biến cấu trúc thì tham số thực của hàm tương ứng

của hàm là một giá trị cấu trúc.

- Khi đối số của hàm là con trỏ cấu trúc thì tham số thực tương ứng của hàm là

địa chỉ của các biến cấu trúc.

- Khi đối số của hàm là mảng cấu trúc hình thức hoặc con trỏ cấu trúc thì tham

số thực tương ứng của hàm là tên mảng cấu trúc.

b) Hàm có kiểu cấu trúc

Các hàm có thể trả về giá trị là cấu trúc hoặc con trỏ cấu trúc

c) Thí dụ

Thí dụ sau đây sẽ minh họa việc sử dụng cấu trúc trong hàm:

Thí dụ 9.1: Xét kiểu cấu trúc DienTro gồm 3 thành phần :

+ Ten (Tên điện trở ) kiểu mảng char

+ nsx (ngày sản xuất) kiểu struct date

+ giatri (giá trị )kiểu float

6 hàm sau đây được định nghĩa để thao tác trên kiểu DienTro

+ Hàm

DienTro *ptim(char *ten, DienTro ds[], int n);

có tác dụng tìm trong danh sách n điện trở lưu trong mảng ds điện trở có tên ten . Hàm

trả về con trỏ trỏ tới điện trở tìm được hoặc trả về NULL nếu không tìm thấy .

+ Hàm

DienTro tim(char *Ten, DienTro ds[], int n);

Hàm này cũng có tác dụng tìm kiếm như hàm ptim, nhưng nó trả về một cấu trúc chứ

không chứa thông tin điện trỏ tìm được . Các thông tin này sẽ bằng không nếu không

tìm thấy .

+ Hàm

void DoiCho(DienTro *p1, DienTro *p2);

dùng để đổi chỗ hai cấu trúc .

+ Hàm

Page 108: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

108

void sapxep(DienTro *p,int n);

có tác dụng sắp xếp n phần tử cấu trúc chứa trong p theo thứ tự tăng của ngày sản

xuất. Trong hàm sapxep có dùng tới hàm DoiCho .

+ Hàm

void Nhap(DienTro *p);

dùng để nhập dữ liệu cho một đối tượng kiểu DienTro. Một chú ý quan trọng là : nếu

trong hàm Nhap() ta dùng câu lệnh .

scanf(“%f%*c”,&h.b1);

để nhập trực tiếp vào thành phần h.b1 thì bị treo máy.

+ Hàm

void in(DienTro p);

dùng để in thông tin 1 cấu trúc ra màn hình.

Thứ tự làm việc của chương trình như sau : Đầu tiên là phần nhập số liệu , rồi đến sắp

xếp, sau đó sẽ in danh sách DienTro đã sắp xếp. Cuối cùng đến các mục tìm kiếm theo

hàm ptim và hàm tim. Dưới đây là chương trình nguồn:

#include"stdio.h"

#include<conio.h>

#include<string.h>

struct date

{

int ngay,thang,nam;

};

typedef struct

{

char ten[25];

struct date nsx;

float giatri;

} DienTro;

DienTro *ptim(char *ten,DienTro ds[],int n);

DienTro tim(char *ten,DienTro ds[],int n);

void DoiCho(DienTro *p1,DienTro *p2);

Page 109: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

109

void SapXep(DienTro *ps,int n);

void Nhap(DienTro *p);

void in(DienTro p);

DienTro tim(char *ten,DienTro ds[],int n)

{

int i;DienTro ps;

ps.nsx.ngay=ps.nsx.thang=ps.nsx.nam=0; ps.giatri=0.0;

ps.ten[0]=0;

for(i=1;i<=n;i++)

if (strcmp(ten,ds[i].ten)==0) return(ds[i]);

return(ps);

}

DienTro *ptim(char *ten,DienTro ds[],int n)

{

int i;

for(i=1;i<=n;i++)

if(strcmp(ten,ds[i].ten)==0) return(&ds[i]);

return(NULL);

}

void DoiCho(DienTro *p1,DienTro *p2)

{

DienTro t;

t=*p1;

*p1=*p2;

*p2=t;

}

void Nhap(DienTro *p)

{

DienTro q; float gt;

printf("\nNhap vao ten dien tro :");gets(q.ten);

printf("\nNhap ngay ngay san xuat :");

scanf("%d%d%d",&q.nsx.ngay,&q.nsx.thang,&q.nsx.nam);

Page 110: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

110

printf("\Nhap gia tri dien tro :");

scanf("%f%*c",&gt);q.giatri=gt;

*p=q;

}

void in(DienTro p)

{

printf("\n\n%s%12d-%d-%d%12.1f",p.ten,p.nsx.ngay,p.nsx.thang,p.nsx.nam,p.giatri);

}

void SapXep(DienTro *p,int n)

{

int i,j;

for(i=1;i<=n;++i)

for(j=i+1;j<=n;++j)

if(p[i].nsx.nam>p[j].nsx.nam)

DoiCho(&p[i],&p[j]);

}

void main()

{

DienTro *p,ds[100];int n,i,j;char ten[40];

/*vao so lieu */

clrscr();

printf("\n So dien tro n=");scanf("%d%*c",&n);

for(i=1;i<=n;i++)

Nhap(&ds[i]);

//sap xep theo chieu tang cua nam san xuat

SapXep(ds,n);

//in danh sach sau khi sap xep

printf("\n\nTen dien tro|Ngay san xuat|Gia tri");

for(i=1;i<=n;++i)

in(ds[i]);

//tim kiem theo ho ten

while(1)

Page 111: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

111

{

printf("\nNhap ten dien tro can tim\(Bam Enter de ket thuc)");

gets(ten);

if(ten[0]==0) break;

if((p=ptim(ten,ds,n))==NULL)

printf("\n Khong tim thay");

else in(*p);

}

//tim kiem theo ho ten dung ham tim

while(1)

{

printf("\nNhap ten dien tro can tim\(bam Enter de ket thuc)");

gets(ten);

if(ten[0]==0) break;

if(tim(ten,ds,n).ten[0]==0)

printf("\nKhong tim thay");

else in(tim(ten,ds,n));

} //ket thuc

}

Thí dụ 9.2 :Xét kiểu cấu trúc sophuc(số phức ) gồm hai thành phần :

+ Biến x kiểu float biểu thị phần thực của số phức .

+ Biến y biểu thị phần ảo của số phức.

Hai hàm sau đây được sử dụng để thao tác trên kiểu sophuc .

+ Hàm

sophuc cong(sophuc u,sophuc v);

dùng để cộng các giá trị số phức u,v. Giá trị của hàm là cấu trúc chứa tổng u+v .

+ Hàm

void insp(sophuc u);

Page 112: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

112

dùng để in cấu trúc u. Một điểm đáng lưu ý ở đây là: có thể dùng hàm này để in trực

tiếp tổng của hai giá trị phức cho bởi hàm cong(). Dưới đây là nội dung chi tiết chương

trình:

#include"stdio.h"

#include"conio.h"

typedef struct

{

float x,y;

} sophuc;

sophuc cong(sophuc u ,sophuc v);

void insp(sophuc u);

sophuc cong(sophuc u,sophuc v)

{

sophuc w;

w.x=u.x+v.x;

w.y=u.y+v.y;

return w;

}

void insp(sophuc u)

{

printf("(%0.2f,%0.2f)",u.x,u.y);

}

void main()

{

clrscr;

sophuc u,v;

u.x=6.5;u.y=-3.6;

v.x=2.8;v.y=12.1;

printf("\nSo phuc u=");

insp(u);

printf("\n So phuc v=");

Page 113: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

113

insp(v);

printf("\n Tong=");

insp(cong(u,v));

getch();

}

9.8. Cấp phát bộ nhớ động Giả sử người lập trình cần quản lý các viện, mỗi viện có nhiều phòng, mỗi phòng

lại có nhiều nhân viên. Khi thiết kế chương trình người lập trình chưa biết có bao nhiêu

viện, có bao nhiêu phòng và cũng chưa biết mỗi phòng có bao nhiêu nhân viên. Nếu

dùng mảng (cấp phát bộ nhớ tĩnh ) thì phải sử dụng số viện tối đa. Mỗi viện phải xem

như có cùng số phòng và mỗi phòng phải xem như có số nhân viên bằng nhau. Như

vậy sẽ có nhiều vùng nhớ được cấp phát mà không bao giờ sử dụng đến. Chương trình

dưới đây minh họa cách cấp phát bộ nhớ động. Số viện sẽ dùng bằng số viện cần quản

lý. Mỗi viện sẽ được cấp phát một vùng nhớ vừa đủ để chứa số phòng thực sự của nó,

và mỗi phòng cũng có một vùng nhớ ứng với số nhân viên hiện có của nó.

Nhân viên được mô tả bằng cấu trúc nhanvien có hai thành phần là hoten(họ tên),

và ns(năm sinh). Phòng được mô tả bởi cấu trúc phong có 3 thành phần là

tenphong(tên phòng) ,sonhanvien(số nhân viên trong phòng) và nv(con trỏ kiểu

nhanvien trỏ tới vùng nhớ chứa thông tin các nhân viên trong phòng). Viện được mô tả

bằng cấu trúc vien gồm 3 thành phần là tenvien(Tên viện), sophong (số phòng của

viện) và phong(con trỏ kiểu phong trỏ tới vùng nhớ chứa thông tin của các phòng trong

viện ).

Chương trình gồm hai phần:

+ Phần đầu gồm các thao tác nhập liệu và cấp phát bộ nhớ đặt xen kẽ nhau.

Nhập liệu để biết cần cấp phát bao nhiêu. Cấp phát để có vùng nhớ chứa thông tin

nhập vào sau đó.

+ Phần tiếp là in ra màn hình các thông tin về các viện, các phòng và nhân viên

vừa nhập vào.

#include"stdio.h"

#include"conio.h"

Page 114: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

114

#include"alloc.h"

typedef struct

{

char hoten[25];//ho ten

int ns; //nam sinh

} NhanVien;

typedef struct

{

char tenphong[30];//ten phong

int sonhanvien; //so nhan vien

NhanVien *nv;

} Phong;

typedef struct

{

char tenvien[30];//ten vien

int sophong; //so phong

Phong *ph;

} Vien;

Vien *V;

int sovien;

void main()

{

int i, j, k, ngay, thang, nam, sophong, sonhanvien, ns;

//vao so lieu va cap phat bo nho

// so vien

clrscr();

printf("\n Nhap so Vien: ");scanf("%d%*c",&sovien);

V=(Vien*) malloc((sovien+1)*sizeof(Vien)); //Tao ra mot mang Vien

//vao so lieu tung vien

for(i=1;i<=sovien;++i)

{

printf("\nTen vien thu %d:",i);

Page 115: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

115

gets(V[i].tenvien);

printf("\n So phong cua vien %s: ",V[i].tenvien);

scanf("%d%*c",&sophong);V[i].sophong=sophong;

V[i].ph=(Phong*)malloc((sophong+1)*sizeof(Phong));

for(j=1;j<=sophong;++j)

{

printf("\n Ten phong %d cua vien %s:",j,V[i].tenvien);

gets(V[i].ph[j].tenphong);

printf("\n So nhan vien cua phong %s vien %s :",V[i].ph[j].tenphong,V[i].tenvien);

scanf("%d%*c",&sonhanvien);

V[i].ph[j].sonhanvien=sonhanvien;

printf("\n\nVien %s Phong %s",V[i].tenvien,V[i].ph[j].tenphong);

for(k=1;k<=sonhanvien;++k)

{

printf("\nHo ten nhan vien %d: ",k);

gets(V[i].ph[j].nv[k].hoten);

printf("\nNam sinh nhan vien %d: ",k);

scanf("%d%*c",&ns);

V[i].ph[j].nv[k].ns=ns;

}

}

}

//dua ra man hinh

clrscr();

for(i=1;i<=sovien;++i)

{

printf("\n Vien %s co %d phong",V[i].tenvien,V[i].sophong);

for(j=1;j<=V[i].sophong;++j)

{

printf("\n Phong %s vien %s co %d nhan

vien",V[i].ph[j].tenphong,V[i].tenvien,V[i].ph[j].sonhanvien);

for(k=1;k<=V[i].ph[j].sonhanvien;++k)

Page 116: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

116

{

printf("\n Ho ten: %s Sinh nam: %d",V[i].ph[j].nv[k].hoten,V[i].ph

[j].nv[k].ns);

}

}

}

getch();

}

9.9. Cấu trúc tự trỏ và danh sách liên kết

9.9.1. Cấu trúc tự trỏ

9.9.1.1. Khái niệm cấu trúc tự trỏ

Cấu trúc có ít nhất một thành phần là con trỏ kiểu cấu trúc đã định nghĩa gọi là

cấu trúc tự trỏ.

9.9.1.2. Khai báo cấu trúc tự trỏCó 3 cách để khai báo cấu trúc tự trỏ. Giả sử, một linh kiện điện tử gồm các

thông tin sau: Mã linh kiện, tên linh kiện và giá, có xây dựng được cấu trúc tự trỏ như

sau:

Cách 1:

typedef struct pointer

{

char ten_linhkien[20];

int ma;

float gia;

struct pointer *next;

}linhkien;

Cách 2:

typedef struct pointer linhkien;

pointer

{

char ten_linhkien[20];

Page 117: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

117

int ma;

float gia;

pointer *next;

}

Cách 3:struct pointer

{ char ten_linhkien[20]; int ma; float gia; struct pointer *next;

}typedef pointer linhkien;

9.9.2. Danh sách liên kết đơn9.9.1.1. Định nghĩa

Danh sách liên kết được xây dựng từ các cấu trúc tự trỏ và có các tính chất sau:

- Biết địa chỉ cấu trúc đầu đang được lưu trữ trong một con trỏ nào đó.

- Trong mỗi cấu trúc (trừ cấu trúc cuối) chứa địa chỉ của cấu trúc tiếp theo trong

danh sách.

- Cấu trúc cuối chứa hằng Null

Page 118: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

118

Hìn

h

9.1.

Mi

nh

họa

dan

h

sác

h

liên

kết

the

o

chiề

u

thu

ận

Dan

h

sác

h có 3 tính chất như trên gọi là danh sách liên kết đơn theo chiều thuận. Danh sách này

cho phép truy xuất từ cấu trúc đầu tới cấu trúc cuối.

Tương tự, danh sách liên kết theo chiều ngược cũng có 3 tính chất trên nhưng

theo chiều ngược lại:

- Biết cấu trúc cuối.

- Trong mỗi cấu trúc, chứa địa chỉ của cấu trúc đứng trước.

- Cấu trúc đầu chứa hằng NULL.

NULLnext

5.000gia

3ma

Tu dien 2ten_linhkien

ct3

Addr(ct3next

5.000gia

2ma

Tu dien 1ten_linhkien

ct2

Addr(ct2next

20.000gia

1ma

Đien troten_linhkien

ct1Addr(ct1head

Page 119: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

119

Danh sách liên kết theo chiều thuận cho phép truy xuất từ cấu trúc cuối đến cấu trúc

đầu.

9.9.2.2. Các thao tác trên danh sách liên kết đơn

Một số thao tác chủ yếu trên danh sách liên kết:

- Lập danh sách mới

- In danh sách móc nối ra mành hình

- Tìm kiếm trên danh sách móc nối

- Xóa một người khỏi danh sách móc nối

- Bổ sung một người vào danh sách

- Chèn một người vào giữa danh sách

9.9.2.3. Lập danh sách mới

Để tạo danh sách liên kết mới cần thực hiện các khâu sau:

- Cấu phát bộ nhớ cho một cấu trúc

- Nhập các trường thông tin vào vùng nhớ vừa cấp

- Gán địa chỉ cấu trúc sau cho thành phần con trỏ của cấu trúc đứng trước.

9.9.2.4. Duyệt danh sách

Để duyệt qua tất cả các phần tử của một danh sách dùng một con trỏ p chứa địa

chỉ cấu trúc đang xét.

- Đầu tiên p=head

- Để chuyển đến người tiếp theo dùng phép toán gán

p=p->next

- Dấu hiệu để biết đang xét đến cấu trúc cuối cùng là p->next=NULL

9.9.2.5. Xóa một người khỏi danh sách sách liên kết

Để loại một người khỏi danh sách liên kết cần:

- Lưu trữ địa chỉ cấu trúc cần loại vào một con trỏ (để giải phóng)

- Gán trường next của cấu trúc dứng trước cho địa chỉ cấu trúc đứng sau cấu

trúc cần loại

- Giải phóng bộ nhớ của người cần loại

Page 120: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

120

9.9.2.6. Chèn một người vào danh sách liên kết

Để chèn một người vào danh sách cần làm các thao tác sau:

- Cấp phát bộ nhớ và nhập bổ sung

- Sửa thành phần con trỏ để đảm bảo mỗi cấu trúc chứa địa chỉ của cấu trúc tiếp

theo.

9.10. Thí dụThí dụ 9.1. Chương trình minh họa cách dùng danh sách móc nối .Chương trình gồm các phần + Nhập một số người và chứa vào bộ nhớ dưới dạng danh sách móc nối thuận(lập danh sách mới) + In danh sách móc nối ra màn hình . + Tìm kiếm trên danh sách móc nối theo quê . + Xóa một người khỏi danh sách . + Bổ xung vào cuối danh sách . + Chèn một người vào giữa danh sáchTrước khi xem chương trinh, hãy tìm hiểu các thủ thuật làm việc trên danh sách mócnối . + Để tạo danh sách mới cần thực hiện các bước sau : - Cấp phát bộ nhớ cho một cấu trúc . - Nhập một người vào vùng nhớ vừa cấp . - Gán địa chỉ người sau cho thành phần con trỏ của người trước . + Để duyệt qua tất cả các phần tử của một danh sách ta thường dùng con trỏ pchứa địa chỉ cấu trúc đang xét . - Đầu tiên cho p=pdau - Để chuyển đến người tiếp theo ta dùng phép gán : p=p -> tiep; - Dấu hiệu để biết đang xét người cuối cùng lá : p->tiep==Null + Để loại bỏ một người khỏi danh sách cần : - Lưu chữ địa chỉ cần loại vào con trỏ (dùng để giải phóng toàn bộ nhớ củangười cần loại ) - Sửa để người trước đó có địa chỉ của người đứng sau người mà ta định loại . - Giải phóng bộ nhớ của người cần loại .

Page 121: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

121

+ Để bổ xung (hoặc chèn ) cần - Cấp phát bộ nhớ và nhập bổ xung . - Sửa thành phần con trỏ trong các cấu trúc có liên quan để đảm bảo mỗi ngườichứa địa chỉ người tiếp theo . Chương trình dưới đây sẽ minh họa các kỹ thuật nói trên . Ngoài ra, chương trìnhcòn sử dụng phương pháp cấp phát bộ nhớ và cách truy nhập tới thành phần cấu trúcthông qua con trỏ.#include"stdio.h"#include"conio.h"#include"alloc.h"#include"string.h" typedef struct pp { char ht[25];//ho ten char qq[20];// que quan int tuoi; struct pp *tiep; }person; void main() { int t; char ht[25],qq[20]; person *pdau,*p,*p1; clrscr(); // nhap vao so nguoi va luu tru trong bo nho duoi //dang mot danh sach lien ket pdau=NULL; while(1) { printf("\n Ho ten :(bam Enter de ket thuc nhap lieu)"); gets(ht); if(ht[0]==0) break; if (pdau==NULL) { pdau=(person*)malloc(sizeof(person));

Page 122: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

122

p=pdau; } else { p->tiep=(person*)malloc(sizeof(person)); p=p->tiep;

} strcpy(p->ht,ht); printf("\nQue quan :"); gets(p->qq); printf("\n Tuoi :"); scanf("%d%*c",&t); p->tuoi=t; p->tiep=NULL; } //dua danh sach lien ket ra man hinh //biet con tro pdau tro toi danh sach p=pdau; while (p!=NULL) { printf("\nHo ten: %-25s Que: %-30s Tuoi: %d",(*p).ht,(*p).qq,(*p).tuoi); p=p->tiep; } //Tim kiem theo que quan while(1) { printf("\nQue(Ban Enter de ket thuc tim kiem):"); gets(qq); if(qq[0]==0) break; /*duyet tu dau danh sach va in ra man hinh nhung nguoi tim duoc */ p=pdau; while(p!=NULL)

Page 123: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

123

{ if(strcmp(p->qq,qq)==0) printf("\nHo ten: %-25s Que: %-30s Tuoi: %d ",(*p).ht,(*p).qq,(*p).tuoi); p=p->tiep; } } printf("\nLoai phan tu dau danh sach"); // Loai phan tu dau danh sach if(pdau!=NULL) { p=pdau; pdau=p->tiep; //pdau tro toi truong thu hai free(p); //giai phong vung nho cua nguoi dau } printf("\nBo sung mot nguoi vao cuoi danh sach"); //bo sung mot nguoi vao cuoi danh sach if(pdau==NULL) //danh sach rong { pdau=(person*)malloc(sizeof(person)); p=pdau; } else { //tim dia chi cuoi va dat vao do p=pdau; while(p->tiep!=NULL)p=p->tiep; //cap phat vung nho va nhap bo xung p->tiep=(person*)malloc(sizeof(person)); p=p->tiep; } //Nhap bo sung printf("\nHo Ten :"); gets(p->ht); printf("\nQue quan :");gets(p->qq);

Page 124: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

124

printf("\n Tuoi :");scanf("%d%*c",&t); p->tuoi=t; p->tiep=NULL; printf("\n Chen them vao truoc nguoi thu hai"); /* chen them vao truoc nguoi thu hai gia thiet co it nhat hai nguoi,truoc tien cap phat vung nho cho p de chua nguoi bo sung */ p=(person*)malloc(sizeof(person)); printf("\n Ho ten :"); gets(p->ht); printf("\nQue quan :");gets(p->qq); printf("\n Tuoi :");scanf("%d%*c",&t);p->tuoi=t; //ket noi p1=pdau->tiep; //p1 chua dia chi nguoi thu hai pdau->tiep=p; //sua de nguoi dau chua dia chi nguoi bo sung p->tiep=p1;/*nguoi moi bo sung chua dia chi nguoi thu hai trong danh sach cu */ /*dua danh sach moi ra man hinh biet con tro pdau tro toi dau danh sach */ p=pdau; while(p!=NULL) { printf("\n Ho ten %-25s Que %-30s Tuoi %d",(*p).ht,(*p).qq,(*p).tuoi); p=p->tiep; } // giai phong bo nho while(p!=NULL) { p=pdau; pdau=pdau->tiep; free(p); } printf("\n\nDa giai phong bo nho"); getch(); }

Page 125: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

125

Thí dụ: 9.2. Chương trình này cũng nhập số người và tạo thành một danh sách liên kết, nhưng phức tạp hơn chương trình 1 ở chỗ : Người vừa nhập được nhập chèn vào vị tríthích hợp để danh sách được sắp xếp theo chiều tăng của tuổi. Như vậy ta có quy trìnhđể vừa nhập liệu vừa sắp xếp một cách đông thời .Chương trình dùng hai hàm . Hàm void vao_sl(char *ht,person **p); dùng để gán ht(họ tên ) vào p->ht, gán Null cho p->tiep và nhập quê quán gán chop->qq, nhập tuổi gán cho p->tuoi. Hàm void bo_sung(person *pdau,person ng,person **pchen,int *vt);dùng để xác nhận xem cấu trúc ng cần chèn vào đâu. Nếu vt =0 thì ng cần chèn vàođầu danh sách . Còn nếu vt=1 thi ng cần chèn vào sau phần tử được pchen trỏ tới . Chương trình vừa minh họa các thủ thuật làm việc trên danh sách móc nối vừa minhhọa cho việc dùng các hàm có đối con trỏ.#include"stdio.h"#include"conio.h"#include"alloc.h"#include"string.h" typedef struct pp { char ht[25]; //ho ten char qq[20];//que quan int tuoi; struct pp *tiep; } person; void bo_sung(person *pdau,person ng,person **pchen,int *vt); /* y nghia cua ham bo_sung : Biet : + pdau tro toi danh sach (gia thiet pdau!=NULL) + ng la cau truc nguoi vua nhap Ham cho biet : + Neu vt=0 chen vao dau danh sach +Neu vt=1 chen vao sau cau truc do pchen tro toi */ void vao_sl(char *ht,person **p); /* Ham vao_sl dung de :

Page 126: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

126

+ Cap phat vung nho cho p + Gan ht vao p->ht + Vao cac so lieu khac nhu que qua va tuoi */ void bo_sung(person *pdau,person ng,person **pchen,int *vt) { person *p=pdau,*p1; if(ng.tuoi<p->tuoi) { *vt=0;return; } else while(1) { p1=p->tiep; if((p1==NULL)||(p1!=NULL && ng.tuoi<p1->tuoi)) { *pchen=p; *vt=1; return; } p=p1; } } void vao_sl(char *ht,person **p) { int t; person *pp; pp=(person*)malloc(sizeof(person)); strcpy(pp->ht,ht); printf("\nQue quan :");gets(pp->qq); printf("\nTuoi :");scanf("%d%*c",&t);pp->tuoi=t; pp->tiep=NULL; *p=pp; } main()

Page 127: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

127

{ int vt;char ht[25]; person *pdau,*p,*ptg,*pchen; clrscr(); /* vao mot do nguoi luu tru trong bo nho duoi dang mot danh sach lien ket */ pdau=NULL; while(1) { printf("\nHo ten(bam Enter de ket thucvao so lieu):"); gets(ht); if(ht[0]==0) break; if(pdau==NULL) vao_sl(ht,&pdau); else { vao_sl(ht,&ptg); bo_sung(pdau,*ptg,&pchen,&vt); if(vt==0)// chen vao dau danh sach { ptg->tiep=pdau; pdau=ptg;

} else { //chen vao sau pchen ptg->tiep=pchen->tiep; pchen->tiep=ptg; } } } /*dua danh sach lien ket ra man hinh biet con tro pdau tro toi dau danh sach */ p=pdau;

Page 128: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

128

while(p!=NULL) { printf("\nHo ten %-25s Que %-30s Tuoi %d",(*p).ht,(*p).qq,(*p).tuoi); p=p->tiep; } getch(); }

Page 129: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

129

Chương 10 TỆP TIN VÀ ĐỒ HỌA

10.1. Một số khái niệm về tệp tin

Đối với các kiểu dữ liệu đã biết như kiểu số, kiểu mảng, kiểu cấu trúc thì dữ liệu

được tổ chức trong bộ nhớ trong (RAM) của máy tính nên khi kết thúc việc thực hiện

chương trình thì dữ liệu cũng bị mất; khi cần người lập trình bắt buộc phải nhập lại từ

bàn phím. Điều đó vừa mất thời gian vừa không giải quyết được các bài toán với số

liệu lớn. Để giải quyết vấn đề, người ta đưa ra kiểu tập tin (file) cho phép lưu trữ dữ

liệu ở bộ nhớ ngoài (đĩa). Khi kết thúc chương trình thì dữ liệu vẫn còn, do đó có thể

sử dụng nhiều lần. Một đặc điểm khác của kiểu tập tin là kích thước lớn với số lượng

các phần tử không hạn chế (chỉ bị hạn chế bởi dung lượng của bộ nhớ ngoài).

Có 3 loại dữ liệu kiểu tập tin:

Tập tin văn bản (Text File): là loại tập tin dùng để ghi các ký tự lên đĩa, các ký tự này

được lưu trữ dưới dạng mã Ascii. Điểm đặc biệt là dữ liệu của tập tin được lưu trữ

thành các dòng, mỗi dòng được kết thúc bằng ký tự xuống dòng (new line), ký hiệu

‘\n’; ký tự này là sự kết hợp của 2 ký tự CR (Carriage Return - Về đầu dòng, mã Ascii

là 13) và LF (Line Feed - Xuống dòng, mã Ascii là 10). Mỗi tập tin được kết thúc bởi

ký tự EOF (End Of File) có mã Ascii là 26 (xác định bởi tổ hợp phím Ctrl + Z).

Tập tin văn bản chỉ có thể truy xuất theo kiểu tuần tự.

Tập tin định kiểu (Typed File): là loại tập tin bao gồm nhiều phần tử có cùng kiểu:

char, int, long, cấu trúc… và được lưu trữ trên đĩa dưới dạng một chuỗi các byte liên

tục.

Tập tin không định kiểu (Untyped File): là loại tập tin mà dữ liệu của chúng gồm các

cấu trúc dữ liệu mà người ta không quan tâm đến nội dung hoặc kiểu của nó, chỉ lưu ý

đến các yếu tố vật lý của tập tin như độ lớn và các yếu tố tác động lên tập tin.

Biến tập tin: là một biến thuộc kiểu dữ liệu tập tin dùng để đại diện cho một tập tin.

Dữ liệu chứa trong một tập tin được truy xuất qua các thao tác với thông số là biến tập

tin đại diện cho tập tin đó.

Page 130: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

130

Con trỏ tập tin: Khi một tập tin được mở ra để làm việc, tại mỗi thời điểm, sẽ có một

vị trí của tập tin mà tại đó việc đọc/ghi thông tin sẽ xảy ra. Người ta hình dung

có một con trỏ đang chỉ đến vị trí đó và đặt tên nó là con trỏ tập tin.

Sau khi đọc/ghi xong dữ liệu, con trỏ sẽ chuyển dịch thêm một phần tử về phía cuối tập

tin. Sau phần tử dữ liệu cuối cùng của tập tin là dấu kết thúc tập tin EOF (End f File).

10.2. Một số thao tác trên tập tinMuốn thao tác trên tập tin, ta phải lần lượt làm theo các bước:

B1- Khai báo biến tập tin.

B2- Mở tập tin bằng hàm fopen().

B3- Thực hiện các thao tác xử lý dữ liệu của tập tin bằng các hàm đọc/ghi dữ liệu.

B4- Đóng tập tin bằng hàm fclose().

Các thao tác với tập tin sử dụng các hàm được định nghĩa trong thư viện stdio.h.

10.2.1. Khai báo biến tập tin Cú pháp: FILE <Danh sách các biến con trỏ>

Các biến trong danh sách phải là các con trỏ và được phân cách bởi dấu phẩy(,).

Thí dụ: FILE *f1,*f2;

10.2.2. Mở tập tinCú pháp: FILE *fopen(char *Path, const char *Mode)

Trong đó:

- Path: chuỗi chỉ đường dẫn đến tập tin trên đĩa.

- Type: chuỗi xác định cách thức mà tập tin sẽ mở. Các giá trị có thể của Mode:

Page 131: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

131

- Hàm fopen trả về một con trỏ tập tin. Chương trình của ta không thể thay đổi giá trị

của con trỏ này. Nếu có một lỗi xuất hiện trong khi mở tập tin thì hàm này trả về con

trỏ NULL.

Thí dụ: Mở một tập tin tên TEST.txt để ghi.

FILE *f;

f = fopen(“TEST.txt”, “w”);

if (f!=NULL)

{

/* Các câu lệnh để thao tác với tập tin*/

/* Đóng tập tin*/

}

Trong thí dụ trên, ta có sử dụng câu lệnh kiểm tra điều kiện để xác định mở tập

tin có thành công hay không?.

Nếu mở tập tin để ghi, nếu tập tin đã tồn tại rồi thì tập tin sẽ bị xóa và một tập

tin mới được tạo ra. Nếu ta muốn ghi nối dữ liệu, ta phải sử dụng chế độ “a”. Khi mở

với chế độ đọc, tập tin phải tồn tại rồi, nếu không một lỗi sẽ xuất hiện.

Page 132: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

132

10.2.3. Đóng tập tin Hàm fclose() được dùng để đóng tập tin được mở bởi hàm fopen(). Hàm này sẽ

ghi dữ liệu còn lại trong vùng đệm vào tập tin và đóng lại tập tin.

Cú pháp: int fclose(FILE *f)

Trong đó f là con trỏ tập tin được mở bởi hàm fopen(). Giá trị trả về của hàm là

0 báo rằng việc đóng tập tin thành công. Hàm trả về EOF nếu có xuất hiện lỗi.

Ngoài ra, ta còn có thể sử dụng hàm fcloseall() để đóng tất cả các tập tin lại.

Cú pháp: int fcloseall()

Kết quả trả về của hàm là tổng số các tập tin được đóng lại. Nếu không thành công, kết

quả trả về là EOF.

10.2.4. Kiểm tra con trỏ ở cuối tập tinCú pháp: int feof(FILE *f)

Ý nghĩa: Kiểm tra xem đã chạm tới cuối tập tin hay chưa và trả về EOF nếu

cuối tập tin được chạm tới, ngược lại trả về 0.

10.2.5. Di chuyển con trỏ tập tin về đầu tập tinKhi ta đang thao tác một tập tin đang mở, con trỏ tập tin luôn di chuyển về phía

cuối tập tin. Muốn cho con trỏ quay về đầu tập tin như khi mở nó, ta sử dụng hàm

rewind().

Cú pháp: void rewind(FILE *f)

10.3. Tệp nhị phân

10.3.1. Ghi dữ liệu vào tệp nhị phân Cú pháp: size_t fwrite(const void *ptr, size_t size, size_t n, FILE *f)

Trong đó:

- ptr: con trỏ chỉ đến vùng nhớ chứa thông tin cần ghi lên tập tin.

- n: số phần tử sẽ ghi lên tập tin.

- size: kích thước của mỗi phần tử.

- f: con trỏ tập tin đã được mở.

Page 133: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

133

- Giá trị trả về của hàm này là số phần tử được ghi lên tập tin. Giá trị này bằng n trừ

khi xuất hiện lỗi.

10.3.2. Dọc dữ liệu từ tệp nhị phânCú pháp: size_t fread(const void *ptr, size_t size, size_t n, FILE *f)

Trong đó:

- ptr: con trỏ chỉ đến vùng nhớ sẽ nhận dữ liệu từ tập tin.

- n: số phần tử được đọc từ tập tin.

- size: kích thước của mỗi phần tử.

- f: con trỏ tập tin đã được mở.

- Giá trị trả về của hàm này là số phần tử đã đọc được từ tập tin. Giá trị này bằng n hay

nhỏ hơn n nếu đã chạm đến cuối tập tin hoặc có lỗi xuất hiện..

10.3.3 Di chuyển con trỏ tập tin - Hàm fseek() Việc ghi hay đọc dữ liệu từ tập tin sẽ làm cho con trỏ tập tin dịch chuyển một số

byte, đây chính là kích thước của kiểu dữ liệu của mỗi phần tử của tập tin. Khi đóng

tập tin rồi mở lại nó, con trỏ luôn ở vị trí ngay đầu tập tin. Nếu sử dụng kiểu mở tập tin

là “a” để ghi nối dữ liệu, con trỏ tập tin sẽ di chuyển đến vị trí cuối cùng của tập tin

này.

Người lập trình cũng có thể điều khiển việc di chuyển con trỏ tập tin đến vị trí chỉ định

bằng hàm fseek().

Cú pháp: int fseek(FILE *f, long offset, int whence)

Trong đó:

- f: con trỏ tập tin đang thao tác.

- offset: số byte cần dịch chuyển con trỏ tập tin kể từ vị trí trước đó. Phần tử đầu tiên

là vị trí 0.

- whence: vị trí bắt đầu để tính offset, ta có thể chọn điểm xuất phát là:

0 SEEK_SET Vị trí đầu của tệp tin

1 SEEK_CUR Vị trí hiện tại của tệp tin

2 SEEK_END Vị trí cuối của tệp tin

Page 134: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

134

- Kết quả trả về của hàm là 0 nếu việc di chuyển thành công. Nếu không thành công, 1

giá trị khác 0 (đó là 1 mã lỗi) được trả về.

Thí dụ: Viết chương trình ghi lên tập tin CacSo.Dat 3 giá trị số (thực, nguyên, nguyên

dài). Sau đó đọc các số từ tập tin vừa ghi và hiển thị lên màn hình.

#include<stdio.h>

#include<conio.h>

int main()

{

FILE *f;

clrscr();

f=fopen("D:\\CacSo.txt","wb");

if (f!=NULL)

{

double d=3.14;

int i=101;

long l=54321;

fwrite(&d,sizeof(double),1,f);

fwrite(&i,sizeof(int),1,f);

fwrite(&l,sizeof(long),1,f);

/* Doc tu tap tin*/

rewind(f);

fread(&d,sizeof(double),1,f);

fread(&i,sizeof(int),1,f);

fread(&l,sizeof(long),1,f);

printf("Cac ket qua la: %f %d %ld",d,i,l);

fclose(f);

}

getch();

return 0;

}

Page 135: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

135

10.4. Tệp văn bản

10.4.1. Ghi dữ liệu lên tập tin văn bản

10.4.1.1. Hàm putc()

Hàm này được dùng để ghi một ký tự lên một tập tin văn bản đang được mở để làm việc.

Cú pháp: int putc(int c, FILE *f)

Trong đó, tham số c chứa mã Ascii của một ký tự nào đó. Mã này được ghi lên tập tin liên kết

với con trỏ f. Hàm này trả về EOF nếu gặp lỗi.

10.4.1.2 Hàm fputs()

Hàm này dùng để ghi một chuỗi ký tự chứa trong vùng đệm lên tập tin văn bản.

Cú pháp: int puts(const char *buffer, FILE *f)

Trong đó, buffer là con trỏ có kiểu char chỉ đến vị trí đầu tiên của chuỗi ký tự được ghi vào.

Hàm này trả về giá trị 0 nếu buffer chứa chuỗi rỗng và trả về EOF nếu gặp lỗi.

10.4.1.3 Hàm fprintf()

Hàm này dùng để ghi dữ liệu có định dạng lên tập tin văn bản.

Cú pháp: fprintf(FILE *f, const char *format, varexpr)

Trong đó: format: chuỗi định dạng (giống với các định dạng của hàm printf()), varexpr: danh

sách các biểu thức, mỗi biểu thức cách nhau dấu phẩy (,).

Các định dạng và ý nghĩa:

%d Ghi số nguyên

%[.số chữ số thập phân] f Ghi số thực có <số chữ số thập phân> theo quy tắc làm tròn số.

%o Ghi số nguyên hệ bát phân

%x Ghi số nguyên hệ thập lục phân

%c Ghi một ký tự

%s Ghi chuỗi ký tự

%e hoặc %E hoặc %g

hoặc %G Ghi số thực dạng khoa học (nhân 10 mũ x)

Ví dụ: Viết chương trình ghi chuỗi ký tự lên tập tin văn bản D:\\Baihat.txt

#include<stdio.h>

#include<conio.h>

int main()

{

Page 136: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

136

FILE *f;

clrscr();

f=fopen("D:\\Baihat.txt","r+");

if (f!=NULL)

{

fputs("Cac ung dung cua C \n",f);

fputs("Trong dien tu vien thong.",f);

fclose(f);

}

getch();

return 0;

}

Nội dung tập tin Baihat.txt khi được mở bằng trình soạn thảo văn bản Notepad.

Cac ung dung cua C

Trong dien tu vien thong

10.4.2. Đọc dữ liệu từ tập tin văn bản

10.4..2.1 Hàm getc()

Hàm này dùng để đọc dữ liệu từ tập tin văn bản đang được mở để làm việc.

Cú pháp: int getc(FILE *f)

Hàm này trả về mã Ascii của một ký tự nào đó (kể cả EOF) trong tập tin liên

kết với con trỏ f.

10.4.2.2 Hàm fgets()

Cú pháp: char *fgets(char *buffer, int n, FILE *f)

Hàm này được dùng để đọc một chuỗi ký tự từ tập tin văn bản đang được mở ra và liên kết với

con trỏ f cho đến khi đọc đủ n ký tự hoặc gặp ký tự xuống dòng ‘\n’ (ký tự này cũng được đưa

vào chuỗi kết quả) hay gặp ký tự kết thúc EOF (ký tự này không được đưa vào chuỗi kết quả).

Trong đó:

- buffer (vùng đệm): con trỏ có kiểu char chỉ đến cùng nhớ đủ lớn chứa các ký

tự nhận được.

- n: giá trị nguyên chỉ độ dài lớn nhất của chuỗi ký tự nhận được.

- f: con trỏ liên kết với một tập tin nào đó.

Page 137: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

137

- Ký tự NULL (‘\0’) tự động được thêm vào cuối chuỗi kết quả lưu trong vùng

đêm.

- Hàm trả về địa chỉ đầu tiên của vùng đệm khi không gặp lỗi và chưa gặp ký tự

kết thúc EOF. Ngược lại, hàm trả về giá trị NULL.

10.4.2.3 Hàm fscanf()

Hàm này dùng để đọc dữ liệu từ tập tin văn bản vào danh sách các biến theo

định dạng.

Cú pháp: fscanf(FILE *f, const char *format, varlist)

Trong đó: format: chuỗi định dạng (giống hàm scanf()); varlist: danh sách các

biến mỗi biến cách nhau dấu phẩy (,).

Ví dụ: Viết chương trình chép tập tin D:\Baihat.txt ở trên sang tập tin

D:\Baica.txt.

#include<stdio.h>

#include<conio.h>

int main()

{

FILE *f1,*f2;

clrscr();

f1=fopen("D:\\Baihat.txt","rt");

f2=fopen("D:\\Baica.txt","wt");

if (f1!=NULL && f2!=NULL)

{

int ch=fgetc(f1);

while (! feof(f1))

{

fputc(ch,f2);

ch=fgetc(f1);

}

fcloseall();

}

getch();

return 0;

Page 138: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

138

}

Chương 11 ĐỒ HỌA

11.1. Khái niệm Máy tính làm việc trong hai chế độ hoặc văn bản hoặc đồ họa. Khi làm việc trong

chế độ văn bản, máy tính hiển thị ra màn hình theo từng ký tự sắp xếp trong một mảng

gồm 80 cột và 25 hàng, trong khi làm việc trong chế độ đồ họa thì màn hình được tao

ra từ các điểm ảnh riêng lẻ, bao gồm 640 điểm ảnh theo trục x và 480 điểm theo trục y;

khi đó mỗi ký tự sẽ bao gồm một nhóm các điểm ảnh.

Khi cơ bản được hiển thị đưới dạng giao diện đồ họa thì giao diện này được gọi là giao

diện giao tiếp người-máy bằng độ họa hoặc GUI. Một số GUI phổ biến như Microsoft

Windows và X-Windows. Đáng chú ý là các chế độ đồ họa được dùng trong các ứng

dụng cần có yêu cầu cao về độ phân giải của hình ảnh. Những ứng dụng này báo gồm:

các sơ đồ mạch điện, các ảnh đồ họa mô phòng mạch điện, các ảnh động,…

Thông thường, phần đồ họa có thể tìm thấy trong một thư viện của một ngôn ngữ lập

trình, được gọi là thư viện đồ họa. Thư viện này chứa các hàm với các chức năng khác

nhau, bao gồm từ việc vẽ ra các đường đơn giản tới các thao tác trên ảnh bit-map 3D.

Trong C, để sử dụng các thủ tục đồ họa cần phải khai báo tệp đồ họa graphics.h ở phần

đầu của chương trình, cú pháp:

#include <graphics.h>

Màn hình C trong chế đồ họa như sau:

Page 139: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

139

Hình 11-1: Mô phỏng màn hình C trong chế độ

đồ họa

Màn hình trong chế độ đồ họa được tạo ra từ các điểm ảnh, các điểm ảnh này

được truy nhập bằng cách sử dụng một hệ tọa độ x-y để định vị trí. Trục x là trục theo

phương nằm ngang của màn hình và trục y là trục theo hướng thằng đứng trên màn

hình. Điểm ở góc trên trái là điểm có tọa độ (0,0), điểm ở góc dưới phải là điểm có tọa

độ (MAXX, MAXY).

Máy tính hiển thị được đồ họa thông qua một card điều khiển, gọi là card video.

Độ phân giải và số lượng màu hiển thị phụ thuộc vào kiểu card điều khiển đồ họa và

thiết bị hiển thị được dùng. Một chương trình cài đặt có thể tự động phát hiện ra bộ

điều khiển đồ họa và nạp tệp tin chứa các thông tin về việc chương trình giao tiếp với

bộ điều khiển như thế nào. Tệp tin này được gọi là tệp điều khiển hoặc tệp đệm (driver

file). Bảng sau đây trình bày kiểu đồ họa tiêu biểu và các tệp tin điều khiển chúng của

C.

Tên tệp tin Kiểu màn hình đồ họa

ATT.BGI ATT & T6300

CGA.BGI IBMCGA, MCGA và các máy tương thích

EGAVGA.BGI IBM EGA, VGA và các máy tương thích

HERC.BGI Hercules monochrome

IBM8514.BGI IBM 8514 và các máy tương thích

PC3270.BGI IBM 3270 PC

(0,MAXY) (MAXX,MAXY

(MAXX,0)(0,0)

Page 140: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

140

Bảng 11-1: Các tệp tin điều khiển đồ họa của TURBO C

Một chương trình đồ họa thường gồm các phần sau:

1. Khởi động hệ thống đồ họa

2. Xác định màu nền (màu màn hình), mầu đường vẽ, mầu tô và kiểu tô.

3. Vẽ, tô màu các hình mà t among muốn

4. Các thao tác đồ họa khác như hiện các dòng chữ,….

5. Đóng hệ thống đồ họa để trở về chế độ văn bản.

11.3. Các thủ tục đồ họa cơ bản Để thay đổi giữa chế độ đồ họa và chế độ văn bản sử dụng 2 hàm chính: Hàm initgraph() thay đổi từ chế độ văn bản sang chế độ đồ họa và hàm closegraph() thay đổilại dạng text như ban đầu.11.3.1 Tắt chế độ đồ họa Hàm closegraph() làm tắt hệ thống đồ họa. Khuôn mẫu chuẩn được viết như sau: void closegraph(void);11.3.2 Khởi động đồ họa Hàm này sẽ khởi tạo hệ thống đồ họa và đặt phần cứng vào chế độ hoạt động đồhọa. Dạng mẫu chuẩn dùng thủ tục initgraph() được viết như sau: void initgraph(int *graphdriver, int *graphmode, char *pathtodriver);Các máy tính PC có thể được trang bị các bộ (card) điều khiển đồ họa khác nhau,chẳng hạn như:

- CGA (Colour Graphics Adapter)- EGA (Enhanced Graphics Adapter)- VGA (Video Graphics Adapter)- SVGA ( Super Video Graphics Adapter)- IBM 8514- PC 3270Có thể dùng hàm này để tự động phát hiện ra card điều khiển đồ họa bằng việc đặt

thông số graphdriver là DETECT. Cách này tạo điều kiện thuận lợi cho việc đặt các

thông số hiện thị đồ họa ở mức cực đại có thể. Xâu pathtodriver thông báo chochương trình vị trí có thể tìm thấy tệp tin điều khiển đồ họa. Tệp tin này được nạp khicho chương trình chạy. Nếu như xâu là một xâu rỗng “ ” thì chương trình sẽ giả thiết làtệp được tìm thấy trong thư mục làm việc hiện tại.

Page 141: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

141

Nói cách khác, nếu như tệp tin điều khiển được tìm trong thư mục \TC trên ổ đĩa C,thì đường dẫn sẽ là “C \\ TC” (dấu gạch chéo kép báo hiệu một thư mục ở mức dướihay thư mục con).

Chương trình 11-1: Chương trình 11-1 hiện thị một đường chéo được vẽ từ góc trên cùng tới góc dưới

của màn hình. Bộ điều khiển đồ họa được khởi tạo bằng việc sử dụng hàm initgraph().Sau khi khởi tạo thủ tục graphresult() được sử dụng để xác định xem liệu đã mắc lỗitrong việc khởi động bộ điều khiển. Sự trả lại của grok là để chỉ cho thấy là ở đó khôngcó vấn đề gì và màn hình hiển thị đồ họa bây giờ có thể được sử dụng. Nếu như nókhông trả lại thông báo grok thì hàm grapherrormsg() được sử dụng để hiển thị lỗi.Các lỗi điển hình được thông báo là “BGI File not found”, “Graphics not initialised”,“Invalid Font” v.v.

Các hàm getmaxx () và getmaxy () trả lại kích thước màn hình cực đại theo cáchướng tương ứng x và y. Với các màn hình VGA điển hình, số lượng cực đại của cácđiểm ảnh theo hướng x là 640 còn theo hướng y là 480.

Chương trình 11-1 #include <stdio.h>#include <graphics.h>int main(void){int gdriver=DETECT,gmode,errorcode; initgraph(&gdriver,&gmode,""); /* nếu các tệp điều khiển đồ họa không nằm trong thư

mục hiện hành thì chỉ định đến đường dẫn chứa các tệp *//* Thí dụ: C:\\TC “ or “C:\\BORLANDC\\BGI” */ errorcode=graphresult(); if (errorcode == grOk) { setcolor(WHITE); line(0,0,getmaxx(),getmaxy()); closegraph(); } else printf("Graphics error: %s\n",grapherrormsg(errorcode)); return(0);}

Page 142: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

142

11.3.3 Vẽ một điểm ảnhHàm putpixel ( ) vẽ một điểm ảnh tại một vị trí với màu đã được ấn định. Khuôn

dạng chuẩn được cho như sau: void putpixel (int x, int y, int colour);Chương trình 11-2 hiển thị các điểm ảnh với một màu ngẫu nhiên, ở một vị trí ngẫunhiên. Hàm random (X) trả lại một giá trị ngẫu nhiên từ 0 đến X-1. Giá trị ngẫu nhiênnày được đặt dựa trên cơ sở của đồng hồ hệ thống. Giá trí ban đầu của bộ định thời (đồng hồ ) được đặt bằng việc gọi hàm randomize ( ) khi khởi động chương trình.

Sự hiển thị đồ họa được thực hiện bởi việc khởi động hàm open _graphics. Hàm nàyhoặc trả lại giá trị GRAPHICS_ERROR khi có một lỗi nào đó hoặc sẽ trả lại giá trịNO_ERROR. Giá trị trả lại lúc đó sẽ được kiểm tra trong chương trình chính main( )và chương trình sẽ quyết định xem liệu có ngừng chương trình hay không. Nếu như không cólỗi nào thì chương trình sẽ tiếp tục hiển thị các điểm cho đến khi người dùng nhấn phím bất kỳtrên bàn phím. Hàm kbhit ( ) được sử dụng cho mục đích này. Hàm này trả lại một giá trịTRUE khi có một phím được nhấn, khi đó thì vòng lặp do {…} while (!kbhit( )) sẽ tiếp tục thực hiện chương trình cho đến khi nào người dùng nhấn vào một phím nàođó.

#include <stdio.h>#include <graphics.h>#include <time.h> /* chứa hàm randomize() */#include <stdlib.h> /* chứa hàm random() */#include <conio.h> /* chứa hàm kbhit() */enum errors {NO_ERROR=0,GRAPHICS_ERROR};int open_graphics(void);int main(void){int x,y;

if (open_graphics()==GRAPHICS_ERROR) return(GRAPHICS_ERROR); randomize(); /* Khởi tạo hàm ngẫu nhiên */ do { x=random(getmaxx()); y=random(getmaxy());

Page 143: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

143

putpixel(x,y,random(15)); } while (!kbhit()); /* do until a key is pressed */ closegraph(); return(NO_ERROR);}int open_graphics(void){int gdriver=DETECT,gmode,errorcode; initgraph(&gdriver,&gmode,""); errorcode=graphresult(); if (errorcode != grOk) { printf("Graphics error: %s\n",grapherrormsg(errorcode)); return(GRAPHICS_ERROR); } return(NO_ERROR);}

11.3.4 Vẽ một đường thẳng Hàm line ( ) vẽ một đường thẳng bằng màu đang dùng từ điểm có tọa độ (x1,

y1) tới điểm có tọa độ (x2, y2). Khuôn mẫu chuẩn dùng cho hàm line ( ) như sau:

void line (int x1, int y1, int x2, int y2); Chương trình 11-3 thực hiện công việc vẽ nhiều đường thẳng ngẫu nhiên vớicác màu ngẫu nhiên.#include <stdio.h>#include <graphics.h>#include <time.h> /* chứa hàm randomize() */#include <stdlib.h> /* chứa hàm for random() */#include <conio.h> /* chứa hàm for kbhit() */enum errors {NO_ERROR=0,GRAPHICS_ERROR};int open_graphics(void);int main(void){int maxX,maxY; randomize(); /* khởi tạo hàm ngẫu nhiên */

Page 144: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

144

if (open_graphics()==GRAPHICS_ERROR) return(GRAPHICS_ERROR); maxX=getmaxx(); maxY=getmaxy(); do { setcolor(random(15)); line(random(maxY),random(maxY),random(maxX),random(maxY)); } while (!kbhit()); closegraph(); return(NO_ERROR);}int open_graphics(void){int gdriver=DETECT,gmode,errorcode;

initgraph(&gdriver,&gmode,""); errorcode=graphresult(); if (errorcode != grOk) { printf("Graphics error: %s\n",grapherrormsg(errorcode)); return(GRAPHICS_ERROR); } return(NO_ERROR);}11.3.5 Vẽ một hình chữ nhật Hàm rectangle ( ) vẽ một hình chữ nhật bằng cách sử dụng màu đang dùng.

Khuôn mẫu chuẩn dùng cho hàm rectangle ( ) được viết như sau: void rectangle (int x1, int y1, int x2, int y2); Chương trình 11-4 hiển thị một điện trở riêng lẻ trên màn hình. Hàmdraw_resistor (x,y) vẽ điện trở này tại điểm bắt đầu có tọa độ (x,y). Một vấn đề xuấthiện khi tiến hành hiển thị đồ họa là việc hiển thị đồ họa có thể làm thay đổi số lượngcác điểm ảnh có thể hiển thị. Nếu sử dụng tọa độ tuyệt đối thì khi màn hình có độ phângiải cao đối tượng sẽ xuất hiện tương đối nhỏ, còn khi màn hình có độ phân giải thấpđối tượng lại trở lên lớn hơn. Vì thế trong chương trình này, điện trở được vẽ theo tỉ lệ

Page 145: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

145

đối với các tọa độ cực đại x- và y-; biện pháp này làm cho các tọa độ của nó được tínhtương đối so với kích thước của màn hình.#include <stdio.h>#include <graphics.h>enum errors {NO_ERROR=0,GRAPHICS_ERROR};int open_graphics(void);void draw_resistor(int x,int y);int main(void){ if (open_graphics()==GRAPHICS_ERROR) return(GRAPHICS_ERROR); draw_resistor(100,200); getchar(); closegraph(); return(NO_ERROR);}int open_graphics(void){int gdriver=DETECT,gmode,errorcode;

initgraph(&gdriver,&gmode,""); errorcode=graphresult(); if (errorcode != grOk) { printf("Graphics error: %s\n",grapherrormsg(errorcode)); return(GRAPHICS_ERROR); } return(NO_ERROR);}void draw_resistor(int x,int y){int maxx,maxy;

struct{ int length, width, connectline;

Page 146: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

146

} res; maxx=getmaxx(); maxy=getmaxy(); res.length=maxy/20; res.width=maxx/40; res.connectline=maxy/20; line(x,y,x,y+res.length); rectangle(x-res.width/2,y+res.connectline, x+res.width/2, y+res.connectline+res.length); line(x,y+res.connectline+res.length, x,y+res.length+2*res.connectline);}6 Hiển thị văn bản

Hàm ourtextxy ( ) gửi một xâu tới thiết bị lối ra. Các giá trị số không thể đượchiển thị trực tiếp trên màn hình mà cần được chuyển đổi thành một xâu trước khichúng được hiển thị. Khuôn mẫu chuẩn dùng cho hàm ourtextxy ( ) như sau:

void outtextxy (int x, int y, char *textstring);Chương trình 11-5 sử dụng hàm outtextxy ( ) để hiển thị một xâu giá trị điện

trở bên trong hàm draw _resistor ( ).Chương trình 11-5:#include <stdio.h>#include <graphics.h>enum errors {NO_ERROR=0,GRAPHICS_ERROR};int open_graphics(void);void draw_resistor(int x,int y, char str[]);int main(void){ if (open_graphics()==GRAPHICS_ERROR) return(GRAPHICS_ERROR); draw_resistor(100,200,"100 K"); draw_resistor(200,200,"200 K");

getchar(); closegraph(); return(NO_ERROR);

Page 147: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

147

}int open_graphics(void){int gdriver=DETECT,gmode,errorcode;

initgraph(&gdriver,&gmode,""); errorcode=graphresult(); if (errorcode != grOk) { printf("Graphics error: %s\n",grapherrormsg(errorcode)); return(GRAPHICS_ERROR); } return(NO_ERROR);}

7 Vẽ một vòng trònHàm circle( ) thực hiện công việc vẽ một vòng tròn với một bán kính cho trước

và có tâm tại điểm (x,y). Khuôn mẫu chuẩn cho hàm circle ( ) như sau:void circle (int x, int y, int radius);

Chương trình 11-6 dùng hàm circle ( ) để hiển thị một điện áp nguồn.#include <stdio.h>#include <graphics.h>enum errors {NO_ERROR=0,GRAPHICS_ERROR};int open_graphics(void);void draw_resistor(int x,int y, char str[]);void draw_voltage_source(int x,int y, char str[]);int main(void){ if (open_graphics()==GRAPHICS_ERROR) return(GRAPHICS_ERROR); draw_resistor(200,200,"100 K"); draw_resistor(300,200,"200 K"); draw_voltage_source(100,200,"5 V"); getchar(); closegraph(); return(NO_ERROR);

Page 148: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

148

}int open_graphics(void){int gdriver=DETECT,gmode,errorcode; initgraph(&gdriver,&gmode,""); errorcode=graphresult(); if (errorcode != grOk) { printf("Graphics error: %s\n",grapherrormsg(errorcode)); return(GRAPHICS_ERROR); } return(NO_ERROR);}void draw_resistor(int x,int y, char str[]){int maxx,maxy;struct{ int length, width, connectline;} res;

maxx=getmaxx(); maxy=getmaxy();

res.length=maxy/20; res.width=maxx/40; res.connectline=maxy/20;

line(x,y,x,y+res.length); rectangle(x-res.width/2,y+res.connectline, x+res.width/2, y+res.connectline+res.length); line(x,y+res.connectline+res.length, x,y+res.length+2*res.connectline);

outtextxy(x+res.width,y+res.length/2+res.connectline,str);

Page 149: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

149

}void draw_voltage_source(int x,int y, char str[]){int maxy;struct{ int radius, connectline;} volt; maxy=getmaxy(); volt.radius=maxy/40; volt.connectline=maxy/20; line(x,y,x,y+volt.connectline); circle(x,y+volt.connectline+volt.radius,volt.radius); line(x,y+volt.connectline+2*volt.radius, x,y+2*volt.radius+2*volt.connectline); outtextxy(x+volt.connectline+volt.radius, y+volt.radius+volt.connectline,str);}

8 Vẽ ảnh bitmapChương trình 11-7 sẽ hiển thị một khuôn mặt có thể di chuyển được xung quanh mànảnh bằng cách sử dụng các phím mũi tên trên bàn phím. Hình 11-5 chỉ ra một hìnhhiển thị làm mẫu.Hàm getch( ) được sử dụng để phát hiện khi có một phím trên bàn phím bị nhấn. Nếuhàm này trả lại giá trị là 0 thì phím được nhấn là một ký tự mở rộng, chẳng hạn mộtphím chức năng ( F1…F12), các phím sang trang Page up, Page down, các phím mũitên,…v.v. Các ký tự mở rộng có thể được xác định bằng cách gọi hàm getch( ) trở lại.Các giá trị trả lại làm mẫu được liên kết trong bảng 11-4

Các giá trị trả lại

Mũi tên hướng lênMũi tên hướng xuốngMũi tên hướng sang trái Mũi tên hướng sang phải Phím thoát (ESC)

Phím

7280757727

Page 150: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

150

Dòng mã dưới đây xác định xem liệu một phím chức năng vừa được nhấn: ch= getch ( ); if ( ch = = 0) ch = getch( );Thí dụ, nếu nhấn phím Esc thì biến ch sẽ lưu giá trị 27.Chương trình 11-7:/*****************************************************//* FACE.C *//* Tiêu đề: Chuong trinh di chuyen nen *//* Chức năng: Chuong trinh hien thi mot vung nen *//* Co the su dung phim mui ten de di chuyen *//****************************************************/

#include <conio.h>#include <graphics.h>#include <alloc.h>#include <stdio.h>#include <process.h> /* required for exit() */

#define UPARROW 72#define DOWNARROW 80#define LEFTARROW 75#define RIGHTARROW 77#define ESC 27#define INCREMENT 4

enum errors {NO_ERROR=0,GRAPHICS_ERROR,GRAPHICS_MEM_ERROR};

int open_graphics(void);void *get_shape(void);

int main(void){void *shape;int x,y,ch;

Page 151: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

151

if (open_graphics()==GRAPHICS_ERROR) return(GRAPHICS_ERROR);

shape=get_shape();

if (shape==NULL) { puts("Cannot allocate enough graphics memory"); return(GRAPHICS_MEM_ERROR); } x=getmaxx()/2; y=getmaxy()/2;

do { putimage(x, y, shape, XOR_PUT); /* Vẽ ảnh */ ch=getch(); if (ch==0) ch=getch(); /* Đọc vào 1 phím */ putimage(x, y, shape, XOR_PUT); /* xóa ảnh */ if (ch==UPARROW) y-=INCREMENT; else if (ch==DOWNARROW) y+=INCREMENT; else if (ch==LEFTARROW) x-=INCREMENT; else if (ch==RIGHTARROW) x+=INCREMENT; if (x>0.9*getmaxx())x=0.9*getmaxx(); if (x<0) x=0; if (y>0.9*getmaxy())y=0.9*getmaxy(); if (y<0) y=0; } while (ch!=ESC); closegraph(); return(NO_ERROR);}

int open_graphics(void){int gdriver=DETECT,gmode,errorcode; initgraph(&gdriver,&gmode,"");

Page 152: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

152

errorcode=graphresult(); if (errorcode != grOk) { printf("Graphics error: %s\n",grapherrormsg(errorcode)); return(GRAPHICS_ERROR); } return(NO_ERROR);}

void *get_shape(void){int startx,starty ;void *al;int ulx, uly, lrx, lry, size, buffsize; /* Draw shape */ setfillstyle( SOLID_FILL,WHITE ); startx=getmaxx()/2; starty=getmaxy()/2; size=getmaxx()/20; /* draw face outline */ circle(startx,starty,size); floodfill(startx,starty,WHITE); /* Vẽ mắt */ setcolor(RED); circle(startx+size/3,starty,size/3); floodfill(startx+size/3,starty,WHITE); circle(startx-size/3,starty,size/3); floodfill(startx+size/3,starty,WHITE); /* get size of face */ ulx = startx-size; uly = starty-size; lrx = startx+size; lry = starty+size; buffsize = imagesize(ulx, uly, lrx, lry); al = malloc( buffsize ); getimage(ulx, uly, lrx, lry, al);

Page 153: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

153

putimage(ulx, uly, al, XOR_PUT); return(al);}Hàm getimage (x1,y1,x2,y2) thu thập hình ảnh từ các tọa độ (x1, y1) tới (x2, y2) vào

trong bộ nhớ và hàm putimage (x, y, BITMASK) được sử dụng để hiển thị hình ảnhvà xóa ảnh đó khỏi màn hình. Cách nhanh nhất để xóa bỏ một đối tượng ảnh là thựchiện phép loại trừ XOR tất cả các bit của đối tượng ảnh với chính nó. Hàm putimage(x, y, BITMASK) cho phép một mặt nạ bit được áp dụng cho hình ảnh. Hàm OR- loạitrừ được định nghĩa với XOR_PUT. Chẳng hạn, nếu các bit trên một đoạn của mànhình là 11001010, thì khi thực hiện OR-loại trừ với chính nó kết quả sẽ là 00000000.

Page 154: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

154

Phụ lụcBài 1: Viết chương trình chuyển đổi số thập phân 256<n<65535 sang số nhị phân dùng toán tử dịch bít

(bitwise). Trong đó n được nhập vào từ bàn phím.

#include<stdio.h>

#include<conio.h>

void main(){

unsigned int n;

clrscr();

do{

printf("Nhap vao n: ");scanf("%u",&n);

}while(n<0 || n>=65535);

printf("\nDang nhi phan cua %d la: ",n);

if (n&0x8000) printf("1");else printf("0");

if (n&0x4000) printf("1");else printf("0");

if (n&0x2000) printf("1");else printf("0");

if (n&0x1000) printf("1");else printf("0");

if (n&0x0800) printf("1");else printf("0");

if (n&0x0400) printf("1");else printf("0");

if (n&0x0200) printf("1");else printf("0");

if (n&0x0100) printf("1");else printf("0");

if (n&0x0080) printf("1");else printf("0");

if (n&0x0040) printf("1");else printf("0");

if (n&0x0020) printf("1");else printf("0");

if (n&0x0010) printf("1");else printf("0");

if (n&0x0008) printf("1");else printf("0");

if (n&0x0004) printf("1");else printf("0");

if (n&0x0002) printf("1");else printf("0");

if (n&0x0001) printf("1");else printf("0");

getch();

}

Bài 2: Viết chương trình nhập giá trị màu của điện trở và chuyển thành giá trị số của điện trở.

#include<stdio.h>

#include<conio.h>

float SoMau(int colornum){

switch (colornum)

Page 155: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

155

{

case 0: return 0;

case 1: return 1;

case 2: return 2;

case 3: return 3;

case 4: return 4;

case 5: return 5;

case 6: return 6;

case 7: return 7;

case 8: return 8;

case 9: return 9;

default: return 0;

}

}

float HeSoNhan(int colornum){

switch (colornum)

{

case 1: return 10;

case 2: return 100;

case 3: return 1000;

case 4: return 10000;

case 5: return 100000;

case 6: return 1000000;

case 7: return 10000000;

case 8: return 100000000;

case 9: return 1000000000;

default:return 0;

}

}

float SaiSo(float giatri,int colornum){

float ss;

switch (colornum)

{

case 1: ss=1;break;

case 2: ss=2;break;

case 3: ss=3;break;

Page 156: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

156

case 4: ss=4;break;

case 5: ss=5;break;

case 6: ss=6;break;

case 7: ss=7;break;

case 8: ss=8;break;

case 9: ss=9;break;

case -1: ss=10;break;

default: ss=0;

}

ss=(float)(giatri*ss/100);

return ss;

}

int main(void)

{

Enum {SILVER=-1, BLACK, BROWN,RED, ORAGNE, YELLOW, GREEN, BLUE, VIOLET,

GREY, WHILE} colour;

float dientro;

clrscr();

do{

printf("Nhap vao vong mau thu nhat (0->9): ");

scanf("%d", &colour);

}while(colour<BLACK || colour >WHILE) ;

dientro=SoMau(colour);

do{

printf("Nhap vao vong mau thu hai (0->9): ");scanf("%d", &colour);

}while(colour<BLACK || colour >WHILE) ;

dientro=dientro*10+SoMau(colour);

do{

printf("Nhap vao mau he so nhan:"); scanf("%d", &colour);

}while(colour<BLACK || colour >WHILE) ;

dientro=dientro*HeSoNhan(colour);

//Tinh sai so

do{

printf("Nhap vao mau sai so:"); scanf("%d", &colour);

}while(colour<SILVER || colour >WHILE) ;

Page 157: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

157

printf("Gia tri cua dien tro trong khoang tu %5.2f OM den %5.2f

OM",dientro-SaiSo(dientro,colour),dientro+SaiSo(dientro,colour));

getch();

return(0);

}

Bài 4: Viết chương trình tính điện trở tương đương của hai điện trở mắc song song.

#include<stdio.h>

#include<conio.h>

void get_values(float *r1,float *r2);

void get_parallel_res(float r1,float r2,float *r_e);

void print_results(float r1,float r2,float r_e);

int main(void)

{

float R1,R2,R_equ;

clrscr();

get_values(&R1,&R2);

get_parallel_res(R1,R2,&R_equ);

print_results(R1,R2,R_equ);

getch();

return(1);

}

void get_values(float *r1,float *r2)

{

do

{

printf("Nhap R1 >>");

scanf("%f",r1);

if (*r1<0) puts("Gia tri khong hop le: nhap lai");

} while (*r1<0);

do

{

printf("Nhap R2 >>");

scanf("%f",r2);

if (*r2<0) puts("Gia tri khong hop le: Nhap lai");

} while (*r2<0);

Page 158: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

158

}

void get_parallel_res(float r1,float r2,float *r_e)

{

*r_e=1/(1/r1+1/r2);

}

void print_results(float r1,float r2,float r_e)

{

printf("Cac gia tri dien tro %8.3f va %8.3f ohm\n",r1,r2);

printf("Dien tro tuong duong cua mach la %8.3f ohm\n",r_e);

}

Bài 5: Viết chương trình nhập vào một mảng các số nguyên từ bàn phím, sắp xếp mảng theo thứ

tự tăng dần, rồi in kết quả ra màn hình.

Cách 1: Truy nhập mảng thông qua chỉ số của mảng

#include<stdio.h>

#include<conio.h>

void nhapmang(int a[50],int n) ;

void inmang(int a[50], int n) ;

void sapxep( int a[50], int n);

void swap(int*,int*) ;

void main()

{

int a[50],n;

printf("nhap n="); scanf("%d",&n);

// Nhap vao danh sach cac phan tu cua mang

nhapmang(a,n);

//Sap xep mang

sapxep(a,n);

//in mang sau khi da sap xep

inmang(a,n);

getch();

}

void nhapmang(int a[50],int n)

{

for(int i=0; i<n;i++)

{

Page 159: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

159

printf("a[%d]=",i);

scanf("%d",&a[i]);

}

}

void swap(int*a,int*b)

{

int t;

t=*a;

*a=*b;

*b=t;

}

void sapxep(int a[50],int n)

{

for(int i=0;i<=n-1;i++)

for(int j=i+1;j<n;j++)

{ if (a[i]>a[j])

swap(&a[i],&a[j]);

}

}

void inmang(int a[50],int n)

{

for(int i=0;i<n;i++)

printf("%5d",a[i]);

}

Cách 2: Truy nhập mảng thông qua con trỏ:

#include<stdio.h>

#include<conio.h>

void nhapmang(int *a,int n) ;

void inmang(int a[50], int n) ;

void sapxep( int a[50], int n);

void swap(int*,int*) ;

void main()

{

int a[50],n;

clrscr();

Page 160: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

160

printf("nhap n="); scanf("%d",&n);

// Nhap vao danh sach cac phan tu cua mang

nhapmang(a,n);

//Sap xep mang

sapxep(a,n);

//in mang sau khi da sap xep

inmang(a,n);

getch();

}

void nhapmang(int *a,int n)

{

for(int i=0; i<n;i++)

{

printf("a[%d]=",i);

scanf("%d",a+i);

}

}

void swap(int*a,int*b)

{

int t;

t=*a;

*a=*b;

*b=t;

}

void sapxep(int *a,int n)

{

for(int i=0;i<=n-1;i++)

for(int j=i+1;j<n;j++)

{ if (*(a+i)>*(a+j))

swap(a+i,a+j);

}

}

void inmang(int *a,int n)

{

for(int i=0;i<n;i++)

Page 161: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

161

printf("%5d",*(a+i));

}

Bài 6: Viết chương trình nhập vào một mảng các số thực rồi lập ra một mảng gồm các phần tử lớn

nhất trên mỗi cột.

#include<stdio.h>

#include<conio.h>

void Nhap(float a[50][50], int n);

void MaxCot(float a[50][50],int n);

void Hien(float a[50][50],int n);

//Khai bao mang c[] la bien toan cuc

float c[50];

void main(){

float a[50][50];int n;

clrscr();

printf("Nhap vao so phan tu cua mang: ");

scanf("%d",&n);

Nhap(a,n);

Hien(a,n);

MaxCot(a,n);

getch();

}

void Nhap(float a[50][50],int n){

float t;

for (int i=0;i<n;i++)

for (int j=0;j<n;j++) {

printf("a[%d][%d]=",i,j);scanf("%f", &a[i][j]);

}

}

void Hien(float a[50][50],int n){

for (int i=0;i<n;i++)

{

printf("\n");

for (int j=0;j<n;j++)

printf("%5.2f",a[i][j]);

Page 162: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

162

}

}

void MaxCot(float a[50][50],int n){

for(int i=0;i<n;i++)

{

c[i]=a[0][i];

for(int j=0;j<n;j++)

if (c[i]<a[j][i]) c[i]=a[j][i];

}

//Hien mang c[];

printf("\n\nMang gom cac phan tu lon nhat tren moi cot:\n");

for(i=0;i<n;i++)

printf("%5.2f",c[i]);

}

Bài 7: Viết chương trình nhập vào một danh sách các linh kiện, mỗi linh kiện gồm các thông tin:

mã linh kiện, tên linh kiện, ngày sản xuất, địa chỉ sản xuất. In danh sách linh kiện vừa nhập ra màn

hình.

#include<conio.h>

#include<stdio.h>

#include<string.h>

typedef struct

{

unsigned char Ngay;

unsigned char Thang;

unsigned int Nam;

} NgaySX;

typedef struct

{

char ma_lk[10];

char ten_lk[40];

NgaySX Ngaysx;

char DiaChi_NSX[40];

} LinhKien;

void InLK(LinhKien LK[100],int n)

Page 163: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

163

{

printf("Ma linh kien: |Ten linh kien| Ngay san xuat |Dia chi NSX\n");

for(int i=0;i<n;i++)

printf("%s | %s | %d-%d-%d |%s\n",LK[i].ma_lk,LK[i].ten_lk,

LK[i].Ngaysx.Ngay,LK[i].Ngaysx.Thang,LK[i].Ngaysx.Nam,LK[i].DiaChi_NSX);

}

void NhapLK(LinhKien LK[100], int n){

for (int i=0;i<n;i++){

printf("\nNhap ma linh kien: "); fflush(stdin);

gets(LK[i].ma_lk);

printf("\nNhap ten linh kien: ");gets(LK[i].ten_lk);

printf("\nNgay san xuat: ");scanf("%d",&LK[i].Ngaysx.Ngay);

printf("\nThang san xuat: ");scanf("%d",&LK[i].Ngaysx.Thang);

printf("\nNam san xuat: ");scanf("%d",&LK[i].Ngaysx.Nam);

fflush(stdin);

printf("\nDia chi NSX: ");gets(LK[i].DiaChi_NSX);

}

}

int main()

{

LinhKien LK[100]; int n;

clrscr();

printf("Nhap vao so phan tu mang: "); scanf("%d",&n);

//Nhap danh sach linh kien

NhapLK(LK,n);

//in danh sach linh kien ra man hinh

InLK(LK,n);

getch();

return 0;

}

Bài 8: Viết chương trình nhập vào danh sách các điện trở, mỗi điện trở gồm các thong tin: Tên

điện trở, ngày sản xuất, giá trị. Thực hiện tìm kiếm các điện trở theo tên với 2 cách khác nhau truy

nhập thông thường và truy nhập theo con trỏ.

#include"stdio.h"

#include"conio.h"

Page 164: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

164

#include"string.h"

struct date

{

int ngay,thang,nam;

};

typedef struct

{

char ten[25];

struct date nsx;

float giatri;

} DienTro;

DienTro *ptim(char *ten,DienTro ds[],int n);

DienTro tim(char *ten,DienTro ds[],int n);

void DoiCho(DienTro *p1,DienTro *p2);

void SapXep(DienTro *ps,int n);

void Nhap(DienTro *p);

void in(DienTro p);

DienTro tim(char *ten,DienTro ds[],int n)

{

int i;DienTro ps;

ps.nsx.ngay=ps.nsx.thang=ps.nsx.nam=0; ps.giatri=0.0;

ps.ten[0]=0;

for(i=1;i<=n;i++)

if (strcmp(ten,ds[i].ten)==0) return(ds[i]);

return(ps);

}

DienTro *ptim(char *ten,DienTro ds[],int n)

{

int i;

for(i=1;i<=n;i++)

if(strcmp(ten,ds[i].ten)==0) return(&ds[i]);

return(NULL);

}

void DoiCho(DienTro *p1,DienTro *p2)

{

Page 165: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

165

DienTro t;

t=*p1;

*p1=*p2;

*p2=t;

}

void Nhap(DienTro *p)

{

DienTro q; float gt;

printf("\nNhap vao ten dien tro :");gets(q.ten);

printf("\nNhap ngay ngay san xuat :");

scanf("%d%d%d",&q.nsx.ngay,&q.nsx.thang,&q.nsx.nam);

printf("\Nhap gia tri dien tro :");

scanf("%f%*c",&gt);q.giatri=gt;

*p=q;

}

void in(DienTro p)

{

printf("\n\n%s%12d-%d-%d%12.1f",p.ten,p.nsx.ngay,p.nsx.thang,p.nsx.nam,p.giatri);

}

void SapXep(DienTro *p,int n)

{

int i,j;

for(i=1;i<=n;++i)

for(j=i+1;j<=n;++j)

if(p[i].nsx.nam>p[j].nsx.nam)

DoiCho(&p[i],&p[j]);

}

void main()

{

DienTro *p,ds[100];int n,i,j;char ten[40];

/*vao so lieu */

clrscr();

printf("\n So dien tro n=");scanf("%d%*c",&n);

for(i=1;i<=n;i++)

Nhap(&ds[i]);

Page 166: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

166

//sap xep theo chieu tang cua ngay san xuat

SapXep(ds,n);

//in danh sach sau khi sap xep

printf("\n\nTen dien tro|Ngay san xuat|Gia tri");

for(i=1;i<=n;++i)

in(ds[i]);

//tim kiem theo ho ten

while(1)

{

printf("\nNhap ten dien tro can tim\(Bam Enter de ket thuc)");

gets(ten);

if(ten[0]==0) break;

if((p=ptim(ten,ds,n))==NULL)

printf("\n Khong tim thay");

else in(*p);

}

//tim kiem theo ho ten dung ham tim

while(1)

{

printf("\nNhap ten dien tro can tim\(bam Enter de ket thuc)");

gets(ten);

if(ten[0]==0) break;

if(tim(ten,ds,n).ten[0]==0)

printf("\nKhong tim thay");

else in(tim(ten,ds,n));

} //ket thuc

}

Bài 9:

Giả sử người lập trình cần quản lý các viện, mỗi viện có nhiều phòng, mỗi phòng lại có

nhiều nhân viên. Khi thiết kế chương trình người lập trình chưa biết có bao nhiêu viện, có bao

nhiêu phòng và cũng chưa biết mỗi phòng có bao nhiêu nhân viên. Nếu dùng mảng (cấp phát

bộ nhớ tĩnh ) thì phải sử dụng số viện tối đa. Mỗi viện phải xem như có cùng số phòng và mỗi

phòng phải xem như có số nhân viên bằng nhau. Như vậy sẽ có nhiều vùng nhớ được cấp phát

Page 167: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

167

mà không bao giờ sử dụng đến. Chương trình dưới đây minh họa cách cấp phát bộ nhớ động.

Số viện sẽ dùng bằng số viện cần quản lý. Mỗi viện sẽ được cấp phát một vùng nhớ vừa đủ để

chứa số phòng thực sự của nó, và mỗi phòng cũng có một vùng nhớ ứng với số nhân viên hiện

có của nó.

Nhân viên được mô tả bằng cấu trúc nhanvien có hai thành phần là hoten(họ tên), và

ns(năm sinh). Phòng được mô tả bởi cấu trúc phong có 3 thành phần là tenphong(tên phòng)

,sonhanvien(số nhân viên trong phòng) và nv(con trỏ kiểu nhanvien trỏ tới vùng nhớ chứa

thông tin các nhân viên trong phòng). Viện được mô tả bằng cấu trúc vien gồm 3 thành phần là

tenvien(Tên viện), sophong (số phòng của viện) và phong(con trỏ kiểu phong trỏ tới vùng nhớ

chứa thông tin của các phòng trong viện ).

Chương trình gồm hai phần:

+ Phần đầu gồm các thao tác nhập liệu và cấp phát bộ nhớ đặt xen kẽ nhau. Nhập liệu để

biết cần cấp phát bao nhiêu. Cấp phát để có vùng nhớ chứa thông tin nhập vào sau đó.

+ Phần tiếp là in ra màn hình các thông tin về các viện, các phòng và nhân viên vừa

nhập vào.

#include"stdio.h"

#include"conio.h"

#include"alloc.h"

typedef struct

{

char hoten[25];//ho ten

int ns; //nam sinh

} NhanVien;

typedef struct

{

char tenphong[30];//ten phong

int sonhanvien; //so nhan vien

NhanVien *nv;

} Phong;

typedef struct

{

char tenvien[30];//ten vien

Page 168: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

168

int sophong; //so phong

Phong *ph;

} Vien;

Vien *V;

int sovien;

void main()

{

int i, j, k, ngay, thang, nam, sophong, sonhanvien, ns;

//vao so lieu va cap phat bo nho

// so vien

clrscr();

printf("\n Nhap so Vien: ");scanf("%d%*c",&sovien);

V=(Vien*) malloc((sovien+1)*sizeof(Vien)); //Tao ra mot mang Vien

//vao so lieu tung vien

for(i=1;i<=sovien;++i)

{

printf("\nTen vien thu %d:",i);

gets(V[i].tenvien);

printf("\n So phong cua vien %s: ",V[i].tenvien);

scanf("%d%*c",&sophong);V[i].sophong=sophong;

V[i].ph=(Phong*)malloc((sophong+1)*sizeof(Phong));

for(j=1;j<=sophong;++j)

{

printf("\n Ten phong %d cua vien %s:",j,V[i].tenvien);

gets(V[i].ph[j].tenphong);

printf("\n So nhan vien cua phong %s vien %s :",V[i].ph[j].tenphong,V[i].tenvien);

scanf("%d%*c",&sonhanvien);

V[i].ph[j].sonhanvien=sonhanvien;

printf("\n\nVien %s Phong %s",V[i].tenvien,V[i].ph[j].tenphong);

for(k=1;k<=sonhanvien;++k)

{

printf("\nHo ten nhan vien %d: ",k);

Page 169: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

169

gets(V[i].ph[j].nv[k].hoten);

printf("\nNam sinh nhan vien %d: ",k);

scanf("%d%*c",&ns);

V[i].ph[j].nv[k].ns=ns;

}

}

}

//dua ra man hinh

clrscr();

for(i=1;i<=sovien;++i)

{

printf("\n Vien %s co %d phong",V[i].tenvien,V[i].sophong);

for(j=1;j<=V[i].sophong;++j)

{

printf("\n Phong %s vien %s co %d nhan

vien",V[i].ph[j].tenphong,V[i].tenvien,V[i].ph[j].sonhanvien);

for(k=1;k<=V[i].ph[j].sonhanvien;++k)

{

printf("\n Ho ten: %s Sinh nam: %d",V[i].ph[j].nv[k].hoten,V[i].ph

[j].nv[k].ns);

}

}

}

getch();

}

Bài 10. Viết chương trình nhập vào một mảng gồm n các số nguyên rồi ghi các

thông tin của mảng vừa nhập vào một tệp Mang.txt theo quy ước sau:

+ Dòng đầu tiên chứa n

+ Các dòng tiếp theo chứa các phần tử của mảng, mỗi phần tử được lưu trên một dòng

+ Dòng cuối cùng lưu tổng số các phần tử của mảng

#include <stdio.h>

#include<conio.h>

Page 170: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

170

void NhapMang(int *a,int n){

for(int i=0;i<n;i++)

{

printf("a[%d]=",i);scanf("%d",a+i);

}

}

void GhiTep(FILE *f,int *a,int n){

int dem=0; char *s;int n1,tam;

f=fopen("Mang.txt","w+"); // mo tep de ghi va doc tep

if (f!=NULL){

// ghi ten tep vao dau tep

fputs("Mang.txt\n",f);

//ghi so phan tu cua mang vao dau tep

fprintf(f,"%d\n",n);

for(int i=0;i<n;i++)

{

fprintf(f,"a[%d]=%d\n",i,*(a+i));

dem+=*(a+i);

}

//ghi tong cac phan tu vao tep

fprintf(f,"Tong=%d",dem);

//in noi dung tep ra man hinh

rewind(f); //dua con tro ve dau tep

fgets(s,20,f) ;

printf("%s\n",s);

fscanf(f,"%d",&n1);

printf("So phan tu cua mang la: %d\n",n1);

printf("Cac phan tu cua mang: ");

for(i=0;i<n; i++)

Page 171: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

171

{

fscanf(f,"%5s%d",s,&tam);printf("%5d",tam);

}

//in ra man hinh tong cac phan tu cua tep

fscanf(f,"%5s%d",s,&tam);

printf("\nTong cac phan tu la: %d",tam);

fclose(f);

}

else

printf("Khong mo duoc tep");

}

int main(void)

{

int a[100],n;

FILE *f;

clrscr();

printf("Nhap vao so phan tu cua mang n="); scanf("%d",&n);

NhapMang(a,n);

GhiTep(f,a,n);

getch();

return 0;}

Bài 11. Viết chương trình nhập vào một danh sách các diện trở rồi lưu danh sách

đó vào một têp “DT.DAT” (tệp nhị nhâp), rồi đọc danh sách đó từ tệp ra màn

hình.

#include<stdio.h>

#include<conio.h>

#include<string.h>

#include<io.h> //chua ham eof

typedef struct {

char name[15];

Page 172: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

172

float value;

} DienTro;

void NhapDS(DienTro *DT,int n){

float t;

DienTro p;

// phai nhap qua cau truc trung gian;

//ko nhap truc tiep duoc

for(int i=0;i<n;i++)

{

printf("Nhap ten dien tro: ");gets(p.name);

printf("Nhap gia tri DT: ");

scanf("%f%*c",&t); //Nhap xong xoa bo dem ban phim

p.value=t;

(*(DT+i))=p;// dau cham co do uu tien cao hon toan tu * nen fai viet (*(DT+i))

}

}

void InDS(DienTro DT[50],int n){

float t;

for(int i=0;i<n;i++)

{

printf("Ten dien tro: %15s ",DT[i].name);

printf(" Gia tri DT: %5.2f\n",DT[i].value);

}

}

void GhiTep(DienTro *DT,int n){

FILE *f; DienTro p;

f=fopen("DT.DAT","w+");

if (f!=NULL) {//neu mo tep thanh cong

for(int i=0;i<n;i++){

fwrite(DT+i,sizeof(DienTro),1,f);

Page 173: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

173

}

rewind(f); //dua con tro ve dau tep de doc tep

//Doc du lieu tu tep

printf("\n\nDoc danh sach tu tep\n");

while(fread(&p,sizeof(DienTro),1,f)>0){ //hoac cach khac while(!feof(f){})

//doc tung phan tu tu tep

printf("Ten dien tro: %15s ",p.name);

printf(" Gia tri DT: %5.2f\n",p.value);

}

}

fclose(f);//dong tep

}

void main(){

DienTro DT[100];int n;

clrscr();

printf("Nhap vao so phan tu cua mang: ");

scanf("%d%*c",&n);// phai xoa bo dem trc khi su dung ham gets()

NhapDS(DT,n);

InDS(DT,n) ;

GhiTep(DT,n);

getch();

}

Bài 12. Viết chương trình nhập vào một xâu ký tự rồi thực hiện các yêu cầu sau:

a. Cho biết độ dài xâu

b. Đếm xem trong xâu có bao nhiêu ký tự ‘a’

c. Chuẩn hóa xâu (đầu xâu và cuối xâu không có đấu cách, mỗi từ cách nhau một ký tự

trống )

d. Cho biết vị trí của ký tự ‘k’ đầu tiên trong xâu

e. Copy xâu KTLTC vào xâu đã cho

e. Đổi xâu sang chữ chứ thường hoặc chữ hoa.

Page 174: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

174

#include<stdio.h>

#include<conio.h>

#include<string.h>

void main(){

char *s;int dem=0;

//nhap xau ki tu

clrscr();

printf("Nhap vao mot xau ky tu: ");

gets(s);

//cho biet do dai xau ky tu

printf("Do dai cua xau ky tu: %d",strlen(s));

//Dem ky tu 'a' trong xau

for(int i=0;i<strlen(s);i++)

if (s[i]=='a') dem++;

printf("\nSo ky tu 'a' trong xau vua la: %d",dem);

//CHUAN HOA XAU

//cat bo khoang trong o dau xau

while(s[0]==' ') strcpy(s,s+1);

// cat bo khong trong o cuoi xau;luu y: s[strlen='\0']

while (s[strlen(s)-1]==' ') s[strlen(s)-1]='\0';

//cat bo khoang trong giua cac tu chi giu lai 1 dau cach

i=0;

while (i<strlen(s)) {

while(s[i]==' ' && s[i+1]==' ') strcpy(s+i,s+i+1);

i++;

}

printf("\n%s",s);

//so sanh xau ky tu voi xau "Lap trinh C"

if (strcmp(s,"Lap trinh C")>0)

printf("\nXau vua nhap lon hon xau Lap trinh C");

Page 175: Chương 1 Mở đầu 1.1. Phần cứng, phần mềm, và sụnect.ictu.edu.vn/attachments/article/215/Lap trinh C - Doan ngoc... · linh kiện, ốc vít và đai ốc, vỏ

175

else if (strcmp(s,"Lap trinh C")<0)

printf("\nXau vua nhap nho hon xau Lap trinh C");

else

printf("\nHai xau bang nhau");

// sao chep xau "KTLPC" sang nhap sang xau vua nhap

strcpy(s,"KTLPC");

printf("\nXau sau khi sao chep la: %s",s);

//Cho biet vi tri cua ky tu 'k'dau tien trong xau, ko phan biet chu hoa, thuong

dem=0;

for(i=0;i<strlen(s);i++)

if (s[i]=='K' || s[i]=='k'){

printf("\nVi tri ki tu k dau tien la: %d",i);

dem++;

}

if (dem==0)printf("\nTrong xau ko co ky tu k nao");

getch();

}