You are on page 1of 65

Bài 1: NGUYÊN TẮC LẬP TRÌNH

Các khái niệm cơ bản:


MTĐT hoạt động dựa trên nguyên tắc làm việc theo chương trình.
-1 Chương trình (Program):
Gồm những phát biểu, những lệnh riêng lẻ sắp xếp theo một trình tự nào đó nhằm
điểu khiển máy tính làm việc.
Các lệnh này phải được diễn tả bằng một ngôn ngữ lập trình thích hợp mới có thể
thực hiện được. Những ngôn ngữ thường dùng như : Basic, Pascal, C, Prolog ... .
-2 Ngôn ngữ lập trình (Programming language):
Là một hệ thống hữu hạn các ký hiệu
Ví dụ: Ngôn ngữ Pascal dùng các ký hiệu: if, then, else, while, , var, {, }.... gọi là các
từ khóa (key word)
và các ký tự: a,..,z,A,..,Z, 0,1,..,9,... không dùng các ký hiệu của bộ chữ cái Hy
Lạp.
Các ký hiệu đó phải tuân theo các qui ước về ngữ pháp và ngữ nghiã, cũng giống như
khi bạn muốn nói một câu có ý nghiã thì phải kết hợp các từ theo các qui tắc ngữ pháp
và ý nghiã của từ trong tiếng mình đang sử dụng.
Ngữ pháp (Syntax): qui ước về quan hệ giữa càc ký hiệu.
Ví dụ: Trong ngôn ngữ Pascal, các ký hiệu {, } phải đi thành từng cặp; sau ký hiệu IF
phải là một (biểu thức điều kiện), sau đó phải là ký hiệu THEN;...
Ngữ nghiã (semantics): Qui ước về ý nghiã của các ký hiệu.
Ví dụ: Dấu + biểu thị phép cộng; dấu * biểu thị phép nhân; phát biểu IF ... THEN ...
có nghiã là “Nếu ... thì ....”;...
-3 Sự thực hiện chương trình (Program Execution):
Chương trình được thực hiện tuân theo các qui ước ngữ nghiã của ngôn ngữ.
Ví dụ: Phát biểu:
IF (x > 0) THEN frintf("dương") ELSE frintf("không dương");
Đến lúc thực hiện phát biểu này nếu trị của x lớn hơn 0 thì phát biểu
WRITE(‘dương’) được thực hiện (chuỗi chữ ‘dương’ được in ra), còn phát biểu
WRITE(‘không dương’) không được thực hiện.
Phần lớn các ngôn ngữ hiện nay thực hiện tuần tự, tức là lần lượt thực hiện phát biểu
này xong rồi đến phát biểu kế tiếp. Cũng có ngôn ngữ thực hiện song song, tức là thực hiện
cùng lúc một số phát biểu.
-4 Thuật Toán (Algorithm): Cách thức giải quyết vấn đề.
Giáo sư Niklaus Wirth (Trường ĐH kỹ thuật Zurich, Thụy sĩ), người sáng lập ra
ngôn ngữ Pascal (1970) đã viết: Algorithms + Data structures = Programs
Để viết chương trình giải quyết một vấn đề, trước tiên cần phải có giải thuật.
Thuật toán là một dãy các chỉ dẫn rõ ràng các thao tác (công việc, phép toán...) trên
những đối tượng, sao cho sau một số hữu hạn bước thực hiện các thao tác, ta đạt được mục
tiêu định trước.
Có nhiều cách diễn đạt thuật giải:
a. Bằng ngôn ngữ tự nhiên:
Ví dụ: Thuật toán giải phương trình bậc nhất ax + b = 0
Bước 1: Nhập vào các hệ số a và b
Bước 2: Nếu a = 0 chuyển sang bước 3
nếu không thì chuyển sang bước 4.
Bước 3: Nếu b = 0 thì thông báo phương trình đã cho có vô số ngiệm
Nếu không thì thông báo là phương trình vô nghiệm.
Chuyển sang bước 5.
Bước 4: Thông báo nghiệm của phương trình là -b/a.
Bước 5: Dừng thuật toán.
Trong ví dụ này, đối tượng chịu thao tác là các số thực a, b, x với ràng buộc ax + b =
0. Và các thao tác là :
- Nhập hai số a, b
- So sánh ( a = 0 ?; b = 0 ?)
- Chia ( phép chia -b cho a)
- Thông báo kết quả
- Dừng thuật toán.
- Chuyển sang bước ...
Nhận xét: Ưu điểm là người viết không phải chuẩn bị trước những kiến thức gì đặt
biệt. nhưng thường dài dòng, không làm nổi bật được cấu trúc thuật toán.
b. Ngôn ngữ Lưu đồ (Flow Chart: sơ đồ khối):
Lưu đồ thuật giải được xây dựng từ những hình cơ bản sau:
a.Nút giới hạn:
B
egin E
nd
b. Nút thao tác:
A

c. Nút điều kiện:


-
ĐK
+
d. Cung: là những đường có hướng nối từ nút này đến nút khác.
Hoạt động của thuật toán dưới dạng lưu đồ được bắt đầu từ nút đầu tiên, theo các
cung đến các nút khác cho đến khi gặp nút kết thúc thì dừng thuật toán.
Ví dụ: Lưu đồ thuật toán giải phương trình bậc nhất ax + b = 0
Begin

Nhập a, b

-
a=0
+
-
b=0
Xuất phương trình
+ có nghiệm x = -b/a
Xuất phương trình Xuất phương trình
có vô nghiệm có vô số nghiệm

End
2
Nhận xét: Trực quan, giúp ta có được cái nhìn tổng quan hơn về toàn cảnh của quá
trình xử lý của thuật toán. Tuy nhiên ngôn ngữ lưu đồ chỉ thích hợp cho những thuật toán
tương đối đơn giản.
c. Dùng ngôn ngữ phỏng trình hay mã giả:
Ngôn ngữ phỏng trình vay mượn các cú pháp của một ngôn ngữ lập trình kết hợp với
ngôn ngữ tự nhiên để thể hiện thuật toán.
Dùng ngôn ngữ phỏng trình giúp người cài đặt dễ dàng nắm bắt nội dung của thuật
toán và dễ dàng cài đặt bằng 1 ngôn ngữ lập trình cụ thể vì thuật toán đã được biểu diễn
bằng các cấu trúc và ký hiệu chuẩn của NNLT.
d. Bằng ngôn ngữ lập trình: Người ta thường dùng ngôn ngữ Pascal để diễn đạt thuật
giải.
-IICÁC BƯỚC CƠ BẢN TRONG LẬP TRÌNH:
Khi giải một bài tin học bạn cần thực hiện tuần tự các bước:
Bước 1: Tìm hiểu mục đích và yêu cầu bài toán để: Xác định dữ liệu nhập và xuất .
Bước 2: Xác định các vấn đề cần giải quyết và Tìm thuật toán để giải quyết nó: bằng cách
xây dựng một số ví dụ phản ảnh đúng các yêu cầu đề ra, rồi thử giải các ví dụ đó để
hình thành dần những hướng đi của thuật toán.
Diễn tả rõ ràng thuật giải.
Bước 3: Xác định cấu trúc dữ liệu để biểu diễn các dữ liệu cần xử lý cho phù hợp với các
thao tác của thuật toán
Bước 4: Soạn thảo chương trình.
Bước 5: Chạy thử và sửa chữa: Chạy chương trình nhiều lần với những dữ liệu khác nhau
để kiểm tra tính đúng đắn thuật giải.
Ví dụ 1: Tìm UCLN của hai số tự nhiên a, b dựa theo tính chất Euclid: UCLN của 2 số tự
nhiên không thay đổi nếu ta thay số lớn nhất trong 2 số đó bằng hiệu của nó với số
thứ 2.
Bước 1:
input: 2 số tự nhiên a và b ;
output: UCLN(a,b)
Bước 2: Tìm thuật toán:
Xét ví dụ: a = 75 ; b = 30 và thực hiện theo tính chất đã phát biểu để tìm thuật toán
Thuật toán:
Bắt đầu
Trong khi a>0 và b > 0
Nếu a >b thì thay a = a - b
Nếu a < b thì thay b = b - a
Kết quả là (a + b).
Kết thúc
Ví dụ 2: Tìm tất cả các số tự nhiên có 2 chữ số, khi đảo thứ tự của 2 chữ số đó sẽ thu được
một số nguyên tố cùng nhau với số đã cho.
Cách giải 1:
Bước 1: Input: Các số nguyên x có 2 chữ số ab :10 ..99
output: Các số nguyên x = ab sao cho UCLN(ab, ba) = 1
Bước 2: Vấn đề cần giải quyết:Tìm số đảo của số x; Tìm UCLN(x,x')
• Tìm số đảo: x = ab x' = hdv(x) * 10 + hc(x)
• Tìm UCLN: áp dụng thuật toán Euclide
Thuật toán:Cho x lấy giá trị từ 10 đến 99
- Tìm số đảo x' của số x
3
- Nếu UCLN(x,x')=1 thì ghi nhận x
Cách giải 2:
Nhận Xét 1: x = 23 ta có UCLN(23, 32) =1 suy ra 23 là số cần tìm và số 32 cũng là
số cần tìm.
Nhận xét 2: x = 44 thì x'=44 UCLN(x,x')=44 suy ra x=aa không là số cần tìm.
Nhận xét 3: Trong các số x=a0 thì chỉ có x=10 là số cần tìm
Thuật toán 2: Ghi nhận số 10 là số cần tìm
Cho a = 1 tới 9
cho b = 1 tới a-1
x = a*10+b và x' = b*10+a
Nếu UCLN(x,x') =1 thì ghi nhận x và x'
Chú ý:
Khi lập trình thường có hai loại sai lầm như sau:
+ Thuật giải sai: Với cách giải sai thì chương trình sẽ cho kết quả sai
+ Thuật giải đúng nhưng thể hiện bằng chương trình sai: có nghiã không thể hiện
chính xác giải thuật bằng ngôn ngữ lập trình.
Ví dụ: Tính nghiệm phương trình bậc hai: x1:=(-b - sqrt(delta))/2*a là sai

4
Bài 2: Tổng Quan Về Ngôn Ngữ C
-I Giới thiệu về C:
Khác với Pascal, là ngôn ngữ cấp cao được xây dựng để học tập và giảng dạy lập
trình, ngôn ngữ C và ngôn ngữ phát triển của nó là C++ được xây dựng xuất phát từ những
yêu cầu xây dựng những ứng dụng thực tế, vì thế C có được tất cả những ưu điểm của ngôn
ngữ cấp cao đó là:
• Tính cô đọng và rất gọn khi lập trình, có thể thâm nhập vào mọi nguồn dữ liệu và
hệ thống.
• Có nhiều mô hình tổ chức bộ nhớ và nhiều toán tử mạnh.
Với những ưu điểm trên, ngày nay các chương trình lớn đều được viết bằng C. Ví dụ
như hệ điều hành Windows, Unix... Ngôn ngữ C trở nên bắt buộc đối với các nhà lập trình
chuyên nghiệp.
• Bộ phần mềm Turbo C 2.0 có các tập tin chính như sau:
- TC.EXE: Trình soạn thảo và biên dịch chính
- *.LIB: Các thư viện hàm
- *.H: Các tập tin tiêu đề khai báo các hằng, biến, kiểu dữ liệu, hàm thư viện.
• Khởi động trình soạn thảo TC: DDL>TC
• Đóng trình soạn thảo TC: Alt + X
• Thoát tạm khỏi IDE: Alt F O .Để vào lại IDE: nhập EXIT
I- Các ví dụ về chương trình C:
Ví dụ 1: In ra màn hình 2 dòng chữ:
DAY LA CHUONG TRINH C
CHAO CAC BAN
/* Chương trình in 2 dòng chữ*/ printf(“DAY LA CHUONG TRINH C”);
#include “stdio.h” printf(“\nCHAO CAC BAN”);
#include “conio.h” getch();
void main() /* hàm chính */ }
{
Giải thích:
• /* ... */ : dòng ghi chú sẽ không được biên dịch.
Mỗi chương trình nên bắt đầu bằng một ghi chú nói rõ mục đích của nó
• Chỉ thị #include : dùng nạp header file chứa các hàm thư viện sử dụng trong
chương trình:
- File stdio.h: chứa các hàm nhập xuất trên dòng nhập xuất gián tiếp (printf )
- File conio.h: chứa các hàm nhập xuất trực tiếp từ bàn phím, màn hình (getch).
Khi sử dụng một hàm chuẩn cần biết nó nằm trong tập tin header nào và phải dùng
lệnh #include để nạp tập tin đó vào chương trình.
#include <[path]filename> : mặc định sẽ tìm trong thư mục chỉ định trong Options
Directories INCLUDE Directories
#include “[path]filename” : mặc định tìm ở thư mục hiện hành, nếu không tìm thấy
sẽ tìm trong thư mục INCLUDE.
Thông báo lỗi nếu không tìm thấy: Unable to open include file ‘[path]filename’
• Dòng main() : là bắt buộc đối với tất cả các chương trình C. Dấu () sau từ khóa
main cho biết main là 1 khối chương trình gọi là hàm (function).
Khối thân hàm được xác định bằng cặp ký hiệu:
{ /* bắt đấu phần thân */
} /* kết thúc phần thân */
• Một câu lệnh được viết trên 1 hay nhiều dòng nhưng phải kết thức bởi dấu ;
Ví dụ 2: Chương trình tính chu vi và diện tích hình tròn theo giá trị bán kính r nhập vào từ
bàn phím.
#include “stdio.h” else
#include <conio.h> {
#define Pi 3.14 cv = 2 * PI * r;
main() dt = PI * r * r;
{ float r, cv, dt; /*Khai báo 3 biến kiểu printf(“\nKet Qua:”);
thực*/ printf(“\n\t Chu vi = %10.2f \n\t
printf(“\nNhap ban kinh r = “); Dien tich = %10.2f”, cv, dt );
scanf(“%f”, &r); }
if (r <= 0) getch();
printf(“\a\aBan kinh <= 0 khong }
tinh“);
Ví dụ 3: Một chương trình C có thể chứa 1 hay nhiều hàm, trong đó, bắt buộc phải có 1
hàm main và phải nằm trên tất cả những hàm khác (nếu có). hàm này được tự động
thực hiện đầu tiên khi chạy chương trình.
#include “stdio.h” else
#include <conio.h> { cv = 2 * PI * r; dt = PI * r * r;
#define Pi 3.14 printf(“\nKet Qua:”);
float r ; /*biến ngoài*/ printf(“\n\t Chu vi = %10.2f \n\t
Void NhapBanKinh( ); Dien tich = %10.2f”, cv, dt );
main() }
{ getch();
float cv, dt; /*Khai báo 2 biến cục bộ*/ }
printf(“\nChương trình tính diện tích Void NhapBanKinh( )
và chu vi hình tròn\n”); { printf(“\nNhap ban kinh hinh tron r =
NhapBanKinh; “);
if (r <= 0) scanf(“%f”, &r);
printf(“\a\aBan kinh <= 0 khong }
tinh“);
Chú thích: có thể sử dụng hằng M_PI đã định nghiã trong math.h
-IICấu trúc của một chương trình C:
• Các #include: dùng nạp header file chứa các hàm thư viện sử dụng trong chương trình:
• Các #define: dùng tạo các macro thay thế.
• Khai báo các đối tượng dữ liệu ngoài (biến, mảng, cấu trúc, hợp...)
• Khai báo trước đặc trưng các hàm tự tạo (nếu có).
• Hàm main
• Phần định nghiã các hàm tự tạo
Chú ý: Hàm main có thể đặt sau hoặc xen vào giữa các hàm khác, nhưng được tự động gọi
thực hiện đầu tiên trong chương trình.

6
BÀI 3: Các Thành Phần Trong Ngôn ngữ C
-I Khối lệnh:
Một dãy các câu lệnh nằm trong phạm vi 2 dấu móc { và } được gọi là khối lệnh
• Một khối lệnh được xem như là 1 lệnh.
• Do đó, các khối lệnh có thể lồng nhau.
-IIKiểu dữ liệu:
Một kiểu dữ liệu là một tập hợp các giá trị có chung cách thức lưu trữ trong bộ nhớ,
cùng phương pháp xử lý, cùng một số các phép toán.
Các kiểu dữ liệu cơ bản bao gồm:
Kiểu M Phạm vi Kích thước
Char -128 ... +127 1 byte
unsigned char 0... 255 1 byte
Int -32768 .. 32767 2 byte
unsigned (int) 0 .. 65535 2 byte
long (int) -2.147.483.648 .. 4 byte
2.147.483.647
unsigned long 0 .. 4.294.967.295 4 byte
Float 3.4E-38 .. 3.4E+38 4 byte
Double 1.7E-308.. 1.7E+308 8 byte
long double 3.4E-4932..1.1E+4932 10 byte
Kiểu số thực: Số có giá trị tuyệt đối nhỏ hơn cận dưới được xem = 0.
Kiểu char và unsigned char: Một giá trị kiểu char chiếm 1 byte và biểu diễn được một ký
tự trên bảng mã ASCII.
Ví dụ: ‘0’..’9’ có mã 48..57; ‘A’..’Z’ <--> 65..90; ‘a’..’z’ <--> 97..122
Ví dụ: sự khác nhau giữa 2 kiểu:
char c1=196; unsigned char c2 = 196;
printf(“Biểu diển c1 : %c %d \n Biểu diễn c2 : %c %d ”, c1, c1, c2, c2);
Kết quả: Biểu diễn c1 : - -60
Biểu diễn c2 : - 196
Thực chất: c1 = -60 nhưng c1 và c2 đều biểu diễn ký tự có mã ASCII là 196.
-IIIHằng:
Hằng là những giá trị cố định thuộc phạm vi của một kiểu dữ liệu, không bị thay đổi
khi thực hiện chương trình.
-1 Hằng số nguyên:
• Hằng int: -15 1234
Printf(“%d %d”, -15, 1234);
• Hằng long: Được viết: - 4567L hay - 4567l
Số nguyên vượt ra ngoài miền xác định của int cũng được xem là hằng long
• Hằng số nguyên hệ 8: Luôn nhận giá trị dương, được viết dưới dạng: 0a1a2a3
Ví dụ: số 97 hệ 10 được viết: 0141 trong hệ 8
Printf(“%7d, 0141) -->97
• Hằng nguyên hệ 16: Được viết: 0xa1a2... hay 0Xa1a2...
Ví dụ: số 169 được viết 0xa9 hay 0XA9
Printf(“%d”, 0xa9) --> 169.
-2 Hằng số thực:
• Kiểu float : 123.45F
• Kiểu double: 123.45 123.45e-4 (0.012345)
• Kiểu long double: 123.45L
-3 Hằng kiểu ký tự:
Là 1 ký tự riêng biệt được viết trong 2 dấu nháy đơn. Ví dụ: ‘A’
• Hằng ký tự thực sự là một số nguyên. Giá trị của hằng ký tự chính là mã ASCII
của ký tự đó
Ví dụ: char c1 = ‘A’;
printf(“Biểu diễn c1: %c %d %o %x”, c1, c1, c1, c1) --> A 65 101 41
• 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ụ: ‘B’ - ‘A’ = 66 - 65 = 1
• Có thể biểu diễn hằng ký tự bằng số:
Ví dụ: char c1=’A’,c2=65, c3=’\101’, c4 = ‘\x41’;
hay c1=’A’,c2=65, c3=0101, c4 = 0x41;
printf(“%c %c %c %c”,c1, c2, c3, c4); sẽ in ra AAAA
printf(“%c%c%c“,7,7,7); sẽ phát 3 tiếng beep.
• Một vài hằng ký tự đặt biệt ta cần phải sử dụng cách viết sau:
‘\’ ’ : ‘\” ‘: ‘\\’ : ‘\n’ : ‘\r’ ‘\b’: ‘\0’: ‘\t’ :
‘ “ \ LF (10) CR(13) backspace NULL Tab.
-4 Hằng chuỗi:
Là chuỗi bất kỳ đặt trong cặp dấu ngoặc kép (“ “)
Ví dụ: “TURBO C”
• Các ký tự trong chuỗi có thể gồm các ký tự không có trên bàn phím, khi đó được
biểu diễn sau ký hiệu (\)
• Chuỗi ký tự trong C được lưu trữ dưới dạng mảng các ký tự và được C thêm ký tự
‘\0’ (null) cuối chuỗi.
Ví dụ: Phân biệt giữa ký tự ‘a’ và chuỗi “a”
• Chuỗi rỗng được viết bằng 2 dấu nháy đôi liên tục: “”
-IVBiến:
Một biến là 1 vùng nhớ trong RAM dùng lưu trữ dữ liệu tạm thời trong quá trình
thực hiện chương trình. Việc truy xuất đến giá trị lưu trong vùng nhớ được thực hiện thông
qua tên biến.
-1 Khai báo biến:
[static] Kiểudữliệu tênbiến1[= giátrị] , tênbiến2[=giátrị];
Tên (identifier): là 1 chuỗi tối đa 32 ký tự gồm chữ cái, số, dấu gạch dưới. Trong đó
ký tự đầu tiên không phải là số. C phân biệt chữ hoa và chữ thường. Có thể chỉ
định chiều dài tối đa của tên bằng chức năng:
OptionsCompilerSourceIdentifier length.
-2 Vị trí khai báo:
Vị trí khai báo biến sẽ qui định phạm vi hoạt động của biến.
• Khai báo bên trong khối lệnh: (Biến cục bộ)
- Phải được khai báo sau dấu {, đầu khối và trước tất cả các câu lệnh khác trong khối.
- Phạm vi hoạt động: Các biến này chỉ được sử dụng bên trong khối lệnh đó.
- Thời gian tồn tại: Khi thực hiện xong khối lệnh, thì vùng nhớ cấp phát cho các
biến này sẽ xóa (ngoại trừ trường hợp có sử dụng từ khóa static).
- Giá trị ban đầu: Nếu không gán giá trị khởi đầu thì giá trị của các biến cục bộ chưa
được xác định. Trường hợp có sử dụng từ khóa Static: thì các biến này có giá trị khởi

8
đầu mặc định là 0 và chỉ được gán giá trị khởi đầu khi thực hiện lần đầu tiên, các lần
sau sẽ sử dụng giá trị hiện đang lưu trử.
- Tên các biến trong cùng 1 khối lệnh không được trùng nhau nhưng có thể trùng tên
với biến nằm ngoài khối lệnh.
Ví dụ: {
int a =5, b =2;
{
int a=4;
b = a+b;
printf(“\n In trong khối a = %d, b = %d, a, b);
}
printf(“\n In ngoài khối a = %d, b = %d, a, b);
}
Kết quả: In trong khối a = 4, b = 6
In ngoài khối a = 5, b = 6
Ví dụ: Phân biệt giữa biến cục bộ có cùng tên.
main()
{ int x = 5;
thu();
printf(“\n gia tri x trong ham main = %d, x);
}
void thu(void)
{ int x =3;
printf(“\n gia tri x trong ham thu = %d, x);
}
• Khai báo bên ngoài các hàm:(gọi là Biến ngoài)
- Phạm vi sử dụng của biến ngoài là từ vị trí khai báo của nó cho đến cuối chương
trình. Nó có thể sử dụng cho các chương trình khác nhờ khai báo extern viết trên các
file này.
- Giá trị ban đầu: Khi chưa được khởi đầu thì giá trị của biến ngoài = 0.
- Thời gian tồn tại: cho đến khi kết thúc chương trình.
-3 Lấy địa chỉ của biến:
Mỗi biến được cấp phát trong bộ nhớ gồm một hay nhiều byte liên tiếp tùy thuộc vào
kiểu dữ liệu. Địa chỉ của byte đầu tiên là địa chỉ của biến.
Phép toán: &tênbiến trả về địa chỉ của biến.
-VLệnh Nhập Xuất Dữ Liệu Cơ Bản:
-1 Xuất dữ liệu ra màn hình:
printf(“dòng điều khiển”, danh sách biểu thức)
• Dòng điều khiển: gồm 3 loại
- Các ký tự hiển thị
- Các ký tự điều khiển: ký tự \ dùng để bắt đầu một ký hiệu điều khiển.
Ký hiệu Điều khiển Ký Hiệu Điều khiển
\n NewLine. Dời con trỏ xuống dòng \\ Backslash. In dấu \
\t Tab. Dời con trỏ đến vị trí tab kế tiếp \” Dùng in dấu nháy kép “
\r Carriage return. Dời con trỏ về đầu dòng \’ Dùng in dấu nháy đơn ‘
\a Alert. Phát tiếng beep ở loa
- Các mã đặc tả giá trị của biểu thức khi hiển thị. Thứ tự các đặt tả phải phù hợp
với kiểu của biểu thức cần in.

9
Đặc tả Ý nghiã Đặc tả Ý nghiã
%d số nguyên hệ thập phân có dấu %e %E số thực dạng mũ 10 (1.23456e+2)
%u số nguyên không dấu %g Đọc 1 số dấu chấm động
%o số bát phân %c ký tự đơn
%x số thập lục phân %s chuỗi ký tự
%f số thực (123.456)
Để ấn định độ rộng tối thiểu vùng in 1 số nguyên :%[-][n]d
Ví dụ: printf(“%6d”,-456) |__-456| //thêm dấu cách bên trái
printf(“%-6d”,-456) |-456__| //Canh lề trái
printf(“%2d”,-456) |-456|
Đối với số thực: %[-][n][.tp]f
Ví dụ: printf(“%f”,-45.63) |-45.630000|
printf(“%f”,45.63) |45.630000|
printf(“%8.3d”,-45.63) |_-45.630| /*thêm dấu cách bên trái
printf(“%.1d”,-45.67) |-45.7|
-2 Xuất kết quả ra máy in:
fprintf(stdprn,“dòng điều khiển”, danh sách biểu thức)
* Tham số stdprn dùng chỉ định thiết bị xuất là máy in.
-3 Nhập giá trị cho biến:
a. Toán tử gán và biểu thức gán: (Assignment operators & Assignment expression)
• Trong C có 2 kiểu gán giá trị như sau:
Phép gán đơn giản: biến = biểu thức ;
Phép gán phức hợp: biến pt= biểu thức; <==>biến = biến pt biểuthức
pt : +, -, *, /, %
Ví dụ: a = a + 2; <==> a += 2;
a = a*(b-c); <==> a *= b-c;
• Biểu thức gán: biến = biểuthức
- Giá trị của biểu thức gán là giá trị của biểuthức vế phải
- Kiểu của biểu thức gán là kiểu của biến.
Ví dụ: a = b = 5; <==> a = 5; b = 5;
x = (a = 2) * (b = 3);<==> a = 2; b = 3; x = a * b ; (x==6)
b- Hàm scanf: int scanf(“chuổi điều khiển”, danh sách địa chỉ các biến);
int scanf(const char *đk [,danh sách đối số]);
Dùng đọc dữ liệu trên dòng nhập stdin vào cho các biến. Nếu dòng nhập stdin không
có đủ dữ liệu thì hàm sẽ chờ nhập tiếp dữ liệu cho dòng nhập từ bàn phím, khi ấn phím
Enter, hàm tiếp tục đọc dữ liệu cho các biến còn lại.
Dòng nhập stdin: là vùng nhớ chứa dãy ký tự nhập từ bàn phím, bao gồm cả các ký
tự trắng như dấu cách, tab, ký tự xuống dòng \n.
Chuỗi điều khiển: chứa 3 loại : Các Mã đặc tả kiểu dữ liệu của biến; Ký tự trắng; Ký
tự khác ký tự trắng
Mã đặc tả: dùng xác định cách thức đọc ký tự trên dòng nhập và chuyển thành kiểu dữ
liệu đã đặc tả trước khi gán nó cho biến tương ứng.
Mỗi đặt tả có dạng: %[*][w][.sốlẻ]kýtựchuyểndạng
Ký tự chuyển dạng gồm: d, ld, o, lo, x, lx, f hay e, lf hay le, c, s
[dãykýtự]: Đọc cho đến khi gặp 1 ký tự không thuộc dãy
[^dãykýtự]: Đọc cho đến khi gặp 1 ký tự thuộc dãy.
W : chỉ định chiều dài tối đa ký tự cần đọc.
Cách đọc trên dòng nhập: có 2 cách

10
- Đọc theo ký tự: khi ta sử dụng 1 trong 3 ký tự chuyển dạng: c, [...], [^...]
Ví dụ: int a,b;
char s1[10], s2[10];
scanf(“%d%[_0123456789]%[^0123456789]%3d”, &a, s1, s2,&b);
Với dòng nhập là: 35_13456_XZY_58654
thì: a=35; s1=”_13456_”; s2=”XYZ_” ; b=58645.
- Đọc theo từng trường: mỗi trường là một dãy ký tự được kết thúc bằng ký tự
trắng. Cách đọc theo trường phụ thuộc vào [W]
Ví dụ: scanf(“%f%5f%3d%d”,&x,&y,&a,&b)
Với dòng nhập là: 54.32e-1 25 1234567
Thì các biến sẽ nhận giá trị : x = 54.32e-1; y = 25; a = 123; b = 4567.
Dấu * dùng đọc dữ liệu trên dòng nhập nhưng không lưu vào bộ nhớ. Như vậy,
đặt tả chứa dấu * sẽ không có đối số tương ứng.
Ví dụ: scanf(“%d %*c %d”,&x,&y); dữ liệu nhập: “10/20” --> x=10; y=20
Ký tự trắng: dùng bỏ qua 1 hay nhiều khoảng trống (như dấu cách, tab, ký hiệu xuống
dòng \n) trên dòng nhập cho đến khi gặp ký tự khác trống.
Ví dụ: scanf(“%d %d “,&a,&b);
Dòng nhập phải có 3 trường: 25 12 5 .
Khi đó a=25, b=12 còn 5 vẫn còn trên stdin
Ký tự khác ký tự trắng: dùng đọc và bỏ qua ký tự phù hợp trên dòng nhập. Hàm scanf sẽ
chấm dứt thao tác nếu không tìm thấy ký tự chỉ định
Ví dụ: scanf(“%d / %d”,&x,&y)
Nếu dữ liệu nhập là : 10/20 thì x =10; y = 20
Nếu dữ liệu nhập là : 10 20 <Enter> thì a=10 còn y thì không xác định. Do hàm scanf
dò tìm ký tự / không thấy và số 20 ‘\n’ (10) vẫn còn trên trường vào.
Chú ý: int a,b; scanf(“%d”,&a); scanf(“%d”,&b);
nếu nhập: 25 123 <Enter> thì trôi qua lệnh scanf thứ 2 và a = 25, b = 123.

11
BÀI 4 : CÁC PHÉP TOÁN & HÀM CƠ BẢN
-I Các phép toán
-1 Các phép toán số học:(Arithmetic Operators)
C có 7 toán tử số học gồm
a) 5 toán tử 2 ngôi:
Phép toán Ký hiệu Ví dụ Phép toán Ký Ví dụ
hiệu
Cộng + a+b Nhân * a*b
Trừ - a-b Chia / a/b
Lấy phần dư % n%m
(Modulus)
• Phép chia trên 2 số nguyên cho kết quả là 1 số nguyên, cắt bỏ phần thập phân
không làm tròn số. Ví dụ: 19 / 5 = 3 cắt bỏ 0.8
• Phép chia lấy phần dư chỉ được dùng trên 2 toán hạng kiểu số nguyên (nếu không
sẽ sinh lỗi cú pháp) Ví dụ: 19 % 5 = 4
• Thứ tự thực hiện các toán tử trong một biểu thức tương tự như trong Pascal
Ví dụ: được viết (b*b - 4*a*c)/(2*d) hay (b*b - 4*a*c)/2/d
b) Hai toán tử 1 ngôi: Dùng tăng giảm các biến nguyên hay thực 1 đơn vị
Toán tử tăng (++) : ++biến; hay biến++;
Ví dụ: ++n; hay n++<==> n = n+1;
Toán tử giảm (--): --biến; hay biến--;
Ví dụ: --n; hay n--; <==> n = n -1;
Chú ý: Trường hợp sử dụng toán tử này trong một biểu thức thì việc đặt trước hay sau sẽ
ảnh hưởng đến kết quả bài toán:
Đặt trước: nếu muốn thay đổi giá trị cho n trước khi sử dụng n
Đặt sau: nếu muốn thay đổi giá trị cho n sau khi sử dụng n xong.
Ví dụ: Với n = 4. Lệnh x = ++n sẽ gán 5 cho x. Lệnh x = n++ sẽ gán 4 cho x
Ví dụ: Với x = 10 và y =11 thì:
printf(“%d %d %d“, x--*--y, x-- - --y, x++ + ++y);
Cho kết quả: 120 0 22
Nhưng nếu tách thành 3 lệnh printf:
printf(“%d “, x--*--y); printf(“%d”, x-- - --y); printf(“%d “, x++ + ++y);
Cho kết quả: 120 -2 20
• Độ ưu tiên:Toán tử 1 ngôi có độ ưu tiên cao hơn toán tử 2 ngôi.
-2 Phép chuyển đổi kiểu dữ liệu:
Việc chuyển đổi kiểu dữ liệu thường được diễn ra tự động trong 2 trường hợp:
• Khi 2 toán hạng trong 1 phép toán khác kiểu thì:
- Kiểu thấp hơn sẽ được nâng thành kiểu cao hơn trước khi thực hiện phép
toán.
- Giá trị thu được có kiểu cao hơn
Ví dụ: 1.5 * (5/2) ---> 1.5 * 2 = 3.00
1.5 * 5/2 ---> 7.5 / 2 = 3.75
1.5 * (5/2F) ---> 1.5 * 2.5= 3.75
• Khi gán giá trị kiểu này cho 1 biến kiểu kia: Giá trị của biểu thức vế phải được
chuyển sang kiểu của vế trái và đó là kiểu kết quả.
Ví dụ:int n;
n = 10.5; ---> n = 10

12
Trong một số trường hợp bạn cần sử dụng phép chuyển kiểu:
(type)expression
Phép chuyển kiểu cho ra giá trị thuộc kiểu type chỉ định. Bản thân của expression
thì không thay đổi kiểu.
Ví dụ: Đổi số thực sang số nguyên có làm tròn số
int a; float x;
a = (int)( x + 0.5);
x = (float)a / 2; /*nếu a=5 ==> x = 2.5*/
• Độ ưu tiên của toán tử ép kiểu cùng cấp với toán tử một ngôi.
-3 Các toán tử quan hệ và logic: (Ralational & logical operators)
Các phép toán quan hệ và logic cho kết quả là 1 (Đúng) | 0 (Sai). Được dùng tạo các
biểu thức điều kiện cho việc chọn lựa công việc cần thực hiện.
a) Kiểu logic: C không có kiểu logic cụ thể như Pascal, mà xem:
Số <> 0 là True; Số = 0 là False.
b) Toán tử quan hệ:
Phép so sánh Ký hiệu Ví dụ Phép so sánh Ký Ví dụ
hiệu
Bằng == a == b Lớn hơn hoặc bằng >= a >= b
Khác != a != b Nhỏ hơn < a<b
Lớn hơn > a>b Nhỏ hơn hoặc bằng <= a <= b
Độ ưu tiên:
• Hai phép toán đầu có cùng độ ưu tiên. Bốn phép toán sau cũng cùng độ ưu tiên
nhưng cao hơn 2 phép toán đầu.
• Phép toán so sánh có độ ưu tiên thấp hơn so với các phép toán số học.
Ví dụ: a < b + c <==> a < (b + c)
c) Các phép toán Logic:
Các phép toán logic có thể thực hiện trên các toán hạng nguyên hay thực.
* Phèp phủ định: ! * Phép Và: && * Phép Hoặc ||
Ví dụ: 3 && 7 ---> 1 !1.2 ---> 0 3 || 7 ---> 1
Độ ưu tiên:Các phép toán so sánh có độ ưu tiên nhỏ hơn phép phủ định nhưng lớn hơn
phép && và ||.
Ví dụ: (a < b) && (c > d) <==> a < b && c > d
-4 Phép toán ? : <điều kiện> ? <biểu thức 1> : <biểu thức 2> ;
Trả về giá trị của <biểu thức 1> nếu <điều kiện> đúng, ngược lại trả về giá trị của
<biểu thức 2>
Ví dụ: Tìm số lớn nhất trong 2 số bất kỳ nhập từ bàn phím.
main()
{
float a, b, max;
clrscr();
printf(“\n Nhập số thứ nhất: “);scanf(“%f”,&a);
printf(“\n Nhập số thứ hai : “); scanf(“%f”,&b);
if (a > b) max = a;
else max = b;
printf(“\n Số lớn nhất là : %f “, max);
getch();
}
Thì có thể viết: max = (a > b) ? a : b ;

13
-5 Phép toán phẩy: (<biểu thức 1> , <biểu thức 2> , ... , <biểu thức n>)
Việc thực hiện được thực hiện từ trái sang phải. Kết quả và kiểu dữ liệu của biểu
thức phẩy là của biểu thức ở bên phải <biểu thức n>.
Ví dụ: m = ( t = 2, t*t+3 ); t = 2 và m = 7
-6 Phép toán Xử lý Bit:
Dùng xử lý đến từng Bit của một số nguyên.
Phép toán Ý nghiã Ví dụ
a&b Phép AND theo Bit
a|b Phép OR theo Bit
a^b Phép hoặc loại trừ (XOR)
~a Lấy phần bù theo Bit
a << n Dịch trái n Bit =a * 2n
A >> n Dịch phải n Bit = a / 2n
Chú ý: Phép chuyển trên giá trị Int bảo toàn bit dấu, thực hiện trên giá trị unsigned
int không bảo toàn Bit dấu.
Ví dụ: (-256) << 2 = -1024; (-256) >> 2 = -64.
Ví dụ: Để trích Byte thấp và byte cao của một số nguyên:
b_thap = a & 0xff
b_cao = a >> 8
-7 Thứ tự ưu tiên của các toán tử:
ưu tiên Toán tử Thứ tự thực hiện
1 ( ) [ ] . -> Trái sang phải
2 ! ++ -- - (type) sizeof Phải sang trái
3 * / % Trái sang phải
4 + - Trái sang phải
5 << >>
6 < <= > >= Trái sang phải
7 == != Trái sang phải
8 &
9 ^
10 |
11 && Trái sang phải
12 || Trái sang phải
13 ? :
14 = += -= *= /= %= Phải sang trái
15 , (dấu phẩy) Trái sang phải
-IIMột Số Hàm Cơ Bản:
-1 Hàm đại số:
1) Tính trị tuyệt đối của số nguyên num: int abs(int num); (stdlib.h)
2) Tính trị tuyệt đối của số nguyên kiểu long int: long labs(long num); (stdlib.h)
/* Các hàm sau khai báo trong math.h */
3) Tính trị tuyệt đối của 1 số thực x: double fabs(double x); (math.h)
4) Tìm số nguyên lớn nhất ≤ x: double floor(double x);
5) Tìm số nguyên bé nhất ≥ x: double ceil(double x);
6) Tìm phần dư của y/x: double fmod(double y, double x);
7) Tính căn bặc 2 của x: double sqrt(double x);
8) Tính yx : double pow(double y, double x);

14
9) Tính ex : double exp(double x);
10)Tính ln(x): double log(double x);
11)Tính log10x: double log10(double x);
-2 Hàm lượng giác:
12)Tính sin(x): double sin(double x);
13)Tính cosine của x: double cos(double x);
14)Tính tangent của x: double tan(double x);
15)Tính arc cosine của (x):double acos(double x);
16)Tính arc sine của (x) double asin(double x);
17)Tính arc tangent của x: double atan(double x);
-3 Hàm xử lý ký tự: <CTYPE.H>
1. int tolower(int ch); Trả về chữ thường
2. int toupper(int ch); Trả về chữ in hoa
3. int islower(int ch); Là chữ thường
4. int isupper(int ch); Là chữ in hoa
5. int isdigit( int ch); Là số
6. int isalpha(int ch); Là mẫu tự
7. int isalnum(int ch); Là số hay mẫu tự
8. int isspace(int ch); Là dấu cách.

15
BÀI 5 : CÁC CẤU TRÚC ĐIỀU KHIỂN
-I Cấu trúc chọn:
-1 Cấu trúc chọn với if:
Cú pháp: if (điều kiện)
<Lệnh>;
<Lệnh>: có thể là lệnh đơn hay khối lệnh.
Lệnh ;
Lưu đồ:

Ví dụ: Xét bài toán tính tiền 1 khách hàng phải trả khi mua hàng với quy tắc giảm giá như
sau:
• Nếu mua với số lượng > 50 thì được giảm 2%
• Nếu mua với số lượng > 100 thì được giảm 3%
Trong đó đơn giá và số lượng mua được nhập từ bàn phím
main() if (soluong > 50 && soluong <=100)
{ tien = tien * 0.98;
float dongia, soluong, tien; if (soluong >100)
printf(“\n Cho biết đơn giá : “); tien = tien * 0.97;
scanf(“%f”, &dongia); printf(“\nTien phai tra : %10.2f “, tien);
printf(“\n Cho biết số lượng: “); getch();
scanf(“%f”, &soluong); }
tien = soluong * dongia;
Ví dụ: Cần hiện số phần trăm giảm giá
if (soluong > 50 && soluong <=100)
{ tien = tien * 0.98;
printf(“\n Khách được giảm giá 2% “);
}
if (soluong >100)
{ tien = tien * 0.97;
printf(“\n Khách được giảm giá 3% “);
}
1.1 Cấu trúc chọn với if/else:
Cú pháp: if (điều kiện)
lệnh1;
else
lệnh2;
Luu đồ:
Ví dụ: sử dụng ví dụ tính tiền mua hàng Lệnh 1 Lệnh 2
{ ... if (soluong > 50)
tien = tien * 0.98;
else
if (soluong >100)
tien = tien * 0.97;
......}
Ví dụ: Viết chương trình phân loại học sinh theo DTB ( bằng 2 cách)

16
Cách 1: Xét bắt đầu từ dtb >= 5
main() printf(“\n Khá “);
{ else
float dtb; printf(“\nGiỏi “);
printf(“\n Cho biết điểm trung bình : “); else
scanf(“%f”, &dtb); if (dtb >= 3.5)
if (dtb >= 5 ) printf(“\n Yếu “);
if (dtb <= 6.5) else
printf(“\n Trung bình”); print(“\n Kém “);
else getch();
if (dtb <= 8) }
Cách 2: Xét bắt đầu từ dtb >=8.5
Chú ý: Các lệnh if..else lồng nhau thì else sẽ luôn luôn kết hợp với if nào chưa có else gần
nhất.
Ví dụ:
if (n >0) if (n >0) if (n >0)
if (a > b) if (a > b) {if (a > b)
z = a; z = a; z = a;}
else else else
z = b; z = b; z = b;
-2 Cấu trúc chọn với switch:
switch (biểu thức)
{
case hằng1: <Công việc 1>;
break;
case hằng2: <Công việc 2>;
break;
.............
case hằngn: <Công việc n>;
break;
[default : Công việc n+1;]
}
Diễn giải:
Các hằng i phải là số nguyên. Nếu Biểu thức là số thực sẽ được chuyển sang kiểu nguyên
Giá trị của biểu thức sẽ lần lược so sánh với các hằngi . nếu biểuthức == hằngi thì lệnhi
sẽ được thực hiện. Ngược lại thì lệnh tương ứng với từ khóa default sẽ được thực hiện (nếu
có).
Sau khi máy thực hiện lệnhi nếu sau lệnhi không có lệnh break thì máy sẽ tiếp tục thực
hiện các lệnhi+1,.. cho tới khi gặp lệnh break hay kết thúc lệnh.
Lệnh break cho phép thoát khỏi vòng lặp For, while, do while hoặc lệnh switch.
Ví dụ 1: (máy tính bỏ túi) Nhập vào 2 số và 1 phép toán (+,-,*,/). Hãy in ra giá trị của biểu
thức đó.
Ví dụ 2: Biết tháng và năm. Hãy tính số ngày trong tháng đó.
main()
{
int thang, nam;
printf(“\nCho biết tháng: “);scanf(“%d”,&thang);
printf(“\nCho biết năm: “);scanf(“%d”,&nam);
songay = 0;

17
switch (thang)
{
case 1: case 3: case 5: case 7: case 8: case 10:
case 12: songay = 31; break;
case 4:case 6:case 9:
case 11: songay = 30; break;
case 2:if (nam % 4 == 0) songay = 29;
else songay = 28;
break;
default: printf(“\nNhap thang sai “);
}
if (songay >0) printf(“\nThang %d nam %d co %d ngay”,thang, nam, songay);
getch();
}
-IICấu trúc Vòng Lặp
Mục đích: Thực hiện công việc lặp đi lặp lại theo một qui luật nào đó. Số lần lặp có thể xác
định trước hoặc không thể xác định được.
-1 Cấu trúc lặp với điều kiện trước: (câu lệnh While)
Cú pháp : while (Điều kiện) <công việc>;
Ngữ nghĩa: TRONG KHI điều kiện còn đúng thì thực hiện công việc.
Diễn giải: Trong khi <Điều kiện> còn đúng thì còn thực hiện <lệnh>. Vòng lặp kết
thúc khi <Điều kiện> sai.
Ví dụ 1: Tìm USCLN của hai số nguyên dương m và n. Theo nguyên tắc:
Nếu m=n : thì UCLN(m,n) = m
Nếu m > n : UCLN(m,n) = UCLN(m-n,n)
Nếu m<n : UCLN(m,n) = UCLN(m,n-m)
Ví dụ: n= 35 m= 84 thì UCLN là 7.
while (m<> n)
if (m>n) m:=m-n; else n:=m-n;
Ví dụ 2 : Lãi suất hàng tháng gửi tiết kiệm không kỳ hạn là 1.2%. Một người gởi vào một
số tiền ban đầu là a. Sau bao nhiêu tháng người đó đạt số tiền không nhỏ hơn b?
Ý tưởng: - Số tiền gởi đầu tháng là a
- Cuối tháng số tiền thu được gồm tiền đầu tháng cộng tiền lãi (a+a*k). Đó
cũng là tiền gởi tiết kiệm cho đầu tháng sau.
main() {
{ double a, b, c; printf(“\nNhap lai tien dự kiến:“);
int dem = 0; scanf(“%lf”,&b);
printf(“\n Nhap tien goi ban dau: “); }
scanf(“%lf”, &a); c = a;
while (a <= 0) while (c < b)
{ {
printf(“\nHay nhap lai tien goi:“); c = c + c*0.012;
scanf(“%lf”, &a); dem++;
} }
printf(“\n Nhap so tien dự kiến : “); printf(“\nPhải gởi %d thang”,dem);
scanf(“%lf”,&b); getch();
while (b <= a) }
Chú ý:
- Vòng lặp không thực hiện lần nào: khi lần đầu tiên <điều kiện> có giá trị 0.

18
- Vòng lặp không dừng: khi <điều kiện> luôn luôn khác 0.
Ví dụ: 100 trâu 100 cỏ, trâu đứng ăn 5, trâu nằm ăn 3, lụ khụ trâu già 3 con 1 bó.Hỏi mỗi
loại có mấy con?
HD: a:=1 to 100 div 5; b:=1 to 100 div 3;
nếu a*5+b*3+(100-a-b)/3=100 thì a, b, (100-a-b) div 3 là kết quả
#include "stdio.h"
main()
{ int td, tn, tg;
printf("\nBai toan dem trau");
td = 1;
while (td < 100/5)
{ tn = 1;
while (tn < 100/3)
{ if (td*5 + tn*3 + (float)(100-td-tn)/3 == 100)
{ tg = (100-td-tn);
printf("\n trau : %d, %d,%d ",td,tn,tg);
}
tn++;
}
td++;
}
getch();
}
-2 Cấu trúc lặp với điều kiện sau: do .. while
Cú pháp:
do {
<công viêc lặp>
} while (biểu thức);
Ngữ nghiã: Dùng lặp đi lặp lại một công việc trong khi biểu thức điều kiện còn đúng
(khác 0) và thoát khi biểu thức điều kiện sai ( bằng 0)
Chú ý: Thân vòng lặp được thực hiện ít nhất 1 lần
Ví dụ: Viết chương trình tính trung bình cộng của n số nguyên nhập từ bàn phím. Khi nào
nhập số 0 thì kết thúc nhập và tính trung bình.
main() n +=1;
{ int so, tong=0, n = 0; } while (so != 0);
float tb; n -= 1;
clrscr(); tb = (float) tong / n;
do printf(“\nBan da nhap vao : %d so”,n);
{ printf(“\nNhap 1 so :”); printf(“\nTrung binh cong = %f”,tb);
scanf(“%d”, &so); getch();
tong += so; }
Ví dụ : Nhập số thực Eps ∈(0,1). Tính số Népè e (= 2,71828) bằng công thức khai triển
chuỗi như sau: e = 1 + 1/1! + 1/2! + ..+ 1/N! cho đến khi 1/(N+1)! < Eps .
HD: Dùng thuật toán cộng dồn với một biến chạy i từ 1 trở đi
main()
{ double e = 1; i =1; x = 1, eps;
do { printf(“\n Nhâp số Epsilon: “); scanf(“%lf”, &eps);
} while ( eps >=1 || eps <=0 );
do {
e := e + 1/x; /* hay e += 1/x ; */

19
i++; x = x * i; /* hay x = x *++i; */ /*hay x *= ++i */
} while (1/ x >= eps);
printf(“\nSo e = %.10lf “, eps);
}
-3 Cấu trúc lặp với for :
Vấn đề: Lãi suất hàng tháng gưỉ tiền không kỳ hạn là k%. Một người gởi vào một số
tiền ban đầu là a. Tính tiền người đó có được sau t tháng.
Diễn giải: Tiền lãi tháng = tiền gởi đầu tháng * k %
Tiền gởi đầu tháng sau= tiền gởi đầu tháng trước + tiền lãi tháng
Cú pháp:
for (biểu thức 1; biểu thức 2; biểu thức 3)
lệnh;
Biểu thức 1: Thường là 1 phép gán để tạo giá trị ban đầu cho biến điều khiển
Biểu thức 2: là biểu thức điều kiện
Biểu thức 3: là phép gán dùng thay đổi giá trị của biến điều khiển.
Ví dụ: Viết chương trình tính N! trong đó N là số nguyên dương nhập từ bàn phím.
main() for (i = 1; i <= n ; i++)
{ double gt = 1, ; gt = gt * i ; /* gt *= i */
unsigned int n, i; printf(“\nN! = %.0lf “,gt );
printf(“\nTinh giai thua cua : “); getch();
scanf(“%u”, &n); }
Chú ý:
Biểu thức 1 chỉ được thực hiện 1 lần (lần đầu)
Biểu thức 2, biểu thức 3 và thân vòng For được thực hiện nhiều lần.
Bất kỳ biểu thức nào trong 3 biểu thức trên đều có quyền vắng mặt, Tuy nhiên vẫn
phải giữ dấu “; “
Ví dụ : Vắng biểu thức 1 và 3
main() scanf(“%u”, &n);
{ double gt = 1 ; for ( ; ++i <= n ; ) gt *= i ;
unsigned int n, i =0; printf(“\nN! = %.0lf “,gt );
printf(“\nTinh giai thua cua : “); }
Nếu không có biểu thức 2 hay biểu thức 2 là 1 hằng số khác 0 thì vòng lặp thực hiện vô
tận. Khi đó muốn thoát khỏi vòng lặp phải dùng lệnh break, goto nhan, return(biểuthức),
exit(mã thoát).
Ví dụ : Vắng biểu thức 1, 2 và 3
main() for ( ; ; )
{ { gt = gt * i ; /* gt *= i */
double gt = 1 ; if (++i > n) break;
unsigned int n, i = 1; }
printf(“\nTinh giai thua cua : “); printf(“\n Ket qua %u! = %.0lf “, n, gt );
scanf(“%u”, &n); }

Mỗi biểu thức có thể gồm 1 dãy biểu thức con cách nhau bởi dấu phẩy “,” và được
tính toán từ trái qua phải.
Nếu biểu thức 2 là 1 dãy các biểu thức thì tính đúng sai của biểu thức 2 được xác
định bởi giá trị của biểu thức cuối cùng trong dãy này.
Ví dụ : Vắng biểu thức 1 và 3 và thân vòng lặp là rỗng.
main()

20
{ double gt = 1 ;
unsigned int n, i =1;
printf(“\nTinh giai thua cua : “); scanf(“%u”, &n);
for ( ; gt = gt * i, ++i <= n ; ) ; /* for ( ; i <= n ; gt = gt * i++) ; */
printf(“\nN! = %.0lf “,gt );
}
Ví dụ: Lãi suất hàng tháng gửi tiết kiệm theo chế độ có kì hạn 3 tháng là 2%( tức là cứ 3
tháng người ta lấy số tiền hiện có nhân với lãi suất 2% 1 tháng để ra lãi, rồi cộng lãi
với vốn để có vốn mới). Một người gởi vào 1 số tiền ban đầu là a. Tính số tiền người
đó có được sau t tháng.
HD : Số lần tính lãi là : t % 3. Tiền vốn có được sau mỗi lần tính lãi : a + a*3*2%
Ví dụ 3: Tìm tất cả các số có 3 chữ số abc sao cho tổng lập phương của các chữ số thì bằng
chính số đó. { 153, 370, 371, 407 }
abc =100a + 10b + c = a3 + b3 + c3
Hướng Dẫn: a lấy giá trị 1..9; b và c lấy giá trị từ 0..9
-IIICác Câu Lệnh Khác:
-1 Lệnh goto:
Công dụng: Dùng chuyển điều khiển thực hiện đến 1 câu lệnh nào đó được chỉ định bởi
nhãn.
Cú pháp: goto nhan;
Trong đó, nhan là 1 tên hợp lệ được đặt trước lệnh cần nhẩy đến:
nhan: lệnh;
Chú ý: Không dùng lệnh goto để nhẩy từ ngoài vào trong 1 khối lệnh, nhưng cho phép
nhẩy từ trong khối lệnh ra ngoài.
Ví dụ: Để thoát khỏi thân While, bạn có thể dùng 1 trong các câu lệnh sau:
+ Break;
+ goto nhãn;
Ví dụ:Thoát vòng lặp vô tận với break Ví dụ: Thoát vòng lặp vô tận với goto nhãn;
c = a; c = a;
while (1) while (1)
{ if (c >= b) break; { if (c >= b) goto kq;
c = c + c*0.012; c = c + c*0.012;
dem++; dem++;
} }
printf(“\nBan phai goi %d thang”,dem); kq: printf(“\nBan phai goi %d thang”,dem);
Ví dụ: Viết chương trình tính N! trong đó N là số nguyên dương nhập từ bàn phím.
main() { gt = gt * i ; /* gt *= i */
{ double gt = 1 ; if (++i > n) goto ketthuc;
unsigned int n, i = 1; }
printf(“\nTinh giai thua cua : “); ketthuc:
scanf(“%u”, &n); printf(“\n %u! = %.0lf “, n, gt );
for ( ; ; ) }
Lệnh goto kết hợp với lệnh if cũng có thể tạo thành 1 cấu trúc lặp như sau:
main() i = i + 1;
{ double gt = 1 ; if ( i > n ) goto ketthuc;
unsigned int n, i = 1; else goto laplai;
printf(“\nTinh giai thua cua : “); ketthuc: printf(“\n Ket qua %u! =
scanf(“%u”, &n); %.0lf “, n, gt );
laplai: gt = gt * i; /* hay gt *= i */ }

21
Nhận xét: Hạn chế sử dụng lệnh goto vì khó theo dõi việc thực hiện chương trình.
-2 Lệnh continue:
Công dụng: Được sử dụng trong thân các cấu trúc lặp. Dùng chuyển điều khiển giữa
chừng về đầu vòng lặp, chuẩn bị cho lần lặp mới.
Đối với for: Máy sẽ thực hiện biểu thức3, rồi thực hiện biểu thức 2, sau đó thực hiện lần
lặp mới nếu biểu thức 2 là đúng.
Đối với while và do .. while: máy sẽ chuyển tới xác định biểu thức sau while và thực
hiện lần lặp mới nếu biểu thức 2 là đúng.
Ví dụ: Viết chương trình tính trung bình cộng của n số nguyên dương nhập từ bàn phím.
Khi nào nhập số 0 thì kết thúc nhập và tính trung bình.
main() tong += so;
{ int so, tong=0, n = 0; n +=1;
float tb; } while (so != 0);
clrscr(); tb = (float) tong/n;
do printf(“\nBan da nhap vao : %d so”,n);
{ printf(“\nNhap 1 so :”); printf(“\nTrung binh cong = %f”,tb);
scanf(“%d”, &so); getch();
if (so <= 0) continue; }

22
BÀI 6: LẬP TRÌNH ĐƠN THỂ
-I LẬP TRÌNH ĐƠN THỂ:
-1 KHÁI NIỆM:
Hầu hết các chương trình máy tính giải quyết đều có kích thước khá lớn. Có thể có
những đoạn lệnh lặp đi lặp lại nhiều lần ở nhiều chổ khác nhau. Và rất khó kiểm tra, gở rối khi
có lỗi.
Do vậy cách tốt nhất là xây dựng nó theo từng đơn vị nhỏ, mỗi đơn vị thực hiện 1
công việc cơ bản nào đó. Nhờ đó dễ kiểm tra, gở rối.
Các đơn thể trong C được thực hiện dưới hình thức Hàm.
-2 PHÂN LOẠI HÀM :
Được chia làm 2 loại chính:
• Các hàm thư viện: được liệt kê trong các tập tin tiêu đề *.h chứa trong thư mục
INCLUDE: alloc.h, conio.h, dir.h, dos.h, grapichs.h, io.h, math.h, mem.h, process.h,
stdio.h, stdlib.h, string.h.
• Hàm do người dùng định nghiã
Một chương trình C có thể có nhiều hàm tự tạo, trong đó phải có 1 hàm chính (hàm
main). Thứ tự các hàm là bất kỳ nhưng bao giờ cũng thực hiện từ hàm main.
Ví dụ: Ví dụ: Viết chương trình tính tổ hợp chập k của n : Ckn = n!/(k!.(n - k)!.
//Khai báo trước các hàm dùng trong chương trình
long giaithua(int n)
long tohop(int k, int n)
main()
{
int k, n ;
printf(“\nTinh to hop chap k cua n “);
printf(“\nNhap k va n: “);
scanf(“%d %d”, &k, &n);
printf("\n gt = %ld", tohop(k,n));
getch();
}
long giaithua(int n)
{ long gt=1; int i;
if (n < 0)
{ printf("\nKhong tinh giai thua so am");
getch(); exit(1);
}
for (i=2; i<=n; i++) gt = gt*i;
return gt;
}
long tohop(int k, int n)
{ return giaithua(n)/(giaithua(k)*giaithua(n-k));
}
-IIĐỊNH NGHIÃ VÀ SỬ DỤNG HÀM TỰ TẠO:
-1 ĐỊNH NGHIÃ HÀM:
Hàm được định nghiã dựa trên cấu trúc sau:
Cấu trúc Hàm:
kiểukếtquả tênhàm(danh sách đối số)
{ Khai báo biến cục bộ
các câu lệnh
[return [biểu thức];]
}
• tênhàm: được đặt theo qui tắc đặt tên biến và phải khác nhau trong cùng 1 chương trình.
• Kiểu kết quả của hàm: Nếu hàm cần trả về 1 giá trị thì phải khai báo kiểu của giá trị đó.
Nếu hàm không trả về một giá trị thì dùng kiểu void. Nếu kiểu kết quả là kiểu int thì có thể
bỏ qua phần khai báo kiểu.
• Lệnh return: Dùng để thoát khỏi hàm và trở về nơi gọi hàm. Nếu có (biểuthức) thì giá trị
của biểu thức sẽ là giá trị của hàm.
• Danh sách đối số: là các biến cục bộ mà giá trị của chúng sẽ được cung cấp trong lời gọi
hàm. Các đối số chỉ được sử dụng bên trong thân hàm. Nếu hàm không có đối số thì thay
thế danh sách đối số bằng void.
• Tên Biến cục bộ và tham số hình thức phải khác nhau vì có cùng phạm vi sử dụng là trong
thân hàm. Nhưng có thể trùng tên với các biến bên ngoài hàm.
• Thân hàm: được bắt đầu bằng dấu { và kết thúc bằng dấu }.
-2 GỌI HÀM:
Hàm có thể được gọi thực hiện trong các biểu thức hoặc có thể thực hiện như một lệnh
độc lập. tên_hàm([danh sách tham số])
Danh sách tham số theo thứ tự phải phù hợp với kiểu của đối số.
-3 TRUYỀN THAM SỐ CHO HÀM:
Việc khai báo các đối số cho hàm nhầm mục đích tính toán, thao tác trên nhiều dữ liệu
khác nhau
Ví dụ: long giaithua(int n): dùng tính giai thừa cho nhiều giá trị n khác nhau
Các đối số sẽ nhận các giá trị từ các tham số khi hàm được gọi thực hiện. Có 2 cơ chế
truyền tham số cho hàm.
a) Truyền tham trị:
Đối số được cấp phát vùng nhớ riêng để chứa giá trị của tham số tương ứng. Việc thay
đổi giá trị của đối số không làm thay đổi giá trị của tham số.
void Thu(int a, int b)
{ int c;
a = 5; b = 6; c = 7; d = 8;
printf(“Trong ham thu: %d %d %d %d”, a, b, c, d);
}
main()
{ int a = 1, b = 2, c = 3, d = 4;
printf(“ Truoc khi goi ham thu: %d %d %d %d”, a, b, c, d);
Thu(a,b);
printf(“Sau khi goi ham thu: %d %d %d %d”, a, b, c, d);
}
b) Truyền Tham chiếu:
Đối số thực chất là một tên mới của tham số (còn gọi là bí danh). Do đó, việc thay đổi
giá trị của đối số sẽ ảnh hưởng trực tiếp giá trị của tham số.
Ví dụ: void Thu(int a, int &b)
Ví dụ:
int a, b, c;
void Thu(int d, int &e, int c)
{ int a;
a =c+1; e = a+c; c = c*2;
printf(“Trong ham thu: %d %d %d %d %d”, a, b, c, d, e);
}
main()
{ a =2; b = 4; c = 6;
printf(“ Truoc khi goi ham Thu: %d %d %d ”, a, b, c);
Thu(b, c, a);
printf(“ Sau khi goi ham Thu: %d %d %d ”, a, b, c);
}
Ví dụ Viết chương trình giản ước 1 phân số, có sử dụng 1 hàm tính ước số chung lớn nhất của
2 số.
void nhap_phan_so(int &, int &);
void toigianps(int &, int &);
int ucln(int , int );
main()
{
int a, b;
NhapPhanSo( a, b);
toigianps(a,b);
printf(“\nSau khi giản ước: %d/%d”,a,b);
}
/*-------------------------------*/
int ucln(int a, int b)
{
int m,n,r;
m = abs(a); n = abs(b);
while (n != 0)
{ r = m%n; m = n; n = r ; }
return m;
}
void toigianps(int &tu, int &mau)
{
long int usc;
usc = ucln(tu,mau);
tu = tu/usc;
mau = mau/usc;
}
/*=========================*/
void nhap_phan_so(int &x, int &y)
{
do{
printf("\nNhap phan so (x/y) : ");
scanf("%d %*c %d", &x, &y); fflush(stdin);
if ( y == 0) printf("\nMau so bang 0");
else
if (x < 0 && y < 0)
{ x = abs(x);
y = abs(y);
}
else
if (x*y<0)
{ x = -abs(x);
y = abs(y);
}
} while (y == 0);
}
-IIITÍNH ĐỆ QUI CỦA HÀM:
-1 Khái niệm:
Một đối tượng X gọi là được định nghiã đệ qui nếu trong định nghiã của X có sử dụng
trực tiếp hay gián tiếp ngay chính khái niệm X.
Ví dụ: Định nghiã phép toán giai thừa
(a) 0! = 1! = 1
(b) nếu n > 1 thì n! = n*(n-1)!
Ví dụ: Định nghiã UCLN của 2 số x và y :
(a)UCLN(x,y) = x nếu y = 0
(b)UCLN(x,y) = UCLN(y, phần dư của x/y) nếu y<>0
* Nhận xét: Một định nghiã đệ qui phải có 2 thành phần:
- Thành phần dừng (Không chứa khái niệm đang định nghiã)
Ví dụ: 0! = 1! = 1
- Thành phần đệ qui (có chứa khái niệm đang định nghiã)
ví dụ: n! = n*(n-1)! nếu n > 1
Hàm đệ qui: Một hàm là một sự định nghiã về một công việc nào đó, nên cũng có thể là
đệ qui:
Nếu trong CTC P có lời gọi tường minh đến chính nó thì gọi là đệ qui trực tiếp
Nếu trong P có gọi ctc Q, và trong Q lại có lời gọi P thì P được gọi là đệ qui gián
tiếp.
Khi hàm gọi đệ qui đến chính nó thì mỗi lần gọi, máy sẽ tạo ra một tập biến cục bộ mới
hoàn toàn độc lập với tập biến cục bộ của hàm trước đó.
Cũng như các lệnh lặp, các thủ tục đệ qui cũng có thể thực hiện các tính toán không
dừng, vì thế cần chú ý vấn đề kết thúc.
Ví dụ: Xây dựng hàm tính n! theo đệ qui.
long gt(int n)
{
if (n <= 1) return 1;
else return (n * gt(n-1));
}
Ví dụ: Tính UCLN(x,y) theo thuật toán Euclide
int UCLN(int x, int y)
{
if (y == 0) return x;
else return (ucln(y, x % y));
}
Ví dụ: Dãy số Fibonacci được xác định như sau: F0=1; F1=1; Fn=Fn-1+Fn-2 với n>=2.
Hãy xác định phần tử thứ n của của dãy số.
int Fibo(int n)
{ if (n == 0 || (n == 1) return 1;
else return (fibo(n - 1) + fibo(n - 2));
}
Ví dụ : Bài toán Tháp Hà Nội (Tower of HaNoi)
Có 3 cột A, B, C và 64 điã kích thước khác nhau ở cột A, điã lớn ở dưới, điã nhỏ ở trên.
Hãy chuyển các điã từ A sang C sao cho :
- mỗi lần chỉ được chuyển 1 điã
- ở các cột tình trạng điã luôn luôn là điã lớn ở dưới, điã nhỏ ở trên.
Chúng ta giải bài toán theo kiểu giải quyết từ trên xuống (Top Down) như sau:
Để chuyển n điã từ A ---> C: làm cách nào đó ta chuyển n-1 điã từ A -->B, sau đó
chuyển điã lớn nhất còn lại từ A ---> C. Và rồi lại làm cách nào đó chuyển n-1 điã từ B --> C
Thuật toán: Chuyển n điã từ A sang C, lấy B làm trung gian
Bắt đầu:
Nếu n > 0
- chuyển n-1 điã từ A sang B, lấy C làm trung gian
- chuyển 1 điã từ A sang C
- chuyển n-1 điã từ B sang C, lấy A làm trung gian
/*Program ThapHaNoi;*/
typedef char cot; {có 3 cột ‘A’, ‘B’, ‘C’}
char n;
void main()
{
printf(“Nhap so đia: “);
scanf(“%d”, &n);
Doicot(n,’A’,’B,’C’);
getch();
}
void Doicot(char n ,cot A, cot B, cot C )
{ if ( n > 0 )
{
doicot(n-1,A,C,B);
printf(“\n chuyen 1 dia tu %c sang %c”, A,C);
doicot(n-1,B,A,C);
}
}
Nhận xét: Số lần chuyển điã : 20 + 21 + ... + 2n-1 = 2n -1
Nếu 1 lần chuyển 1 điã mất 1/100 giây thì cần đến 24 tỷ năm
Ví dụ đệ qui gián tiếp: Một con ếch từ đáy giếng leo lên miệng giếng. cứ leo lên 3m thì tụt lại
1 m. Đếm số lần tiến, số lần lùi khi ếch lên được mặt giếng.
int z, t, l ;
void Lui(int &x);
void Tien(int &y)
{ if ( y > 0)
{
y = y - 3;
t = t + 1;
Lui( y );
}
}
void Lui(int &x)
{ if ( x > 0 )
{ x = x + 1;
l = l + 1;
Tien( x);
}
}
main()
{ t = 0; l = 0; z = 5; Tien( z );
printf(“\nso lan tien: %d”,t);
printf(“\nso lan lui: %d”, l );
}
BÀI 7 : QUẢN LÝ THIẾT BỊ
-I QUẢN LÝ MÀN HÌNH VÀ CỬA SỔ:
Trong phần này giới thiệu một số hàm dùng thay đổi màu sắc, di chuyển con trỏ, và
tạo cửa sổ nhập xuất văn bản đã được khai báo trong CONIO.H
-1 Chọn Kiểu Màn Hình Văn Bản:
Mặc định, màn hình gồm 80 cột (đánh số từ 1 đến 80), 25 dòng (đánh số từ 1 đến
25). Để thay đổi số cột và số màu cần hiển thị ta dùng hàm chọn kiểu màn hình như sau:
void textmode(int mode);
Biến mode có thể chứa các giá trị sau:
Tên hằng Giá trị Kiểu màn hình
LASTMODE 1 kiểu màn hình trước đó
BW40 0 đen trắng 40 cột
C40 1 16 màu, 40 cột
BW80 2 đen trắng 80 cột
C80 3 16 màu, 80 cột
MONO 7 đơn sắc, 80 cột.
Ví dụ: textmode(C80) --> định kiểu màn hình văn bản là 16 màu, 80 cột, 25 dòng.
textmode(C40) --> định kiểu màn hình văn bản là 16 màu, 40 cột, 25 dòng, chữ rộng
gấp đôi chế độ 80.
-2 Tạo Cửa Sổ Văn Bản:
void window(int xt, int yt, int xd, int yd);
-3 Quản Lý Con Trỏ Màn Hình:
- gotoxy(X, Y) : Di chuyển con trỏ màn hình đến vị trí (X,Y). (1..80, 1..25)
- int wherex(void) : Trả về vị trí cột hiện hành.
- int wherey(void) : Trả về vị trí cột hiện hành.
Ví dụ:
void WriteXY(int x,y, char ch)
{ int x1, y1;
x1 = wherex(); y1 = wherey();
gotoxy(x,y);
cprintf(“%d”,ch);
gotoxy(x1, y1);
}
-4 Đặt Màu Nền Và Màu Chữ:
a. Các Hằng Định Màu:
BLACK (0); BLUE(1); GREEN(2); CYAN(3); RED(4); MAGENTA(5);
BROW(6); LIGHTGRAY(7); DARKGRAY(8); LIGHTBLUE(09);
LIGHTGREEN(10); LIGHTCYAN(11); LIGHTRED(12); LIGHTMAGENTA(13);
YELLOW(14); WHITE(15); BLINK(128): Nhấp nháy.
b. void textcolor(int Newcolor): Đặt màu văn bản màn hình.
Ví dụ: textcolor(RED + BLINK)
c. void textbackground(int Newcolor): Đặt màu nền màn hình.
d. void textattr(int Newcolor): Thay đổi nhanh màu chữ màu nền. từ bít 0-3 (0-15) màu
chữ, bít 4-6 (16<= ...<128) màu nền, bit 7 nhấp nháy.
TEXTATTR(<MÀU CHỮ>+MÀU NỀN*16 );
-5 Các Hàm Xóa Dữ Liệu Trên Màn Hình Hoặc Cửa Sổ:
• clrscr(): xóa dữ liệu trên màn hình và cửa sổ theo màu nền hiện hành.
• clreol(): Xóa đến cuối dòng
• delline(): Xóa dòng hiện hành.
• void gettextinfo(struct text_info *r);
Dùng lưu kiểu hiển thị văn bản hiện tại vào biến cấu trúc kiểu text_info.
r là con trỏ tới biến cấu trúc kiểu text_info đã được định nghiã trong conio.h như
sau:
struct text_info
{ unsigned char winleft, wintop, winright, winbottom;
unsigned char attribute, normattr;
unsigned char currmode;
unsigned char screenheight, screenwidth;
unsigned char curx, cury;
}
Ví dụ: struct text_info mh;
gettextinfo(&mh); /* Lưu kiểu màn hình hiện hành*/
textbackground(mh.attribute % 16); /*Phục hồi màu nền*/
textcolor(mh.attribute / 16); /*phục hồi màu chữ*/
-6 Nhập Xuất Dữ Liệu Theo Màu Chỉ Định:
a. Nhập Dữ Liệu:
• int cscanf(“chuổi điều khiển”, danh sách địa chỉ các biến);
• int getch(void);
Nhận 1 ký tự từ bộ đệm bàn phím, không cho hiện trên màn hình. Nếu bộ đệm rỗng
thì chờ ấn 1 phím bất kỳ.
Nếu phím được ấn là phím chức năng thì trả về ký tự ‘\0’ và ghi vào buffer bàn phím
1 ký tự đại diện.
Ví dụ : Home: #71(G); Up: 72(H); Down: 80(P); Left: 75(K); Right: 77(M)...
Ví dụ: Trình bày Mã ASCII của phím chức năng và ký tự tượng trưng của phím.
Program Kiem_tra_phim;
#include <CONIO.H>
char ch;
main()
{ printf(“\nKiem tra phim, Nhan ESC để kết thúc”);
do { printf(“\nBan hay an một phim hay tổ hợp phím: ”);
ch =getch();
if (ch == 0) then
{ ch = getch();
printf(“La Phim chuc nang có mã: %d cua ky tu %c”, ch, ch);
} else printf(“\nPhim vua an co ma: %d”, ch );
} while (ch != 27);
}
• int getche(void); nhận 1 ký tự từ bộ đệm bàn phím , cho hiện trên màn hình
Ví dụ: ch = getche()
• int kbhit(void): cho biết số ký tự hiện có trong bộ đệm bàn phím. Bằng 0 nếu bộ đệm
rỗng.
Chú ý: Bạn có thể dùng kbhit() và getch() để xóa bộ đệm bàn phím.
/* Nếu bạn có bấm 1 số phím khi máy đang reo thì các ký tự đó sẽ hiện ra khi kết
thúc reo */
#include “conio.h”
30
void main()
{ int n,ch;
clrscr();
while (n > 0){ --n; putch(7); }
while (kbhit()) { ch = getch(); putch(ch);
}
b. Xuất dữ liệu ra bộ nhớ màn hình:
• int cprintf(“chuổi điều khiển”, danh sách biểu thức);
• int putch(int ch);
Xuất ký tự ra cửa sổ văn bản màn hình theo màu đã xác định bởi hàm textcolor (các
hàm trên stdout như printf, putchar luôn hiển thị ký tự theo màu trắng). Hàm trả về ký tự
được hiển thị.
#include <stdio.h> #include <conio.h>
#define Len 20
char cn[3][Len] = { "Menu 1", "Menu 2", "Thoat"};
int scn = 3;
void SetItem(int x1,int y1,int x2,int y2,char Caption[],int color)
{ struct text_info m;
gettextinfo(&m);
window(x1,y1,x2,y2); textattr(color); clrscr();
cprintf("%s",Caption); textattr(m.attribute);
}
void ActiMenu(int x, int y, char cn[3][Len],int scn, int &chon)
{ int i; char phim; struct text_info m;
gettextinfo(&m);
for (i=0; i<scn; i++)
SetItem(x,y+i,x+Len,y+i,cn[i],BLUE+16*YELLOW);
do{ SetItem(x,y+chon,x+Len,y+chon,cn[chon],WHITE+16*GREEN);
phim=getch();
if (phim == 0) phim= getch();
SetItem(x,y+chon,x+Len,y+chon,cn[chon],BLUE+16*YELLOW);
switch (phim)
{ case 72:if (chon==0) chon = scn-1;
else chon--; break;
case 80:if (chon==scn-1) chon = 0; else chon++;
}
}while (phim != 13 && phim != 27);
if (phim==27) chon=scn-1;
window(m.winleft,m.wintop,m.winright,m.winbottom);
textattr(m.attribute);
clrscr();
}
void main()
{ int chon;
textattr(WHITE+16*BLACK); clrscr();
chon=0;
ActiMenu(1,3,cn,scn,chon);
printf("chon = %d",chon);

31
getch();
}
-IITẠO SỐ NGẪU NHIÊN:
Được khai báo trong stdlib.h và kết hợp với Time.h
1. void randomize(void); Khởi động bộ tạo số ngẫu nhiên
2. int random(int n); Cho 1 giá trị ngẫu nhiên từ 0 đến n-1
3. void srand(unsigned seed); Khởi động bộ số tạo số ngẩu nhiên cho hàm rand(). Nhưng
cho phép trả về cùng số ngẫu nhiên với cùng khởi điểm seed.
4. int rand(void); Cho 1 giá trị ngẫu nhiên từ 0 đến 32767.
#include <stdlib.h> #include <stdio.h> #include <time.h>
void main(void)
{ int i;
time_t t; //srand((unsigned) time(&t));
printf("Ten random numbers from 0 to 99\n\n");
for(i=0; i<10; i++)
{ srand(i%4);
printf("%d\n", rand() % 100);
}
}
-IIIGIỜ HỆ THỐNG: <Dos.h>
1. Kiểu time: struct time {
unsigned char ti_hour, ti_min, ti_sec, ti_hunt;
};
2. Lấy giờ hệ thống: void gettime(struct time *t );
3. Đặt giờ hệ thống: void settime(struct time *t );
Ví dụ: Chương trình luyện tập bàn phím: Hiển thị ngẫu nhiên 1 ký tự, người chơi phải gỏ
ngay ký tự đó, nếu quá thời gian qui định thì tăng điểm lỗi.
long ptramgiay()
{
struct time t;
gettime(&t);
return (((t.ti_hour*3600)+ t.ti_mic*3600)+t.ti_sec)*100+t.ti_hunt;
void main()
{
int i, soloi;
char ch, ktgo;
for (i=1; i<= sokytu; i++)
{ ch = random(26)+65;
gotoxy(20+i, dong); putchar(ch);
tgbd = ptramgiay();
while (1)
{ tghh =ptramgiay();
if (tghh-tgbd>tgcho)
{ soloi++; gotoxy(20+i, dong); putchar(' '); break; }
if (kbhit())
{ ktgo = toupper(getch());
gotoxy(20+i, dong+1); putchar(' ');
if (ktgo != ch)
32
{ soloi++; gotoxy(20+i, dong+2); putchar(ch); }
break;
}
}
}
gotoxy(20, 15);
printf("So Loi : %d", soloi);
}
-IVÂM THANH: <DOS.H>
1. sound(tần số: word): Phát âm thanh theo tần số xấp xỉ.
2. nosound() : Tắt bộ phát âm;
3. void delay(int n): dừng thực hiện chương trình n mili giây
4. void sleep(int n); Tạm dừng trong n giây
Các tần số sau tương ứng với cao độ của các nốt nhạc trong bát độ trung bình, muốn lấy
bát độ thấp hơn thì chia 2, cao hơn thì nhân 2:
DO=523;DO#=554;Re=587;Re#=622;Mi=659;Fa=698;Sol=784;Sol#=831;La=880;
La#=932;Si=988
Ví dụ: /*Program CON_CO;*/
#include "dos.h"
#include "stdio.h" #include "conio.h" #include "stdlib.h" #include “string.h”
const int do1=262,re1=294,mi1=330,fa1=349,sol1=392,la1=440,si1=494,
do2=523,re2=587,mi2=659,fa2=698,sol2=784,la2=880,si2=988,
m3=1,m2=2,m1=4,m1c=6,d=8,dc=12,t=16,tr=32;
int tempo;
void vekhungdon(int x1, int y1,int x2,int y2,int color)
{ int i;
textattr(color); gotoxy(x1,y1); cprintf("%c",218);
for (i=x1+1;i<=x2-1;i++)
{ gotoxy(i,y1); cprintf("%c",196); gotoxy(i,y2); cprintf("%c",196);
}
gotoxy(x2,y1); cprintf("%c",191);
for (i=y1+1;i<=y2-1;i++)
{ gotoxy(x1,i); cprintf("%c",179); gotoxy(x2,i); cprintf("%c",179);
}
gotoxy(x1,y2); cprintf("%c",192); gotoxy(x2,y2); cprintf("%c",217);
}
void note(int tanso, int tg,char *loi)
{ if (strlen(loi)!=0) cprintf("%s ",loi);
sound(tanso);delay(tg*tempo); nosound();
}
void main(void)
{ textbackground(BLACK); clrscr(); vekhungdon(20,5,60,20,WHITE+GREEN*16);
randomize(); tempo=2200; window(21,6,59,19);
textbackground(BLUE); clrscr();
note(do2,m1c,"con"); note(re2,m2,"");
note(sol1,d,"co"); note(sol1,m1,"co");
note(do2,m1,"bay"); note(sol1,m1,"la");
note(do1,m1,""); note(sol1,m2,"la");

33
note(do2,m2,""); note(la1,m2,"bay");
note(sol1,m2,""); note(la1,dc,"la");
note(la1,m2,"bay"); note(sol1,m2,"");
note(fa1,d,"tu"); note(fa1,m1,"tu");
note(fa1,m2,"cua"); note(sol1,m2,"");
note(do2,dc,"phu"); note(la1,m2,"bay");
note(sol1,m2,""); note(la1,d,"ra");
note(la1,m1,"ra"); note(la1,m1,"canh");
note(fa1,d,"dong"); note(fa1,m1,"tinh");
note(la1,m2,"tinh"); note(do2,m2,"");
note(la1,d,"tang"); note(la1,m1,"tang");
note(la1,m2,"tinh"); note(do2,m2,"");
note(fa1,d,"tinh");
}

34
BÀI 8: KIỂU MẢNG (ARRAY)
-I KHÁI NIỆM:
Mảng là 1 tập hợp nhiều biến nhớ có cùng kiểu, có cùng tên gọi và được cấp phát
những vị trí liên tục trong bộ nhớ.
Mỗi phần tử trong mảng được xác định dựa trên tên mảng và chỉ số xác định vị trí
của phần tử trong mảng. Số lượng chỉ số dùng để xác định 1 phần tử trong mảng được gọi
là số chiều của mảng.
Việc khai báo một mảng cần định rỏ:
• Kiểu cơ sở của các phần tử trong mảng
• Tên mảng
• Số chiều và kích thước mỗi chiều đặt trong ngoặc vuông [ ]. Số phần tử trong
mảng bằng tích kích thước các chiều.
Ví dụ: int a[5];
Khai báo mảng 1 chiều tên là a có 5 phần tử, Mỗi phần tử a[i] là một
biến kiểu int và được xác định dựa trên 1 chỉ số như sau: a[0], a[1], a[2], a[3],
a[4].
Ví dụ: int b[2][3];
Khai báo mảng 2 chiều tên là b, có 2*3 = 6 phần tử, Mỗi phần tử là
một biến kiểu nguyên và được xác định dựa trên 2 chỉ số dòng và cột như
sau::
b[0][0] b[0][1] b[0][2]
b[1][0] b[1][1] b[1][2]
Trong đó, thường sử dụng là mảng 1 chiều và mảng 2 chiều.
-IIMẢNG MỘT CHIỀU:
Dùng biểu diễn 1 danh sách giá trị cùng kiểu.
-1 Khai Báo Biến Mảng 1 Chiều:
<kiểucơsở> <tênmảng>[<số phần tử>];
Ví dụ: int a[5];
a là 1 biến mảng; còn a[0],a[1],..,a[4] là các biến số nguyên.
• Có thể sử dụng hằng biến để xác định kích thước mảng:
Ví dụ: #define MAX 100
float a[MAX];
const int MAX = 100;
float b[MAX];
• Có thể gán giá trị khởi đầu cho các phần tử của mảng trong khi khai báo:
<kiểucơsở> <tênmảng>[<số phần tử>] = {gt1, .., gtn} ;
Ví dụ: int a[5] = {1, 3, 4 }
• Nếu số giá trị khởi đầu < số phần tử thì các phần tử còn lại sẽ được gán giá trị khởi đầu
= 0.
Ví dụ: gán giá trị khởi đầu cho 5 phần tử của mảng a là: int a[5] = {0};
• Có thể không chỉ định số phần tử của mảng : <kiểucơsở> <tênmảng>[] = {gt1, .., gtn} ;
Khi đó số phần tử của mảng sẽ là số gía trị khởi đầu trong khai báo mảng.
Ví dụ: int a[] = {1, 3, 4 }; Mảng a có 3 phần tử.
-2 Khai Báo Kiểu Mảng:
typedef <kiểucơsở> tênkiểu[<số phần tử>];

35
Ví dụ: Khai báo kiểu mảng 1 chiều có 10 phần tử mỗi phần tử là 1 biến kiểu số
nguyên
typedef int Dayint[10];
Dayint a;
-3 Một Số Thao Tác Trên Mảng 1 Chiều:
.3.1 Truy xuất trực tiếp đến từng phần tử của biến mảng như 1 biến thuộc kiểu cơ sở:
tênmảng[<chỉsố>]
Ví dụ: ch[1] = ‘A’; x[6] = (x[4]+x[5])/2;
scanf(“%d”, &a[2]);
printf(“a[%d] = %d”, i, a[i]);
Chú ý: Không thể nhập xuất trực tiếp biến mảng.
.3.2 Nhập giá trị cho các phần tử của mảng:
Ý tưởng: Sử dụng 1 vòng lặp thay đổi chỉ số, để nhập dữ liệu cho từng phần tử theo chỉ
số.
Ví dụ: Viết chương trình nhập N số nguyên (N <= 100). Sau đó tính tổng của các số
nguyên lẻ .
.3.3 Xuất giá trị các phần tử :
Ý tưởng: Sử dụng 1 vòng lặp chạy theo chỉ số và xuất phần tử theo chỉ số đó.
for (i = 0; i < n ; i++) printf(“%8d“, a[i]) ;
.3.4 Tìm kiếm một phần tử theo yêu cầu:
Ý tưởng: Lần lượt kiểm tra từng phần tử, bắt đầu từ phần tử đầu mảng.
.3.4.1Tìm kiếm phần tử chứa giá trị cần tìm:
Ý tưởng: Khởi đầu từ phần tử đầu mảng. Lần lượt so sánh với phần tử X. Quá trình tìm
kiếm kết thúc khi tìm thấy hoặc khi hết mảng.
Ví dụ: Tìm trong mảng có N số nguyên phần tử đầu tiên có giá trị là X.
.3.4.2Tìm kiếm phần tử thỏa yêu cầu cực trị:
Ý tưởng: Sử dụng các biến để lưu giữ vị trí phần tử cực trị trong quá trình so sánh.
Ví dụ: Nhập vào nhiệt độ của mỗi ngày trong tuần, từ chủ nhật đến thứ 7. Hãy cho biết
ngày nào nóng nhất.
int Thu[7];
int i, ndmax, ngaynong;
void main(void)
{ printf(“\nNhap nhiet do 7 ngay trong tuần: \n“);
for (i=0; i < 7; i++) {
printf(“Nhap nhiêt do ngay %d : “, i); scanf(“%d”, &Thu[i]);
}
ndmax = Thu[0]; ngaynong = 0;
for (i=1; i < 7; i++)
if (ndmax <Thu[i]) { ndnong = Thu[i]; ngaynong = i; }
printf(“\nNgay nong nhat la ngay %d “, ngaynong +1);
}
.3.5 Sắp xếp một mảng theo thứ tự tăng ( hay giảm) dần:
Thuật toán chọn trực tiếp (select sort)
Ý tưởng: - Tìm phần tử nhỏ nhất trong mảng (0 .. n-1) ta đổi chổ với pt[1]
- Sau đó, lại tìm phần tử nhỏ nhất trong dãy từ (1 .. n-1) ta đổi chổ với
pt[2]
36
- Tiếp tục cho tới khi mảng con chỉ còn 1 phần tử.
void selectsort(kiểu A[ ]; int n);
{ int i, j, vtmin;
for ( i =1; i <= n-1; i++)
{ vtmin = i;
for ( j = i +1; j <= n; j++)
if (a[ j ] < a[vtmin]) min = j ;
Hoanvi(a[vtmin],a[i]);
}
}
.3.6 Chèn một phần tử vào mảng:
Ví dụ: Chèn 1 giá trị vào mảng đã sắp thứ tự.
.3.7 Xóa một phần tử trong mảng:
Ý tưởng: Dồn các phần tử sang trái và giảm số phần tử.
-4 Truyền tham số Mảng một chiều:
Việc truyền tham số là mảng cho hàm luôn theo nguyên tắc truyền địa chỉ. Khi gọi
hàm và thiết kế tham số là mảng 1 chiều cho hàm cần tuân theo nguyên tắc:
Đối số là mảng :
Tên biến mảng: <kiểuhàm> tênhàm( kiểucơsở tênđốisốmảng[ ], ...)
Chú ý: Không cần khai báo kích thước đối số mảng, tuy nhiên nếu có khai báo trình
biên dịch cũng sẽ bỏ quan
Khai báo đặc trưng của hàm có thể là:
<kiểuhàm> tênhàm( kiểu tênmảng[ ], ...);
<kiểuhàm> tênhàm( kiểu [ ], ...);
Gọi hàm và truyền Tham số kiểu mảng: phải là tên mảng, vì tên mảng là hằng địa
chỉ của biến mảng: tênhàm( tênmảng, ...)
Ví dụ: Viết chương trình tính tổng 2 vecto có tối đa N phần tử (N ( 100).
#include <stdio.h>
#define MAX 100;
void NhapVecto( char; int [ ], int );
void InVecto(char, int [ ], int);
void TongVecto(int [ ], int [ ], int [ ], int);
void main()
{
int A[MAX], B[MAX], C[MAX], n;
printf(“\nNhap so chieu : “); scanf(“%d”, &n);
NhapVecto(‘A’, a, n); NhapVecto(‘B’, b, n);
TongVecto(a,b,c, n); InVecto(‘C’, c , n);
}
void NhapVecto( char tenvecto, int x[ ], int sc)
{ int i;
printf(“\n Nhập tọa độ vecto %d chiều : \n“, sc);
for (i = 0; i < sc; i++)
{ printf(“\nTọa độ thứ %d : “, i + 1);
scanf(“%d”, &x[ i ]);}
}
}
void InVecto( char tenvecto, int x[ ], int sc)
37
{ int i;
printf(“\n Tọa độ vecto %c : “, tenvecto);
for (i = 0; i < sc; i++) printf(“%6d“, &x[ i ]);
}
void TongVecto(int x[], int y[], int z[], int sc)
{ int i;
for (i = 0; i < sc; i++) z[ i ] = x[ i ] + y[ i ];
}
-IIIMẢNG HAI CHIỀU:
-1 KHÁI NIỆM:
Mảng 2 chiều là mảng 1 chiều mà mỗi phần tử lại là mảng 1 chiều.
-2 KHAI BÁO VÀ SỬ DỤNG MẢNG HAI CHIỀU:
<kiểu phần tử> tênbiếnmảng[số dòng tối đa][số cột tối đa];
Ví dụ: int a[3][4];
• Gán giá trị ban đầu khi khai báo:
Ví dụ: float x[3][4] = {{3,9,4,6}, {4,0,7,2}, {6,4,8,2} };
x có 3 phần tử x[0],..,x[2] mà mỗi phần tử x[i] là một mảng 1 chiều có 4 phần
tử
x[0] là mảng có các phần tử x[0][1],.., x[0][3] là các số thực.
• Có thể khai báo không chỉ định tổng số dòng: Khi đó tổng số dòng phụ thuộc vào
số dòng được gán.
Kiểu tênmảng[ ] [tổng số cột]= {giá trị gán};
-3 THAO TÁC TRÊN MẢNG 2 CHIỀU:
.3.1 Nhập xuất giá trị của 1 phần tử trên mảng 2 chiều, dùng công thức:
<tên biến mảng>[<chỉ số dòng>][<chỉ số cột>]
Ví dụ: x[1][3] = 9.5;
.3.2 Xuất giá trị của các phần tử của mảng 2 chiều:
for ( i = 0; i < sodong; i++)
{
for (j = 0; j < socot; j++) printf(“%8d”, x[ i ][ j ]);
puts(“”);
}
.3.3 Nhập giá trị cho các phần tử của mảng 2 chiều:
.3.3.1Bằng hàm random(100)
for ( i = 0; i < m; i++)
for (j = 0; j < n; j++) x[ i ][ j ] = random(100);
Ví dụ: Cho một ma trận số nguyên gồm M dòng N cột (M,N<=100).
a) Hãy cho biết cột nào có tổng các số hạng trên cột là lớn nhất.
b) Dòng nào có tổng các số hạng trên dòng là nhỏ nhất.
.3.3.2Nhập giá trị cho các phần tử của mảng 2 chiều Bằng hàm scanf:
for ( i = 0; i < m; i++)
for (j = 0; j < n; j++)
{ printf(“\na[%d][%d] = “, i, j);
scanf(“%d”, &x[ i ][ j ]);
}

38
Ví dụ: Viết chương trình nhân ma trận A (NxL) và B (LxM) : C (NxM) = A*B
#include <stdio.h>
#define MAX 10
main()
{
float a[MAX][MAX], b[MAX][MAX], c[MAX][MAX], x;
int n=2,l=3,m=3;
int i, j;
printf("\nNhap gia tri cho cac phan tu cua mang A: \n");
for (i=0; i< n; i++)
for (j=0; j < l; j++)
{
printf("\tA[%d][%d] : ",i+1,j+1);
scanf("%f", &x);
a[i][j] = x ;
}
printf("\nNhap gia tri cho cac phan tu cua mang A: \n");
for (i=0; i< l; i++)
for (j=0; j < m; j++)
{ printf("\tA[%d][%d] : ",i+1,j+1);
scanf("%f", &x);
b[i][j] = x;
}
for (i=0; i<n; i++)
for (j=0; j<m; j++)
{ c[i][j] = 0;
for (k=0; k < l; k++) c[i][j] +=a[i][k]* b[k][j];
}
for (i=0; i< sd; i++)
{ for (j=0; j < sc; j++) printf("%5.0f", c[i][j]);
puts("");
}
getch();
}
-4 TRUYỀN THAM SỐ TRÊN MẢNG 2 CHIỀU:
• Khai báo đối số là mảng 2 chiều:
<kiểuhàm> tênhàm( kiểu tênmảng[ ][số cột], ...)
• Gọi hàm và truyền tham số kiểu mảng hai chiều:
tênhàm( tênmảng, ...)
-IVMảng nhiều chiều:
-1 KHÁI NIỆM:
Mảng N chiều là mảng N -1 chiều mà mỗi phần tử là mảng một chiều.
-2 KHAI BÁO MẢNG:
<kiểu phần tử> tênbiếnmảng[kt1][kt2]..[ktN];
Ví dụ: float y[2][3][4];
• y là mảng 3 chiều.
• y[i] là mảng 2 chiều.
• y[i][j] là mảng 1 chiều có 4 phần tử kiểu số thực.
39
• y[i][j][k] là 1 số thực.
-3 TRUY XUẤT PHẦN TỬ MẢNG:
<tên biến mảng>[<cs1>][<cs2>]..[<csN]
-4 Thao tác trên mảng N chiều:
Các thao tác trên mảng N chiều cần có N vòng lặp lồng nhau:
for (cs1 = 0; cs1 < kt1 ; cs1++)
for (cs2 = 0; cs2 < kt2; cs2++)
.........
for (csN = 0; csN < ktN; csN++)
<Xử lý phần tử [<cs1>][<cs2>]..[<csN]>;

40
BÀI 9: KỸ THUẬT LẬP TRÌNH VỚI CON TRỎ
-I KHÁI NIỆM VỀ BIẾN CON TRỎ:
-1 Khái niệm:
Biến con trỏ có kiểu cơ sở T là loại biến dùng lưu trữ địa chỉ vùng nhớ của một biến
khác thuộc một kiểu T.
-2 Khai báo biến con trỏ:
<Kiểucơsở> * <Tênbiếncontrỏ>;
Ví dụ: float *pf, p;
int *pi, p;
char *pc, p;
• <Kiểu cơ sở> có thể là kiểu vô hướng hoặc kiểu có cấu trúc bất kỳ hoặc kiểu void.
Đặc biệt: Kiểu con trỏ void: void *Ptr;
Con trỏ kiểu void được gọi là con trỏ không định kiểu và cho phép chứa địa chỉ của
mọi biến thuôc kiểu dữ liệu bất kỳ.
• Biến con trỏ có thể nhận giá trị 0, NULL hay một địa chỉ bộ nhớ. Một biến con trỏ
có giá trị 0 hay NULL hàm ý không trỏ đến vùng nhớ nào cả.
• Sau khi khai báo, biến con trỏ chứa giá trị ngẫu nhiên. Có thể khởi gán giá trị của
biến con trỏ khi khai báo hoặc sử dụng câu lệnh gán tường minh.
-IICÁC THAO TÁC TRÊN BIẾN CON TRỎ:
-1 Gán địa chỉ vùng nhớ cho biến con trỏ:
• Gán địa chỉ của một biến kiểu cơ sở cho biến con trỏ:
<biếncontrỏ> = &<tênbiếnkiểuT>;
Ví dụ: int y, *yptr, *xptr;
y = 5; yptr = &y;
• Gán nội dung của một con trỏ khác cho biến con trỏ:
<biếncontrỏ 1> = <biếncontrỏ 2>
• Nếu hai con trỏ khác kiểu thì khi gán phải ép kiểu của <biến con trỏ 2> theo kiểu
của <biến con trỏ 1>: <biếncontrỏ 1> = (<kiểucơsở 1> * ) <biếncontrỏ 2>;
Ví dụ: char *zPtr;
zPtr = (char*) yPtr;
-2 Nhập xuất nội dung vùng nhớ thông qua con trỏ:
Sử dụng toán tử một ngôi * để lấy nội dung vùng nhớ mà con trỏ trỏ đến:
* <biếncontrỏ>
Ví dụ: printf(“Giá trị của biến y là %d “, *yPtr ); ----> Giá trị của biến y là 5
*yPtr = 8; //Gán giá trị cho biến y thông quan biến con trỏ yPtr
• Nhập dữ liệu vào vùng nhớ của biến y thông qua biến con trỏ:
scanf(“%d”, yPtr);
-3 Phép so sánh con trỏ:
Có thể so sánh địa chỉ vùng nhớ lưu trong các biến con trỏ bằng các phép so
sánh
Ví dụ: Để kiểm tra 2 biến con trỏ có chứa cùng địa chỉ vùng nhớ hay không:
yPtr == xPtr
Ví dụ: So sánh biến con trỏ với giá trị NULL: yPtr == NULL
-IIICON TRỎ VÀ MẢNG:
Ví dụ: int A[8];
int * Aptr;

41
3000 3002 3004 3006 3008 3010
A[0] A[1] A[2] A[3] A[4] A[5]
Trong C, Tên mảng được xem là một con trỏ hằng, chứa địa chỉ phần tử đầu tiên của
mảng nên có thể :
-1 Gán địa chỉ đầu mảng cho biến con trỏ bằng cách dùng tên mảng:
Aptr = A; Aptr = &A[0];
-2 Phép toán cộng trừ địa chỉ phần tử mảng:
Chỉ sử dụng trên biến con trỏ trỏ đến 1 mảng
• Phép cộng địa chỉ: <biếncontrỏ> + <số nguyên>
 <biếncontrỏ> + sizeof(kiểucơsở) * <số nguyên>
Ví dụ: Aptr = A + 3; hay
Aptr += 3; // Aptr = 3000 + 3 * 2
• Phép trừ địa chỉ: <biếncontrỏ 1> - <biếncontrỏ 2>
 số phần tử mảng từ con trỏ 1 đến contrỏ 2
Ví dụ: &A[3] - &A[1] 3
-3 Truy xuất đến 1 phần tử trong mảng bằng cách:
.3.1 Đối với mảng một chiều:
• Dùng tên mảng như địa chỉ đầu và chỉ số là khoảng cách đến phần tử cần truy
xuất.
Ví dụ: *(A + i) với i = 0 .. n -1.
• Dùng tên con trỏ trỏ địa chỉ đầu và chỉ số là khoảng cách đến phần tử cần truy
xuất.
Ví dụ: *(Aptr + i) với i = 0 .. n -1.
• Dùng con trỏ kèm chỉ số: Aptr[ I ]
.3.2 Đối với Mảng 2 chiều:
- Địa chỉ của phần tử [ i ][ j ] bằng: pt + i * CMAX + j
- Giá trị của phần tử [i][j] bằng : *(pt + i * CMAX + j)
Ví dụ: Viết chương trình nhân ma trận A (NxL) và B (LxM) : C (NxM) = A*B
#include <stdio.h>
#define MAX 10
void NhapMaTran(char ,float *, int, int);
void InMaTran(float *,int, int);
void TichMaTran(float* , float *, float *,int , int , int );
main()
{ float a[MAX][MAX], b[MAX][MAX], c[MAX][MAX];
int n=2,l=3,m=3;
NhapMaTran('A',(float*) a, n,l);
NhapMaTran('B', (float*) b,l,m);
TichMaTran((float*)a, (float*)b,(float*)c,n,l,m);
InMaTran((float*)c,n,m);
getch();
}
/*===============================*/
void NhapMaTran(char ten,float *pt, int sd, int sc)
{ int i,j;
printf("\nNhap gia tri cho cac phan tu cua mang %c : \n",ten);
for (i=0; i< sd; i++)
42
for (j=0; j < sc; j++)
{ printf("\t%c[%d][%d] : ",ten,i+1,j+1);
scanf("%f",pt + i*MAX + j);
}
}
/*=======================================*/
void InMaTran(float *pt,int sd, int sc)
{ int i,j;
for (i=0; i< sd; i++)
{ for (j=0; j < sc; j++) printf("%5.0f",*(pt+i*MAX+j));
puts("");
}
}
/*=================================*/
void TichMaTran(float *pa, float *pb, float *pc,int n, int l, int m)
{ int i, j, k;
for (i=0; i<n; i++)
for (j=0; j<m; j++)
{ *(pc+i*MAX+j) = 0;
for (k=0; k < l; k++)
*(pc+i*MAX+j) += (*(pa+i*MAX+k)) * (*(pb+k*MAX+j));
}
}
-4 Gán 1 biến mảng cho 1 biến mảng khác cùng kiểu mảng:
Sử dụng hàm memmove hay memcpy trong string.h:
• memmove(địachỉmảngđích, địachỉmảngnguồn, Kíchthướcmảngnguồn);
• memcpy(địachỉmảngđích, địachỉmảngnguồn, Kíchthướcmảngnguồn);
Dùng sao chép các byte liên tiếp bắt đầu <địachỉmảngnguồn> cho các byte chỉ bởi
<địachỉmảngđích> với số byte chỉ bởi <Kíchthướcmảngnguồn>.
Ví dụ: #define MAX 5
int a[MAX] = {2, 5, 7, 8, 9};
int b[MAX], C[MAX];
Gán giá trị các phần tử của mảng a cho mảng b: memmove(b, a, sizeof(a));
Gán giá trị 3 phần tử của mảng a bắt đầu từ phần tử thứ 2 cho 3 phần tử của mảng c
bắt đầu từ phần tử thứ 3.
memmove(c + 2, a + 1, sizeof(a[0])*3);
Ví dụ: Viết chương trình tạo mảng A gồm 5 phần tử . Gán giá trị mảng A cho mảng
B. Sao chép gía trị 3 phần tử kể từ phần tử thứ 2 cho mảng C bắt đầu từ phần tử thứ 3.
#include <stdio.h>
#include <string.h>
#define MAX 5
int a[MAX] = {2, 5, 7, 8, 9};
int b[MAX], C[MAX], I;
main()
{ menmove(b, a, sizeof(a));
inmang(b, sizeof(b));
menmove(c + 2, a + 1, sizeof(a[0])*3);
inmang(c, 3);

43
}
• void *memset( void * s, int Ch, sốbyte); Đặt n Byte của mảng A tới ký tự Ch
Ví dụ: memset( A, 0, sizeof(A));
-IVCon trỏ và Hàm:
Ngoài cách truyền tham số kiểu tham chiếu bằng toán tử tham chiếu & có thể truyền
tham chiếu bằng biến con trỏ.
• Khai báo hàm nhận tham số kiểu tham chiếu
<Kiểuhàm> <TênHàm>( <Kiểucơsở> *Tênđối số, ..)
• Gọi hàm và truyền tham số kiểu tham chiếu bằng biến con trỏ
TênHàm( &tênbiến,…);
Ví dụ Viết chương trình giản ước 1 phân số, có sử dụng 1 hàm tính ước số chung lớn
nhất của 2 số.
/*=========================*/ void toigianps(int *tu, int *mau)
void nhap_phan_so(int *x, int *y) { long int usc;
{ do usc = ucln(*tu,*mau);
{ printf("\nNhap phan so (x/y) : "); *tu = *tu/usc;
scanf("%d %*c %d", x, y); *mau = *mau/usc;
fflush(stdin); }
if (*y == 0) printf("\nMau so bang 0"); /*-------------------------------*/
else if (*x < 0 && *y < 0) int ucln(int a, int b)
{ *x = abs(*x); {
*y = abs(*y); int m,n,r;
} m = abs(a); n = abs(b);
else if (*x**y<0) r = m%n;
{ *x = -abs(*x); while (r != 0)
*y = abs(*y); { m = n;
} n = r; r = m%n;
} while (*y == 0); }
} return n;
}
main()
{
int a, b;
NhapPhanSo( &a, &b);
toigianps(&a,&b);
printf(“\nSau khi giản ước: %d/%d”,a,b);
}
• Kiểu kết quả của hàm là kiểu con trỏ:
Khi đó, giá trị trả về phải là một địa chỉ hay giá trị NULL
int *Tim(kiểu key; kiểu A[]; int n)
{
vitri:=0;
for (i = 0; i < n; i++)
if ( A[ i ] == key ) return &A[i];
return NULL;
}

44
BÀI 10: CHUỖI KÝ TỰ
-I Khái niệm:
Chuỗi ký tự là mảng một chiều có các phần tử là các ký tự, được đánh dấu kết thúc
chuỗi bằng ký tự NULL (‘\0’).
Các hằng chuỗi là dãy ký tự bao trong dấu nháy kép.
Trong C, các hàm xử lý chuỗi ký tự rất đa dạng và phong phú và được cung cấp trong
tập tin tiêu đề STRING.H (STRING.LIB).
-1 Khai báo biến chuỗi:
Khai báo biến chuỗi như mảng các ký tự:
char tênbiến[kích thước];
Kích thước là một hằng số nguyên qui định kích thước lưu trử tối đa(<65335), kể cả
dấu kết thúc chuỗi. Chương trình sẽ chạy không ổn định nếu nhập giá trị vượt quá kích
thước tối đa.
Ví dụ: char hoten[25];
C sẽ cung cấp 25 bytes từ 0..24 để lưu nội dung và ký tự kết thúc chuỗi.
- Có thể khởi đầu giá trị cho biến chuỗi khi khai báo:
- danh sách các hằng ký tự: char str[10] = {‘C’,’o’,’m’,’p’, ‘u’,’t’,’e’,’r’};
- Một hằng chuỗi ký tự: char str[10] = “Computer”;
Các byte còn lại được gán giá trị Null (‘\0’).
Too many initializers in ....: Số giá trị khởi tạo vượt quá kích thước mảng.
- Có thể khai báo biến chuỗi không chỉ định kích thước :
Ví dụ: char str[ ] = “Computer”;
-2 Khai báo kiều chuỗi:
typedef char string[255];
Tổ chức bộ nhớ:
Giống như mảng, biến chuỗi được cấp phát những byte liên tục.
Tên biến chuỗi là hằng chứa địa chỉ đầu của vùng nhớ.
Do đó, Không thể thực hiện lệnh gán 1 biến hay 1 hằng chuỗi cho biến mảng chuỗi.
Ví dụ: Lệnh gán sau sẽ bị lỗi: str = “Computer”;
1- Địa chỉ chứa ký tự thứ i trong chuỗi có thể truy xuất bằng:
Phép toán lấy địa chỉ: &str[i]
Phép toán cộng địa chỉ: (str + i)
-IICác thao tác trên kiểu chuỗi:
-1 Nhập chuỗi ký tự:
Trong stdio.h có các hàm: scanf(“%s”,str) và gets(str)
Hàm gets: Nhận dãy ký tự từ stdin cho đến khi gặp ‘\n’ . Ký tự ‘\n’ bị loại khỏi stdin
nhưng không đặt vào cuối chuỗi, chuỗi được bổ sung thêm ký tự kết thúc ‘\0’.
Dạng hàm: char *gets(char *s);
Ví dụ: char ht[25]; gets(ht);
Chú ý: int a; char ch,ht[25];
scanf(“%d”,&a); ch = getchar(); gets(ht);
Nếu tại hàm scanf nhập “12Etran van” thì trôi qua cả 2 lệnh kế tiếp. và a = 12, ch =
‘E’, ht = “tran van”. Do đó, nên dùng hàm fflush để làm sạch stdin.
Trong conio.h còn có hàm: cscanf(“%s”,str) và cgets(str)
Hàm scanf() và cscanf() chỉ dùng nhập chuỗi không có dấu cách
Hàm gets() và cgets() cho phép nhập chuỗi có cả dấu cách, C tự động thêm ký tự
Null ‘\0’ vào cuối nội dung chuỗi được nhập.

45
-2 Xuất chuỗi:
Trong stdio.h có hàm: printf(“%s”, const char *str );
int puts(const char *str );
a) Hàm puts: int puts(const char *str);
Xuất chuỗi ký tự str (hằng hay biến con trỏ) và đưa thêm ký tự ‘\n’ lên stdout. Khi
thành công hàm trả về ký tự ‘\n’. Khi có lỗi hàm trả về EOF.
Ví dụ: puts(“\nLac Hong”);
Trong conio.h có hàm: cprintf(“%s”, str);
int cputs(const char *str);
In chuỗi có địa chỉ str đến cuối chuỗi, xác định bởi ký tự Null.
Ví dụ: char s[20] = “An toan la ban”;
In toàn bộ chuỗi: printf(“%s”, s );
In từ chữ ‘t’ về cuối chuỗi: printf(“%s”, s+3); --> toan la ban
-3 Truy xuất đến từng ký tự trong chuỗi: biếnchuỗi[chỉsố]
Ví dụ: Chuyển 1 chuỗi thành chuỗi có ký tự đầu từ là chữ hoa, trong từ là chữ thường.
Ý tưởng: Ký tự đầu từ là ký tự khác dấu cách và có ký tự đứng trước là dấu cách. Ký tự
trong từ là ký tự khác dấu cách và có ký tự đứng trước cũng khác dấu cách
#include <stdio.h>
#include <ctype.h>
#define Max 500
main()
{ char s[Max]="an toan la ban";
char curr, pre; int i;
pre = 32; i=0;
while (s[i] != 0)
{
if (s[i]!= 32)
if (pre == 32) s[i] = toupper(s[i]);
else s[i] = tolower(s[i]);
pre = s[i];
i++;
}
printf("\n%s",s); getch();
}
Ví dụ: Cắt bỏ dấu cách bên phải 1 chuỗi chữ bằng cách ghi ký tự NULL vào sau ký tự khác
dấu cách đầu tiên ở bên phải.
#include <stdio.h>
#include <ctype.h>
#define Max 500
main()
{ char s[Max]="an toan la ban ";
int i, len;
len = strlen(s);
i = len-1;
while (s[i] == 32 && i >=0) i--;
if (i>=0) s[i+1]=0;
printf("\n%s!",s);
}

46
-IIIMảng các chuỗi:
Mảng các chuỗi là mảng 2 chiều có kiểu phần tử là kiểu char.
Ví dụ: Khai báo 1 mảng chứa 20 chuỗi ký tự, mỗi chuỗi dài tối đa 7 ký tự
char as[20][8]; ==> as[i] là một chuỗi có tối đa 7 ký tự.
Ví dụ: Viết chương trình nhập vào 1 năm dương lịch. In ra năm âm lịch tương ứng.
#include "string.h"
#include "stdio.h"
char Chi[12][7]={"Than","Dau","Tuat","Hoi","Ti","Sua","Dan",
"Meo","Thin","Ty","Ngo","Mui"};
char Can[10][7]={"Canh","Tan","Nham","Qui","Giap","At","Binh","Dinh"
,"Mau","Ky"};
int nam;
main()
{
printf("\nNhap Nam Duong Lich: ");
scanf("%d",&nam);
printf("\nNam am lich %s %s", Can[nam % 10],Chi[nam % 12]);
getch();
}
-IVCon trỏ và Chuỗi ký tự:
-1 Khai báo con trỏ: char *p, s[10];
-2 Gán địa chỉ vùng nhớ của biến chuỗi cho biến con trỏ: p = s;
-3 Gán hằng chuỗi cho biến con trỏ: p = “Lac Hong”;
C sẽ cung cấp 1 vùng nhớ 9 bytes và chứa địa chỉ vào biến con trỏ.
Ví dụ kiểm tra:
main()
{ char *s1, *s2;
s1 = “Lac Hong”; s2 = “Dai Hoc”;
printf(“\n%p %p”, s1, s2); --> 400 409
}
-4 Cấp phát động 1 vùng nhớ chứa dữ liệu và ghi địa chỉ vào biến con trỏ:
void *malloc(int sốbytes);
void *calloc(int sophầntử, int kíchthước1ptử);
void *realloc(void *ptr, kíchthướcmới); Cấp phát vùng nhớ mới theo kíchthướcmới và
chép dữ liệu của vùng nhớ trỏ bởi Ptr sang vùng nhớ
mới. Trả về địa chỉ vùng nhớ mới hoặc giá trị NULL .
Ví dụ: Tạo vùng nhớ để chứa 1 chuỗi 20 ký tự
char *s;
s = (char*) malloc(20);
Sau lệnh gán các thao tác trên biến chuỗi đều có thể áp dụng trên biến con trỏ.
-VHàm tự tạo và kiểu chuỗi:
Truyền một chuỗi ký tự cho hàm và luôn theo nguyên tắc truyền địa chỉ giống như
mảng 1 chiều. Khi đó:
Tham số khai báo thường là kiểu con trỏ char
Tham số thực sự tương ứng trong lời gọi hàm phải là địa chỉ hay là hằng chuỗi.
Và Hàm cũng có thể trả về địa chỉ một chuỗi
char * tênhàm(char *str,...);
Ví dụ: Xây dựng hàm trả về một chuỗi chữ có ký tự đầu từ in, trong từ là thường.
47
char *Proper(char *s)
{ char curr, pre;
int i;
pre = 32;
i=0;
while (s[i] != 0)
{
if (s[i]!= 32)
if (pre == 32) s[i] = toupper(s[i]);
else s[i] = tolower(s[i]);
pre = s[i];
}
return s;
}
Ví dụ: Xây dựng hàm xuất 1 chuỗi ký tự tại tọa độ x,y
void putxy(char *str, int x, int y, int color)
{
textattr(color);
gotoxy(x,y);
cprintf(“%s”, str);
}
-VICác hàm xử lý chuỗi strong STRING.H:
-1 Xác định chiều dài thật sự của chuỗi:
int strlen(const char *s);
-2 Đổi chữ thường thành chữ hoa và ngược lại:
char *strupr(char *s); chuyển chuỗi s thành chuỗi chữ Hoa
char *strlwr(char *s); chuyển chuỗi s thành chuỗi chữ thường
* Trong ctype.h có: int tolower(int ch); int toupper(int ch);
-3 Nối 2 chuỗi:
Nối chuỗi nguồn vào sau chuỗi đích: trong đó chuỗi đích phải có đủ kích thước để
chứa
char *strcat(char *dest, const char *src );
Ví dụ: char ho[20]=”Van Nhu“; ten[7]=”Bich”; hoten[30]=””;
strcat(hoten,ho); strcat(hoten,” “); strcat(hoten, ten);
puts(hoten);
Ví dụ: Viết chương trình dịch 1 số nguyên dương có tối đa 3 chữ số thành chữ.
#include "stdio.h"
#include "string.h"
char cs[10][6]={"khong","mot","hai","ba","bon","nam",
"sau","bay","tam","chin"};
int so ;
char *Dich3so(int so)
{
int dv,tram,chuc;
char chu[40];
dv = so % 10;
chuc = (so / 10) % 10;
tram = so / 100;

48
chu[0] = 0;
if (tram > 0)
strcat(strcat(chu,cs[tram]), " tram ");
if (chuc>1)
{
strcat(chu,cs[chuc]);
strcat(chu, " muoi ");
}
else
if (chuc == 1) strcat(chu,"muoi");
else
if (tram > 0 && dv > 0) strcat(chu,"linh ");
if (dv > 0)
if (dv == 5 && chuc>0) strcat(chu,"lam");
else (strcat(chu,cs[dv]));
return chu;
}
void main(void)
{
clrscr();
printf("Nhap 1 so co toi da 3 chu so: ");
scanf("%d",&so);
printf("\n%s",Dich3so(so));
getch();
}
Ví dụ: Xây dựng hàm Concat dùng nối 2 chuỗi và trả về chuỗi kết quả không thay đổi nội
dung 2 chuỗi nguồn.
char *Concat(const char *s1, const char *s2)
{ char *s;
s = (char*)malloc(strlen(s1)+strlen(s2));
s[0] = 0;
strcat(s, s1); strcat(s, s2);
return s;
}
-4 Nối n ký tự của chuỗi nguồn vào sau chuỗi đích:
char *strncat(char *dest, const char *src, int n);
sau đó thêm vào ký tự NULL cuối chuỗi dest
Ví dụ: Viết hàm cắt bỏ các dấu cách không cần thiết trong chuỗi.
char *Trim(char *s)
{ int i,len;
char *str;
char pre=32;
len = strlen(s);
str = (char*) malloc(len+1);
str[0] = 0;
for (i=0; i < len; i++)
{ if ((s[i] != 32) || (s[i] == 32 && pre != 32))
strncat(str,&s[i],1);

49
pre = s[i];
}
if (s[len-1] == 32) str[strlen(str)-1] = 0;
return str;
}
-5 Sao chép chuỗi:
Sao chép chuỗi nguồn sang chuỗi đích từ ký tự đầu cho đến ký tự kết thúc chuỗi
nguồn :
char *strcpy(char *dest, const char *src);
Ví dụ: Viết hàm trích 1 chuỗi con dài n ký tự bên phải chuỗi S.
char *Right(char *s, int n)
{ int len,vt = 0;
char *s1;
len = strlen(s);
if (n <= 0) n = 0;
else
if (n > len) n = len;
vt = len - n;
s1 = (char*)malloc(n+1);
strcpy(s1,s+vt);
return s1;
}
Có thể dùng strcpy để cắt bỏ ký tự trong chuỗi.
Ví dụ: Xóa n ký tự từ vị trí thứ thứ i trong chuỗi Str.
void Delete(char *str; int i, int n) /*Xóa ký tự tại vị trí thứ i */
{ int len ;
len = strlen(str);
if (i >=0 && i < len && n >0)
{
if (n > len-i) n = len-i;
strcpy(&str[ i ], &str[i+n]);
}
}
-6 Sao chép n ký tự từ chuỗi nguồn sang chuỗi đích:
char *strncpy(char *dest, const char *src, int sốtừ);
Nếu sốtừ > strlen(src): thì sẽ thêm những ký tự Null vào cho đủ sốtừ
Nếu sốtừ < strlen(src): thì chuỗi kết quả dest không được kết thúc rỗng.
Ví dụ: Trích ra chuỗi con dài n ký tự bắt đầu từ ký tự thứ i trong chuỗi.
char *Substr(char * s, int i, int n)
{
char *s1;
if (i >=0 && i < len && n >0)
{
s1 = (char*) malloc(n+1);
strncpy(s1,s+i-1,n);
s1[n]=0;
return s1;
}
50
return “”;
}
Ví dụ: Viết hàm trích ra n ký tự từ bên trái chuỗi S
char *Left(char *s,int n)
{
char *s1;
s1 = (char*) malloc(n+1);
strncpy(s1,s,n); /* strncat(s1, s, n); */
s1[n]=0;
return s1;
}
-7 So sánh 2 chuỗi theo thứ tự từ điển:
.7.1 So sánh 2 chuỗi :(phân biệt chữ hoa chữ thường)
int strcmp(const char *s1, const char *s2);
Kết quả: < 0 : s1 < s2; = 0 : s1 == s2; > 0 : s1 > s2;
Ví dụ: Sắp xếp danh sách kiểu chuỗi
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int sosanhtang(const void *p, const void *q);
int sosanhgiam(char *p, char *q);
main()
{
char a[4][10]={"Dai","Hoc", "Lac", "Hong"};
int i;
clrscr();
qsort(a,4,sizeof(a[0]),sosanhtang);
for (i=0; i<4;i++)
printf("\n%s",a[i]);
qsort(a,4,sizeof(a[0]),sosanhgiam);
for (i=0; i<4;i++)
printf("\n%s",a[i]);
getch();
}
int sosanhtang(const void *p, const void *q)
{
return (strcmp((char*)p, (char*)q));
}
int sosanhgiam(char *p, char *q)
{
return (-strcmp((char*) p, (char*) q));
}
.7.2 So sánh 2 chuỗi phân biệt chữ hoa chữ thường:
int stricmp(const char *s1, const char *s2 );
Kết quả: < 0 : s1 < s2; = 0 : s1 == s2; > 0 : s1 > s2;

51
-8 Tìm kiếm:
.8.1 Tìm chuỗi con substr trong 1 chuỗi str :
char *strstr(const char *str, const char *substr);
Trả về con trỏ đến ký tự đầu tiên của chuỗi con str2 trong chuỗi str1.
Ví dụ: char *s;
s = strstr(“Dai Hoc Lac Hong”, “Ho”);
puts( s ); --> “Hoc Lac Hong”
.8.2 Tìm ký tự ch trong chuỗi str: char *strchr(char * s, int ch)
Ví dụ: char *s;
s = strchr(“Dai Hoc Lac Hong”, ‘H’) ;
puts( s ); --> “Hoc Lac Hong”
.8.3 Hàm: int strspn(const char *str1, const char * str2);
Trả về chỉ số ký tự đầu tiên trong str1 không có trong str2.
Ví dụ: Viết hàm cắt bỏ dấu cách bên trái chuỗi s.
void Ltrim(char *s)
{
int i;
i = strspn( s, " "); /*tìm ký tự đầu tiên khác ký tự trắng */
strcpy(s, s+i);
printf("\n%s! %d",s,i);
}
.8.4 Hàm: char *strpbrk(const char *str1, const char *str2);
Trả về con trỏ đến ký tự đầu tiên trong chuỗi str1 xuất hiện trong chuỗi str2.
Ví dụ: printf(“%s”, strpbrk(“Dai hoc”,”h”)) --> hoc
-9 Các hàm chuyển đổi kiểu: (stdlib.h)
.9.1 Chuyển số thành chuỗi:
char *itoa(int số, char *str, int cơsố): chuyển số nguyên thành chuỗi số theo 1 cơ số
nào đó
Ví dụ: char s[10];
itoa(128, s, 10) -->”128”
char *ltoa(int số, char *str, int cơsố): chuyển số Long int thành chuỗi số theo 1 cơ số
nào đó
Ví dụ: ltoa(128, s, 10) -->”128”
char *gcvt(double x, int ndig, char *s); chuyển số thực x thành chuỗi s dài ndig ký tự
không kể dấu trừ..
Ví dụ: gcvt(3.14159, 5, s); ---> s = “3.1416”
gcvt(-3.14159, 5, s); ---> s = “-3.1416”
Chuyển chuỗi thành số:
int atoi(const char *str): Trả về số nguyên int chuyển từ chuỗi số str
Vi dụ: int n;
n = atoi(“123.56”) ; printf(“%d”,n); --> 123
long atol(const char *str): Trả về số nguyên long int chuyển từ chuỗi số str
Vi dụ: long n;
n = atol(“123.56”) ; printf(“%ld”,n);--> 123
double atof(const char *str): Trả về số thực double chuyển từ chuỗi số str
Vi dụ: double x;

52
x = atof(“123.56Hello”)
printf(“%lf”, x) --> 123.56

53
BÀI 11: Kiểu Structure
-I Khái niệm:
Kiểu mẫu tin là kiểu dữ liệu tự tạo mà một biến thuộc kiểu gồm nhiều mục dữ liệu,
mỗi mục dữ liệu có tên khác nhau và có thể có kiểu khác nhau.
Kiểu mẫu tin cho phép chúng ta mô tả các đối tượng thực tế có cấu trúc phức tạp.
Ví dụ: Mô tả thông tin về một con người gồm các thông tin: Họ tên, năm sinh, nơi
sinh, giới tính, địa chỉ, tình trạng gia đình.v.v...
-IIĐịnh nghiã kiểu cấu trúc mới:
Mẫu 1:
struct tên_kiểu{
kiểu tênmục1;
kiểu tênmục2;
............
};
Mẫu 2:
typedef struct {
kiểu tênmục1;
kiểu tênmục2;
............
} tênkiểu;
Ví dụ: Định nghiã kiểu cấu trúc tên là date
struct date { char day, month; int year; };
Ví dụ: Định nghiã kiểu cấu trúc mô tả lý lịch nhân viên
struct lylich
{ char ho[20]; char ten[7];
struct date ngsinh, ngayvaocoquan;
float hesoluong;
};
-IIIKhai báo biến kiểu cấu trúc:
Mẫu 1:
struct tênkiểucấutrúc tênbiến ={giá trị mục 1, giá trị mục 2,...};
Ví dụ: struct date ngay={12,5,1999};
struct lylich nv ={“Le Anh”,”Tuan”,{12,3,1960},{16,4,1990},2.05};
Chú ý: * Nếu định nghĩa kiểu theo mẫu 2 thì không cần dùng từ khóa struct.
* Chư c khi aău bieân caâu truc va mạng caâu truc khai bao beđn ngoai cac ham hay
khai bao static. Vieôc khi aău c thc hieôn 1 laăn khi dch chng trnh.
Mẫu 2: Khai báo biến khi định nghiã kiểu cấu trúc.
struct [tên_kiểu]
{
kiểu tênmục1;
kiểu tênmục2;
............
} danhsáchbiến;
-IVCác thao tác trên biến mẫu tin:
-1 Gán 2 biến cấu trúc có cùng kiểu cấu trúc.
nv1 = nv2;

54
-2 Truy xuất giá trị từng mục dữ liệu trong 1 biến kiểu cấu trúc:
Sử dụng phép toán dấu chấm: tênbiến.tênmục
Ví dụ: printf(“\nHo ten nhan vien: %s %s”,nv.ho,nv->ten);
printf(“\nNgay sinh: %d/%d/%d”,nv.ngsinh.day,nv.ngsinh.month,nv.ngsinh.year);
-3 Địa chỉ của biến cấu trúc và mục dữ liệu:
• Lấy địa chỉ của biến cấu trúc: &tênbiến
• Lấy địa chỉ của mục dữ liệu: &tênbiến.tênmục
scanf(“%f”,&nv.hesoluong);
scanf(“%d/%d/%d”, &nv.ngsinh.day,&nv.ngsinh.month,&nv.ngsinh.year);
Ví dụ: Tính toạ độ trung điểm của 1 đoạn thẳng AB
typedef struct{ float x,y;}Diem;
Diem A,B,M;
main()
{
printf("\nNhap toa do diem A: ");
scanf("%f%*c%f",&A.x,&A.y);
printf("\nNhap toa do diem B: ");
scanf("%f%*c%f",&B.x,&B.y);
M.x = (A.x + B.x)/2;
M.y = (A.y + B.y)/2;
printf("\nToa do diem M la (%.2f,%.2f)",M.x,M.y);
getch();
}
-VCon trỏ của kiểu cấu trúc:
-1 Khai báo con trỏ kiểu cấu trúc:
struct tênkiểucấutrúc *têncontrỏ;
ví dụ: struct lylich *p;
-2 Gán địa chỉ vùng nhớ của 1 biến cấu trúc cho biến con trỏ:
têncontro = &tênbiếncấutrúc;
-3 Truy xuất giá trị 1 thành phần của biến cấu trúc bằng biến con trỏ:
(*p).tênmuc hay p ->tênmục
Chú ý: Phép toán (->) hay (.) có độ ưu tiên cao nhất tương đương với các dấu () hoặc [ ]
-VIHàm và kiểu cấu trúc:
-1 Đối số của hàm có thể là:
Một biến cấu trúc: Khi đó tham số thực tương ứng là tên biến cấu trúc.
Một con trỏ cấu trúc: Khi đó tham số thực tương ứng là địa chỉ của biến cấu trúc.
-2 Hàm có thể trả về một:
Giá trị kiểu cấu trúc:
Con trỏ kiểu cấu trúc
Ví dụ Viết chương trình tính tổng 2 phân số.
typedef struct{ int tu, mau } PhanSo;
void nhap_phan_so(PhanSo *);
void toigianps(PhanSo *);
PhanSo Tong(PhanSo , PhanSo);
int ucln(int , int );
main()
{ PhanSo a, b, c;
NhapPhanSo( &a ); NhapPhanSo( &b );
55
c = Tong(a,b); toigianps(&c);
printf(“\nSau khi giản ước: %d/%d”,c.tu, c.mau);
}
/*=========================*/
void NhapPhanSo(PhanSo *a)
{ PhanSo x;
do{ printf("\nNhap phan so (x/y) : ");
scanf("%d %*c %d", &x.tu, &x.mau); fflush(stdin);
if ( x.mau == 0) printf("\nMau so bang 0");
} while (x.mau != 0);
*a = x;
if (a->tu * a->mau >= 0) a->tu = abs(a->tu);
else a->tu = -abs(a->tu);
a->mau = abs(a->mau);
}
/*-------------------------------*/
void toigianps(PhanSo *a)
{
long int usc;
usc = ucln(a->tu,a->mau);
a->tu = a->tu/usc;
a->mau = a->mau/usc;
}
/*-------------------------------*/
int ucln(int a, int b)
{
int m,n,r;
m = abs(a); n = abs(b);
while (n != 0)
{ r = m%n; m = n; n = r; }
return m;
}
/*----------------------*/
Phanso Tong(Phanso a, Phanso b)
{
Phanso c;
c.tu = a.tu*b.mau + b.tu*a.mau;
c.mau = a.mau*b.mau;
return c;
}
-VIIMảng cấu trúc:
Dùng quản lý danh sách đối tượng thuộc kiểu cấu trúc
Tênkiểucấutrúc mảng[MAX];
Ví dụ: lylich nv[100];
nv[i] là 1 biến cấu trúc thuộc kiểu lylich.

Ví dụ: Nhập và in danh sách thí sinh theo thứ tự tên và họ


#include <stdio.h>

56
#include <conio.h>
#include <string.h>
#include <stdlib.h>
typedef struct{ unsigned char ngay, thang;
int nam; } date;
typedef struct{ int sbd;
char ho[25],ten[7];
date ngsinh;
float toan,ly,hoa;
float DTB;
}hoso;
hoso thisinh[100];
int n;
void NhapHoso(hoso ts[],int *n)
{
int i=0;
hoso hs;
printf("\nNhap Ho so thi sinh \"Ho bang rong de ket thuc\"");
do
{
hs.sbd = i+1;
printf("\nNhap ho so cho thi sinh: %3d",hs.sbd);
printf("\nHo : "); gets(hs.ho);
if (hs.ho[0]=='\0') break;
printf("Ten: "); gets(hs.ten);
printf("Ngay sinh: ");
scanf("%d/%d/%d%*c", &hs.ngsinh.ngay, &hs.ngsinh.thang,
&hs.ngsinh.nam);
printf("Diem Toan: "); scanf("%f%*c",&hs.toan);
printf("Diem Ly: "); scanf("%f%*c",&hs.ly);
printf("Diem Hoa: "); scanf("%f%*c",&hs.hoa);
hs.DTB = (hs.toan+hs.ly+hs.hoa)/3;
ts[i] = hs;
i++;
}while (i<100);
*n = i;
}
int sosanh(const void *p, const void *q)
{
int kq;
kq = strcmp(((hoso*)p)->ten,((hoso*)q)->ten);
if (kq == 0) return strcmp(((hoso*)p)->ho,((hoso*)q)->ho);
return kq;
}
void InKQ(hoso ts[], int n)
{
int i;
qsort(ts,n,sizeof(ts[0]),sosanh);

57
printf("\n%-4s %-25s %-10s %-4s %-4s %-4s %-5s","SBD", "Ho Ten",
"Ngay sinh","Toan","Ly","Hoa","Phong");
for (i=0;i<n;i++)
printf("\n%4d %-18s %-7s %2d/%2d/%2d %4.1f %4.1f %4.1f %3d",
ts[i].sbd,ts[i].ho,ts[i].ten,ts[i].ngaysinh.ngay,
ts[i].ngaysinh.thang,ts[i].ngaysinh.nam,ts[i].toan,
ts[i].ly, ts[i].hoa, ts[i].phongthi);
getch();
}
void Hoanvi(hoso *ts1, hoso *ts2)
{
hoso tam;
tam = *ts1; *ts1 = *ts2;
*ts2 = tam;
}
void bublesort(hoso ts[], int n)
{
int i,j;
int ssten;
for (i=1; i<=n-1; i++)
for (j=n-1; j>=i;j--)
{
ssten = strcmp(ts[j].ten,ts[j-1].ten);
if ((ssten < 0 ) ||
(ssten == 0 && strcmp(ts[j].ho,ts[j-1].ho) < 0))
Hoanvi(&ts[j],&ts[j-1]);
}
}
hoso Timhs(int sbd, hoso ts[], int n)
{
int i; hoso hs;
hs.sbd = hs.ngaysinh.ngay= hs.ngaysinh.thang= hs.ngaysinh.nam=0;
hs.ho[0]= hs.ten[0] = 0;
for (i=0; i<n; i++)
if ( sbd == ts[i].sbd) return ts[i];
return hs;
}
hoso *pTimhs(int sbd, hoso ts[], int n)
{
int i;
for (i=0; i<n; i++)
if (sbd==ts[i].sbd) return(&ts[i]);
return (NULL);
}
void main(void)
{
clrscr();
NhapHoso(thisinh,&n);

58
InKQ(thisinh,n);
return 0;
}

59
Thành Phần Kiểu Bit: (Bit Field)
Ý nghiã và cách khai báo:
Đối với các thành phần trong một cấu trúc dùng lưu trữ những số nguyên nhỏ, ta có thể khai
báo theo kiểu nhóm Bits như sau:
Kiểudữliệu [tênTP] : sốbít;
Kiểu dữ liệu: là unsigned int hay signed int
Sốbít ( 16;
Khi muốn bỏ qua một số Bits ta bỏ trống tên thành phần.
Ví dụ:Ta có thể khai báo kiểu ngày như sau:
typedef struct
{ unsigned d:5; m:4, y : 5, : 2;
int t :2; //Thứ tự ngày tháng năm
int : 6;
char gach;
} date;
* Các thành phần không phải là kiểu nhóm Bits sẽ được lưu ở byte khác bỏ qua phần bit
không sử dụng..
Các quy tắc nhập xuất các thành phần kiểu Nhóm Bits:
Gán/xuất giá trị : Dùng phép toán dấu (.) hay (->) nếu sử dụng con trỏ.
Ví dụ: date day;
day.d = 20; printf(" %d ", day.d);
Không thể lấy địa chỉ thành phần kiểu nhóm Bits: do đó để nhập dữ liệu cho thành phần
kiểu Nhóm Bits phải dùng 1 biến số nguyên trung gian.
Ví dụ: unsigned temp;
scanf(%d",&temp); day.d = temp;
Do đó, không thể truyền thành phần kiểu Bit cho hàm theo kiểu địa chỉ.
Kieơu Union:
Khai nieôm: Gioâng nh kieơu maêu tin, kieơu union cung goăm nhieău thanh phaăn nhng
cac thanh phaăn cụa union c caâp phat vung nh chung, kch thc cụa moôt bieân union baỉng kch thc
cụa thanh phaăn ln nhaât.
Kieơu union thng c dung mođ tạ thanh phaăn d lieôu thay oơi tuy theo
Khai bao:
Vieôc nh ngha kieơu, khai bao bieân va truy xuaât eân tng thanh phaăn cụa bieân kieơu
union gioâng nh oâi vi kieơu maêu tin.
V dú: Quạn ly danh sach hóc sinh goăm: hó teđn, ngay sinh, ieơm trung bnh, xeâp loái,
neâu hóc sinh gioi hay kha th c hóc boơng, hóc sinh yeâu th lái lp hoaịc khođng tuy theo DTB di
3.5 hay tređn 3.5
union ketqua
{ float hocbong;char PheBinh[30];
};
struct hocsinh
{ char hoten[30]; float dtb;
char xeploai[3]; union ketqua kq;
};_typedef union
{ float hocbong;char PheBinh[30];
} ketqua;
typedef struct {
char hoten[30]; float dtb;
char xeploai[3];ketqua kq;
} hocsinh;__

60
KIỂU TẬP TIN
Một tập tin đơn giản chỉ là một dãy các byte lưu trữ trong bộ nhớ ngoài với một tên file.
Tên file được đặt theo qui ước của hệ điều hành. Dữ liệu ghi ở dạng số nhị phân như trong bộ nhớ.
Mỗi file đều được đánh dấu kết thúc bởi ký tự có mã 26 (trong stdio.h đã định nghiã EOF = –1).
Dựa vào cách nhập dữ liệu và xuất dữ liệu, có 2 loại tập tin :
• File văn bản: Tổ chức dữ liệu theo từng dòng, cuối mỗi dòng được kết thúc bở 2 ký tự
CR(13) LF(10).
• File nhị phân:
Việc nhập xuất File văn bản chỉ khác file nhị phân khi xử lý ký tự chuyển dòng (mã 10, ký
hiệu ‘\n’):
• Khi ghi, 1 ký tự LF (line feed) được chuyển thành 2 ký tự CR (Carriage Return_13) và
LF (Line Feed_10).
• Khi đọc 2 ký tự liên tiếp CR và LF trên file chỉ cho ta 1 ký tự LF.
Việc truy xuất đến 1 file trên đĩa được thực hiện thông qua biến con trỏ file.
Qui cách làm việc với tập tin:
- Khai báo biến tập tin
- Mở tập tin
- Truy xuất tập tin
- Đóng tập tin
Truy xuất File bằng các Hàm của STDIO.H:
a. Khai báo biến file: FILE *tênbiếnfile;
b. Mở file: tênbiếnfile = fopen(char *têntậptin, char *kiểu);
• têntậptin: Bao gồm cả đường dẫn. Nếu có lỗi sẽ cho gt NULL.
• Kiểu: Kết hợp bởi các mã sau nhầm khai báo kiểu tập tin và cách thức truy xuất tập tin.
Kiểu Ý nghiã
“b” Mở tập tin kiểu nhị phân
“t” Mở tập tin kiểu văn bản
“r” Mở để đọc. Nếu không có File sẽ báo lỗi
“r Mở để đọc hoặc ghi
+”
“ Mở file mới để ghi. Nếu file đã có sẽ bị xóa
w”
“ Mở file mới để đọc hoặc ghi.
w+”
“ Mở file để ghi thêm vào cuối file
a”
“ Mở file để đọc ghi. Nếu file cũ thì nối thêm,nếu không thì tạo
a+” mới
Ví dụ: FILE *fp;
f = fopen(“C:\\THOCA”, “wt”);
c. Đọc ghi file :
• Kiểu văn bản:
- int fprintf(FILE *fp, const char *dk,các biểu thức);
Ghi dữ liệu theo khuôn dạng. Nếu thành công hàm trả về số byte ghi lên điã, ngược lại trả
về giá trị EOF (-1). Ký tự ‘\n’ được ghi thành 2 ký tự CR và LF.
Ví dụ: fprintf(fp,”so dinh %d \n”, n);
- fscanf(FILE *fp, const char *dk,địa chỉ các biến);
Đọc dữ liệu theo khuôn dạng. Nếu thành công hàm trả về số byte đọc được, ngược lại trả về giá
trị EOF (-1).

61
Ví dụ: Đọc file DAGIAC.DAT chứa toạ độ các đỉnh của đa giác. dòng đầu ghi số đỉnh. Các dòng
sau, mỗi dòng ghi tạo độ của 1 đỉnh của đa giác.
typedef struct pointtype Polygol[10];
Polygol P;
void main(void)
{
FILE *fp;
char c; int n,i;
fp = fopen("c:\\dagiac.dat","rt");
fscanf(fp,"%d",&n);
printf("%d\n",n);*/
for (i=0; i<n; i++)
fscanf(fp,"%d %d",&P[i].x,&P[i].y);
fclose(fp);
}
• Trường hợp không biết trước số đỉnh của đa giác:
i = 0;
while (fscanf(fp,"%d %d",&P[i].x, &P[i].y) != EOF)
printf("%d %d\n",P[i].x,P[i].y);
- int fputs(const char *Str, FILE *fp);
Ghi chuỗi Str vào file. Khi thành công hàm trả về ký tự cuối cùng được ghi, ngược lại hàm cho
EOF.
- char *fgets(char * Str, int n, FILE *fp);
Đọc 1 chuỗi tối đa n-1 ký tự. Hàm trả về địa chỉ vùng chứa chuỗi. Nếu có lỗi hoặc gặp cuối file
hàm cho giá trị NULL.
Việc đọc kết thúc khi:
+ Đọc hết n-1 ký tự;
+ Gặp dấu xuống dòng (cặp mã 13 10): khi đó mã 10 (‘\n’) được ghi vào xâu kết quả
+ Gặp dấu kết thúc file
• Kiểu nhị phân:
- int putw(int n, FILE *fp);
Ghi số nguyên n vào file. Trả về EOF nếu có lỗi, ngược lại trả về số byte được ghi.
- int getw(FILE *fp);
Đọc số nguyên 2 byte. nếu có lỗi hay cuối file hàm trả về EOF
- int fwrite(void *ptr, int size, int n, FILE *fp);
Ghi n phần tử của mảng ptr, mỗi phần tử có kích thước size bytes. Hàm trả về số
phần tử thật sự được ghi.
- int fread(void *ptr, int size, int n FILE *fp);
Đọc n phần tử của mảng ptr , mỗi phần tử có kích thước size bytes. Hàm trả về số
phần số phần tử thật sự đọc được.
d. Đóng file:
- int fclose(FILE *fp); Nếu có lỗi hàm cho EOF ngược lại cho giá trị 0.
- int fcloseall( ); Nếu có lỗi cho EOF nếu không cho số file được đóng.
e. Kiểm tra cuối file: int feof(FILE *fp);
Hàm cho giá trị khác 0 nếu gặp cuối file sau khi đọc
Ví dụ: while (1)
{ fscanf(fp,”%d %d”, &P[i].x, &P[i].y);
if ( feof( f )) break;
}
f. Di chuyển con trỏ phần tử
- void rewind(FILE *fp); Chuyển con trỏ phần tử về đầu file.

62
- int fseek(FILE *fp, long sốbyte, int vịtríxuấtphát); chuyển đầu đọc từ vị trí xuất
phát qua 1 số byte về cuối file (sốbyte>0) hay đầu file (sốbyte<0). Nếu thành công
hàm trả về giá trị 0.
Vị trí xuất phát có thể nhận các giá trị:
+ SEEK_SET hay 0: xuất phát từ đầu file
+ SEEK_CUR hay 1: xuất phát từ vị trí hiện hành
+ SEEK_END hay 2: xuất phát từ cuối file.
g. Vị trí con trỏ byte: long ftell(FILE *fp); Cho biết con trỏ ở byte thứ mấy của file (tính từ 0).
Khi có lỗi hàm trả về –1L.
Nhận xét:
Bản chất là truy xuất tuần tự, nên truy xuất chậm. Lưu trữ dữ liệu dài hạn, không giới hạn

63
Phụ lục:
Các thao tác soạn thảo và thực hiện chương trình:
1. Mở tập tin chương trình:
• Tạo tập tin mới: File New
• Nạp tập tin chương trình từ điã: File Load (F3)
2. Nhập nội dung chương trình:
Một số thao tác phím trong khi nhập:
Di chuyển con trỏ:
Ctrl + → (Ctrl + ←): Qua trái (phải) 1 từ
Tab: Di chuyển 1 khoảng
Home / End: Ra đầu / cuối dòng
Ctrl+PgUp / Ctrl+PgDn: Về đầu/cuối chương trình
Chèn/ Xóa:
Ins : Bật tắt chế độ chèn đè
Ctrl+N: chèn 1 dòng
Enter: Xuống dòng/ thêm dòng mới
Ctrl+Y: Xóa dòng
Ctrl+Q+Y : Xóa từ con trỏ đến cuối dòng
Khối văn bản:
- Ctrl+K+B : Đánh dấu đầu khối
- Ctrl+K+K : Đánh dấu cuối khối
- Ctrl+K+C : Chép khối
- Ctrl+K+V: Di chuyển khối
- Ctrl+K+I : Di chuyển khối sang phải 1 cột
- Ctrl+K+U : Di chuyển khối sang trái 1 cột
- Ctrl+K+Y: Xóa khối
- Ctrl+K+H: Hiện ẩn đánh dấu khối
- Ctrl+K+P: In khối ra máy in.
- Ctrl+K+W: ghi khối ra điã
- Ctrl+K+R: đọc 1 file trên đĩa
3. Lưu nội dung vào điã:
Ấn F2 hay File Save và đặt tên file (nếu chưa)
Nếu muốn lưu vào file khác: Chọn File Write to và đặt tên file
4. Chạy và sửa lỗi chương trình:
• Kiểm tra và chỉ định thư mục hệ thống: Options Directories (nếu cần)
Include directories: C:\TC\INCLUDE (chỉ định nơi chứa các tập tin *.H)
Library directories: C:\TC\LIB (Nơi chứa các tập tin thư viện hàm *.LIB)
Output Directory: (Nơi chứa các file *.OBJ và *.EXE)
Turbo C directory: C:\TC (thư mục chứa tập tin cấu hình .TC và TCHELP.TCH..
Pick file name: Tên file lưu trữ tên các file vừa mở
Current pick file: Tên file chọn lọc hiện hành
Nếu có sửađổi và cần lưu cấu hình mới cho các lần sau: Options Save Options
Overwrite: \TC\TCConfig.TC (Y/N) ?
• Chạy chương trình:
Biên dịch, nối kết và chạy chương trình: Ctrl+F9 (Run Run)
- Nếu có lỗi hoặc có các vấn đề cần chú ý thì sẽ xuất hiện những dòng thông báo lỗi
(Error) hoặc những dòng cảnh báo (Warning) trong khung Watch.
64
- Khi chương trình bị treo: Ctrl+Break và Ctrl+F2 (Run Program reset)
- Khi đã có tập tin .EXE, ta có thể thực hiện tại dấu đợi lệnh của DOS.
• Chạy từng dòng lệnh chương trình bằng:
F7 (Run Trace into) hay F8 (Run Step over)
Ctrl+F7 : Nhập tên biến để kiểm tra giá trị các biến.

65

You might also like