You are on page 1of 290

Lập Trình Căn Bản

Tổng Quan
Mục Đích Yêu Cầu
 Khái niệm về ngôn ngữ lập trình
 Khái niệm về kiểu dữ liệu
 Kiểu dữ liệu có cấu trúc (cấu trúc dữ liệu)
 Khái niệm về giải thuật
 Ngôn ngữ biểu diễn giải thuật
 Ngôn ngữ sơ đồ (lưu đồ), sử dụng lưu đồ để biểu diễn
các giải thuật
 Tổng quan về Ngôn ngữ lập trình C
 Các kiểu dữ liệu trong C
 Các lệnh có cấu trúc
 Cách thiết kế và sử dụng các hàm trong C
 Một số cấu trúc dữ liệu trong C
Nội Dung Cốt Lõi
 Chương 1: Giới thiệu về ngôn ngữ C & môi trường lập
trình Turbo C
 Chương 2: Các thành phần của ngôn ngữ C
 Chương 3: Các kiểu dữ liệu sơ cấp chuẩn và các lệnh
đơn
 Chương 4: Các lệnh có cấu trúc
 Chương 5: Chương trình con
 Chương 6: Kiểu mảng
 Chương 7: Kiểu con trỏ
 Chương 8: Kiểu chuỗi ký tự
 Chương 9: Kiểu cấu trúc
 Chương 10: Kiểu tập tin
LẬP TRÌNH CĂN BẢN
Phần 1
GIỚI THIỆU VỀ CẤU TRÚC DỮ
LIỆU VÀ GIẢI THUẬT
P.D.Nghiệp
Nội dung chương
 Từ bài toán đến chương trình
 Giải thuật
 Khái niệm giải thuật
 Các đặc trưng của giải thuật
 Ngôn ngữ biểu diễn giải thuật
 Một số giải thuật cơ bản
 Các cấu trúc suy luận cơ bản của giải thuật
 Từ giải thuật đến chương trình
 Kiểu dữ liệu
 Khái niệm về ngôn ngữ lập trình
 Chương trình dịch
Từ Bài Toán Đến Chương Trình
 Các bước giải bài toán bằng máy tính
 Mô tả các bước giải bài toán
 Vẽ sơ đồ xử lý
 Viết chương trình xử lý bằng ngôn ngữ giả
 Chọn ngôn ngữ lập trình và chuyển chương trình
từ ngôn ngữ giả sang ngôn ngữ lập trình
 Thực hiện chương trình: nhập vào các tham số,
nhận kết quả
Giải Thuật
 Khái niệm giải thuật
 Các đặc trưng của giải thuật

 Ngôn ngữ biểu diễn giải thuật

 Một số giải thuật cơ bản

 Các cấu trúc suy luận cơ bản của giải thuật

 Từ giải thuật đến chương trình


Khái Niệm Giải Thuật
 Ví dụ: Hoán đổi chất lỏng trong 2 bình A (nước
mắm) và B (rượu):
 Yêu cầu phải có thêm một bình thứ ba gọi là bình C.
 Bước 1: Đổ rượu từ bình A sang bình C.
 Bước 2: Đổ nước mắm từ bình B sang bình A.
 Bước 3: Đổ rượu từ bình C sang bình B.

 “Giải thuật là một dãy các thao tác trên những dữ


liệu vào sao cho sau một hữu hạn bước ta thu
được kết quả của bài toán ”.
Các Đặc Trưng Của Giải Thuật
 Tính kết thúc
 Số bước là hữu hạn
 Tính xác định
 Máy phải thực hiện được
 Cho cùng kết quả trên các máy khác nhau
 Tính phổ dụng
 Tính hiệu quả
 Thời gian
 Tài nguyên máy
Ngôn Ngữ Biểu Diễn Giải
Thuật
 Ngôn ngữ tự nhiên
 Ngôn ngữ sơ đồ

 Ngôn ngữ giả


Ngôn Ngữ Tự Nhiên
 Là ngôn ngữ của chúng ta
 Ví dụ: Giải thuật giải phương trình bậc nhất ax+b=0.
Bước 1: Nhận giá trị của các tham số a, b.
Bước 2: Xét giá trị của a xem có bằng 0 hay không?
Nếu a=0 thì làm bước 3, nếu a khác không thì
làm bước 4.
Bước 3: (a bằng 0) Nếu b bằng 0 => pt vô số nghiệm.
Nếu b khác 0 => pt vô nghiệm.
Bước 4: ( a khác 0) Ta kết luận phương trình có
nghiệm x=-b/a.
Ngôn Ngữ Sơ Đồ (1)
 Mô tả giải thuật bằng các sơ đồ hình khối đã được
(quy ước trước)
Ngôn Ngữ Sơ Đồ (2)
 Ví dụ: Dùng lưu đồ để biểu diễn giải thuật tìm UCLN
như sau:
Ngôn Ngữ Giả
 Là một sự kết hợp giữa ngôn ngữ tự nhiên với các
cấu trúc câu lệnh của một ngôn ngữ lập trình.

 Ví dụ: Giải thuật giải phương trình bậc nhất ax+b=0.


 Nhập vào a, b
 If a==0 then
If b==0 then
Kết luận phương trình vô số nghiệm
else
Kết luận phương trình vô nghiệm
else
Kết luận phương trình có nghiệm x=-b/a
Một Số Giải Thuật Cơ Bản (1)
 Ví dụ 1: Yêu cầu:
 Nhập vào 1 dãy n số
hạng a1, a2, .., an
 Tính tổng S:
S= a1 + a2 + a3 + ... + an
 In S ra màn hình
Một Số Giải Thuật Cơ Bản (2)
 Ví dụ 2: Yêu cầu:
 Nhập vào 2 số a và b là
2 hệ số của pt: ax+b=0
 Cho biết nghiệm của
phương trình.
Các Cấu Trúc Suy Luận Cơ Bản
Của Giải Thuật (1)
 Giải thuật được thiết kế theo 3 cấu trúc suy luận cơ bản:

 Tuần tự (Sequential):
 Các công việc được thực hiện tuần tự, công việc này nối tiếp
công việc kia.

 Cấu trúc lựa chọn (Selection)


 Lựa chọn một công việc để thực hiện căn cứ vào một điều kiện
nào đó
 Cấu trúc 1: Nếu < điều kiện> (đúng) thì thực hiện <công việc>
 Cấu trúc 2: Nếu < điều kiện> (đúng) thì thực hiện <công việc 1>,
ngược lại (điều kiện sai) thì thực hiện <công việc 2>
 Cấu trúc 3: Trường hợp < i> thực hiện <công việc i>
Các Cấu Trúc Suy Luận Cơ Bản
Của Giải Thuật (2)
 Cấu trúc lặp (Repeating)
 Lặp lại thực hiện một công việc không hoặc nhiều
lần căn cứ vào một điều kiện nào đó.
 Có 2 dạng như sau:
 Lặp với số lần xác định
 Lặp với số lần không xác định
Từ Giải Thuật Đến Chương
Trình
 Cả 2 đều là tập các chỉ thị (instruction) – làm
thế nào để giải quyết 1 công việc (task).
 Giải thuật
 Nói chuyện với con người, dễ hiểu.
 Dùng ngôn ngữ đơn giản (English) – không viết
bằng mã.
 Chương trình
 Nói chuyện với máy tính.
 Có thể được xem như 1 diễn tả hình thức (formal
expression) của 1 giải thuật.
Kiểu Dữ Liệu
 Ví dụ:
int x,y;
float r=3.25;

 “Kiểu dữ liệu là một tập hợp các giá trị có cùng một
tính chất và tập hợp các phép toán thao tác trên các
giá trị đó”.
 Có 2 loại
 Kiểu dữ liệu sơ cấp
 Kiểu dữ liệu có cấu trúc
Kiểu Dữ Liệu Sơ Cấp
 “Kiểu
dữ liệu sơ cấp là kiểu dữ liệu mà giá trị
của nó là đơn nhất”.

 Ví dụ: Kiểu int trong C


 là kiểu sơ cấp
 gồm các số nguyên từ -32768..32767
 và các phép toán: +, -, *, /, %…
Kiểu Dữ Liệu Có Cấu Trúc
 “Kiểudữ liệu có cấu trúc là kiểu dữ liệu mà
các giá trị của nó là sự kết hợp của các giá trị
khác”.

 Ví dụ : Kiểu chuỗi ký tự trong C.


 là kiểu có cấu trúc.
 Ví dụ: char *chuoi = “Chao cac ban!”;
Ngôn Ngữ Lập Trình
 Kháiniệm về ngôn ngữ lập trình
 Chương trình dịch
Khái Niệm Về Ngôn Ngữ Lập
Trình
 Ngôn ngữ lập trình là một ngôn ngữ dùng để
viết chương trình cho máy tính
 Ta có thể chia ngôn ngữ lập trình thành các
loại sau:
 Ngôn ngữ máy
 Hợp ngữ
 Ngôn ngữ cấp cao
Ngôn Ngữ Máy (machine
language)
 Là các chỉ thị dưới dạng
nhị phân, can thiệp trực Machine Language
tiếp vào trong các mạch
điện tử. 10100110 01110110
00100110 00000000
 Có thể được thực hiện 11111010 11111010
01001110 10100110
ngay không cần qua 11100110 10010110
11001110 00101110
bước trung gian nào. 10100110 01001110
11111010 01100110

Tuy nhiên chương trình


01001110 10000110
 etc...

viết bằng ngôn ngữ máy 3

dễ sai sót, cồng kềnh và


khó đọc, khó hiểu vì toàn
những con số 0 và 1.
Hợp Ngữ (Assembly language)
 Bao gồm tên các câu lệnh và
quy tắc viết các câu lệnh đó.
Assembly Language
 Tên các câu lệnh bao gồm hai
phần: MOV AL,a ; chuyển giá trị a vào thanh ghi
 Phần mã lệnh (English) chỉ ; AL
phép toán cần thực hiện ADD AL,b ; Cộng giá trị của thanh ghi AL
 Phần địa chỉ chứa toán hạng ; với giá trị b
của phép toán đó.
 Để máy thực hiện được một
chương trình viết bằng hợp ngữ
thì chương trình đó phải được
dịch sang ngôn ngữ máy. Công
cụ thực hiện việc dịch đó được
gọi là Assembler.
Ngôn Ngữ Cấp Cao (High level
language )
 Rất gần với ngôn ngữ con người.

 Một chương trình viết bằng ngôn


ngữ cấp cao được gọi là chương
trình nguồn (source programs).

 Để máy tính "hiểu" và thực hiện


được các lệnh trong chương trình
nguồn thì phải có một chương
trình dịch để dịch chương trình
nguồn thành dạng chương trình
có khả năng thực thi.
Chương Trình Dịch
 Được dùng để chuyển một chương trình
nguồn sang chương trình đích.
 Có 2 dạng:
 Thông dịch (interpreter):
 Dịch từng lệnh một, dịch tới đâu thực hiện tới đó.
 Ví dụ: ngôn ngữ LISP.
 Biên dịch (compiler):
 Dịch toàn bộ chương trình nguồn thành chương trình
đích rồi sau đó mới thực hiện.
 Ví dụ: Pascal, C...
Hết chương
LẬP TRÌNH CĂN BẢN
Phần 2 - Chương 1
GIỚI THIỆU VỀ NGÔN NGỮ C
& MÔI TRƯỜNG TURBO C 3.0
PDNghiệp

30
Nội dung

 Tổng quan về ngôn ngữ lập trình C


 Lịch sử
 Những đặc điểm của C
 Môi trường lập trình Turbo C
Lịch Sử (1)
 Ngôn ngữ C được thiết kế bởi Dennis Ritchie ở
phòng thí nghiệm Bell vào đầu những năm 1970.
 Sự phát triển của C dựa trên các ngôn ngữ đã có:
 ALGOL 60 (1960),
 CPL (Cambridge, 1963),
 BCPL (Martin Richard, 1967),
 B (Ken Thompson, 1970)
 C là ngôn ngữ lập trình cấp cao, được sử dụng rất
phổ biến để lập trình hệ thống cùng với Assembler
và phát triển các ứng dụng, dù rằng đang có sự
chuyển đổi sang dùng C++.
 90% của UNIX được viết bằng C
Lịch Sử (2)

Dennis
Ritchie(trái) và
Ken Thompson
trước hệ thống
PDP-11 với 2
text-terminal
(1972)
Lịch Sử (3)
 Năm 1978, Dennis
Ritchie và Brian
Kernighan xuất bản 1
quyển sách mô tả ngôn
ngữ C.

Dennis Ritchie (trái) và Kernighan


Lịch Sử (4)
C được chuẩn hóa vào năm 1989 bởi ANSI
(American National Standards Institute),
được biết như ANSI C.
 Được chuẩn hóa ISO (International standard)
năm 1990 (chuẩn này cũng được ANSI chấp
nhận và được biết như C89)
 Được cập nhật năm 1995 (C95) và 1999
(C99).
Những đặc điểm của C (1)
 Tính cô đọng (compact):
 C chỉ có 32 từ khóa chuẩn và
 40 toán tử chuẩn.
 Tính cấu trúc (structured):
 C có một tập hợp những chỉ thị lập trình: cấu trúc lựa
chọn, lặp, …
 Đơn giản dễ hiểu.
 Tính tương thích (compatible):
 C có bộ tiền xử lý và
 Các thư viện chuẩn vô cùng phong phú
 Nên khi chuyển từ máy tính này sang máy tính khác các
chương trình viết bằng C vẫn hoàn toàn tương thích.
Những đặc điểm của C (2)
 Tính linh động (flexible):
 Cú pháp rất uyển chuyển, chấp nhận nhiều cách thể hiện
 Có thể thu gọn kích thước của mã lệnh
 Làm chương trình chạy nhanh hơn
 Biên dịch (compile):
 C cho phép biên dịch nhiều tập tin chương trình riêng rẽ
thành các tập tin đối tượng (object) và
 Liên kết (link) các đối tượng đó lại với nhau thành một
chương trình có thể thực thi được (executable) thống nhất
Môi trường lập trình Turbo C
 Turbo C do hãng Borland cung cấp.
 Có các chức năng: soạn thảo chương trình,
dịch, thực thi chương trình, …
 Phiên bản được sử dụng ở đây là Turbo C
3.0
Gọi Turbo C
Soạn thảo chương trình mới
 Vào menu File ->New

#include <stdio.h>
#include<conio.h>
int main ()
{
char ten[50];
printf(“Xin cho biet ten cua ban !”);
scanf(“%s”,ten);
printf(“Xin chao ban %s”,ten);
getch();
return 0;
Gõ chương trình vào
}
Ghi chương trình đang soạn
thảo vào đĩa
 Sử dụng File->Save hoặc gõ phím F2
 Lệnh Save As để lưu chương trình với tên khác

Tên hiện tại (tên


cũ)

Tên mới ( kể cả
tên thư mục)
Qui tắc đặt tên tập tin (file)
 Theo quy tắc đặt tên tập tin của DOS.
 Tên của tập tin gồm 2 phần: phần tên và phần mở rộng.
 Phần tên:
 Bắt đầu là 1 ký tự từ a..z (không phân biệt hoa thường).
 Theo sau có thể là các ký tự từ a..z, các ký số từ 0..9 hay dấu
gạch dưới (_), phần này dài tối đa là 8 ký tự.
 Phần mở rộng:
 Dài tối đa 3 ký tự.
 Ví dụ:
 Tên đúng: CHAO.C, baitap2.c, chao_ban.c
 Tên sai: 1CHAO.C, chao+ban.c
Thực hiện chương trình + Mở
một chương trình+ Thoát
 Thực hiện chương trình
 Nhấn Ctrl-F9 hoặc vào menu
Run->Run
 Mở một chương trình đã có
trên đĩa
 Vào menu File->Open hoặc
nhấn F3
 Thoát khỏi Turbo C
 Vào menu File->Exit hoặc
nhấn Alt-X
Các lệnh trên menu Option
 Directories:
 Include directories: chứa
các tập tin ta muốn đưa vào
chương trình (file .h trong
dòng #include).
 Library directories: chứa
các tập tin thư viện (file .lib)
 Output directory: chứa
các tập tin “đối tượng “ .obj
và .exe sau khi biên dịch
chương trình.
 Source directories: chứa
các tập tin “nguồn” (.obj và
.lib).
 Environment: dùng để
thiết lập môi trường làm
việc.
Hết chương
LẬP TRÌNH CĂN BẢN
Phần 2 - Chương 2
CÁC THÀNH PHẦN CƠ BẢN
CỦA NGÔN NGỮ C
P.D.Nghiệp
Nội dung chương này
 Bộ chữ viết trong C
 Các từ khóa

 Cặp dấu ghi chú thích

 Các kiểu dữ liệu sơ cấp chuẩn

 Tên và hằng

 Biến và biểu thức

 Cấu trúc của một chương trình C


Bộ chữ viết trong C
 Bộ chữ viết trong ngôn ngữ C bao gồm các
ký tự sau:
 26 chữ cái latinh lớn A,B,C...Z
 26 chữ cái latinh nhỏ a,b,c ...z.
 10 chữ số thập phân 0,1,2...9.
 Các ký hiệu toán học: +, -, *, /, =, <, >, (, )
 Các ký hiệu đặc biệt: :. , ; " ' _ @ # $ ! ^ [ ] { } ...
 Dấu cách hay khoảng trống.
 Phân biệt chữ in hoa và in thường
Các từ khóa trong C
 Từ khóa là các từ dành riêng của C.
 Ta không được dùng từ khóa để đặt cho các tên
của riêng mình.
Cặp dấu chú thích (comment)
#include <stdio.h>
#include<conio.h>
int main (){
char ten[50]; /* khai bao bien ten
kieu char 50 ky tu */
printf(“Xin cho biet ten cua ban !”);
scanf(“%s”,ten); /*Doc vao 1 chuoi la ten ban*/
printf(“Xin chao ban %s\n ”,ten);
//Dung chuong trinh, cho go phim
getch();
return 0;
}

 Khi biên dịch các phần chú thích bị bỏ qua


 Dùng /* và */: chú thích dài nhiều dòng
 Dùng //: chú thích chỉ 1 dòng
Các kiểu dữ liệu sơ cấp chuẩn
trong C
 Kiểu số nguyên (integer)
 Kiểu số thực (real)
Kiểu số nguyên
 Được dùng để lưu các giá trị nguyên hay còn gọi
là kiểu đếm được.
 Kiểu số nguyên 1 byte (8 bits)

 Kiểu số nguyên 2 bytes (16 bits)

 Kiểu số nguyên 4 byte (32 bits)


Kiểu số thực
 Được dùng để lưu các số thực hay các số có dấu
chấm thập phân

 Kiểu void
 Mang ý nghĩa là kiểu rỗng không chứa giá trị gì cả
 Ví dụ: void main(){
….}
Dùng sizeof()
 Kíchthước 1 kiểu có thể được xác định lúc
chạy chương trình (runtime), dùng sizeof:
 Ví dụ:
sizeof(double) =>8(byte)
sizeof(long double)=>10(byte)
Kiểu enum (1)
 enum gần giống với tiền xử lý #define.
 Nó cho phép ta định nghĩa 1 danh sách các bí danh
(aliase) để trình bày các số nguyên.
 Ví dụ:
#define MON 1
#define TUE 2
#define WED 3
có thể dùng enum:
enum week { Mon=1, Tue, Wed, Thu, Fri Sat, Sun}
days;
 Ưu điểm của enum so với #define là nó có phạm
vi (scope), nghĩa là 1 biến chỉ có tác dụng trong
khối (block) nó được khai báo.
Kiểu enum (2)
Kiểu enum (3)
Tên và hằng trong C
 Tên (identifier)
 Được dùng để đặt cho chương trình, hằng, kiểu,
biến, chương trình con, ...
 Có 2 loại:
 Tên chuẩn: là tên do C đặt sẵn như tên kiểu: int,
char, float,…; tên hàm: sin, cos...
 Tên do người lập trình tự đặt.
Chú ý khi đặt tên
Tên do người lập trình tự đặt
 Ví dụ:
 Tên đặt hợp lệ: Chieu_dai, Chieu_Rong, Chu_Vi
 Tên không hợp lệ: Do Dai, 12A2

 Phải tuân thủ quy tắc:


 Sử dụng bộ chữ cái, chữ số và dấu gạch dưới (_)
 Bắt đầu bằng một chữ cái hoặc dấu gạch dưới.
 Không có khoảng trống ở giữa tên.
 Không được trùng với từ khóa.
 Độ dài tối đa của tên là 32 ký tự, tuy nhiên cần đặt sao cho rõ
ràng, dễ nhận biết và dễ nhớ.
 Không cấm việc đặt tên trùng với tên chuẩn nhưng khi đó ý
nghĩa của tên chuẩn không còn giá trị nữa.
Hằng (Constant)
 Là đại lượng không đổi trong suốt quá trình thực thi
chương trình

=> không thể gán lạI giá trị cho hằng

 Hằng có thể là:


 1 con số
 1 ký tự
 1 chuỗi ký tự
Hằng số thực
 Giá trị kiểu: float, double, long double
 2 cách thể hiện
 Cách 1: viết thông thường
 Ví dụ: 123.34 -223.333 3.00 -56.0
 Cách 2: viết theo số mũ hay số khoa học
 Một số thực được tách làm 2 phần (phân cách
bởi e/E)
 Phần giá trị: như cách 1
 Phần mũ: là một số nguyên
 Ví dụ:
1234.56e-3 = 1.23456 (là số 1234.56*10-3)
-123.45E4 = -1234500 ( là -123.45*104)
Hằng số nguyên (1)
 Hằng số nguyên 2 byte (int) hệ thập phân
 Sử dụng 10 ký số 0..9
 Ví dụ:

123 (một trăm hai mươi ba)


-242 (trừ hai trăm bốn mươi hai)
 Hằng số nguyên 2 byte (int) hệ bát phân
 Sử dụng 8 ký số 0..7
 Cách biểu diễn: 0<các ký số từ 0 đến 7>
 Số bát phân : 0d d d …d d ( d có giá trị từ 0..7)
n n-1 n-2 1 0 i
n

=> giá trị: ∑ i


d
i =0
* 8 i

 Ví dụ:
020=2*81 + 0*80 =(16)10
Hằng số nguyên (2)
 Hằng số nguyên 2 byte (int) hệ thập lục phân
 Là kiểu số nguyên dùng:
 10 ký số 0..9 và
 6 ký tự A, B, C, D, E ,F
 Cách biểu diễn:
0x<các ký số từ 0 đến 9 và 6 ký tự từ A đến F>
 Số thập lục phân : 0xdndn-1dn-2…d1d0
n

=> Giá trị thập phân= ∑ i


d *
i =0
16 i

 Ví dụ:
0x345=3*162 + 4*161 + 5*160 = (837)10
0x2A9= 2*162 + 10*161 + 9*160= (681)10
Hằng số nguyên (3)
 Ví dụ: Kết quả của chương trình sau là gi?
Hằng số nguyên (4)
 Hằng số nguyên 4 byte (long)
 Được biểu diễn như số int trong hệ thập phân
nhưng kèm theo ký tự l hoặc L.
 Ví dụ:
45345L hay 45345l hay 45345
Hằng ký tự (char)
 Ví dụ: ‘a’, ‘A’, ‘0’, ‘9’
 Là 1 ký tự được viết trong
cặp dấu nháy đơn (‘).
 Mỗi một ký tự tương ứng
với 1 giá trị trong bảng mã
ASCII.
 Hằng ký tự cũng được xem
như trị số nguyên.
 Chúng ta có thể thực hiện
các phép toán số học trên 2
ký tự (dùng giá trị ASCII của
chúng)
 ASCII = American Standard
Code for Information
Interchange
Hằng chuỗi ký tự
 Ví dụ: “Ngon ngu lap trinh C”
 Là 1 chuỗi hay 1 xâu ký tự được đặt trong cặp dấu nháy
kép (“).

 Chú ý:
 “” : chuỗi rỗng - không có nội dung
 Khi lưu trữ trong bộ nhớ, một chuỗi được kết thúc bằng ký tự
NULL (‘\0’: mã Ascii là 0).
 Để biểu diễn ký tự đặc biệt bên trong chuỗi ta phải thêm dấu \ phía
trước.
 Ví dụ:
Viết “I\’m a student” cho “I’m a student”
Viết “Day la ky tu \“dac biet\”” cho “Day la ky tu “dac biet””
Biến và Biểu thức (variable
and expression)

 Biến dùng để chứa dữ liệu trong quá trình thực hiện


chương trình.
 Giá trị của biến có thể bị thay đổi.
 Cú pháp khai báo biến:
<Kiểu dữ liệu> Danh sách các tên biến cách nhau bởi dấu phẩy;
Khởi tạo giá trị cho biến lúc
khai báo
 Ví dụ:

 Cách viết giá trị cho biết luôn kiểu của nó:

 Chú ý: 8864L có kiểu long, còn 8864 có kiểu int


Vị trí khai báo biến (1)
 Biến ngoài
 Được đặt bên ngoài tất cả
các hàm
 Ảnh hưởng đến toàn bộ
chương trình (biến toàn
cục)
Vị trí khai báo biến (2)
 Biến trong
 Được đặt bên trong
hàm, chương trình
chính hay một khối
lệnh
 Ảnh hưởng đến hàm,
chương trình hay
khối lệnh chứa nó
(biến cục bộ).
Biểu thức (1)
 Ví dụ:
(-b + sqrt(Delta))/(2*a)
 Biểu thức là một sự kết hợp giữa
 Các toán tử (operator) và
 Các toán hạng (operand)
 Các loại toán tử trong C
 Toán tử số học
 Toán tử quan hệ và logic
 Toán tử Bitwise
 Toán tử ?
 Toán tử con trỏ & và *
 Toán tử dấu phẩy
Các toán tử số học (1)
Các toán tử số học (2)

• Tăng và giảm (++ & --)


++x hay x++ giống x = x + 1
--x hay x-- giống x = x – 1

• Tuy nhiên: • Còn:


x = 10; x = 10;
y = ++x; //y = 11, x=11 y = x++; //y = 10, x=11
Các toán tử số học (3)

 Đâu là sự khác nhau?


x++ trả về giá trị hiện hành của x và sau đó tăng x
++x tăng x trước và sau đó trả về giá trị mới của x
Biểu thức Boolean (boolean
expression)
 Chú ý! Không có kiểu Boolean rõ ràng trong C (điều này
đã được giới thiệu ở C99). Thay vào đó C dùng các giá
trị nguyên để tượng trưng cho giá trị Boolean, với qui
ước:
false Giá trị 0
true Bất kỳ giá trị nào ngoại trừ 0

 Chú ý! C dùng “=” cho phép gán, và dùng “==“ cho phép
so sánh. Nó trả về 1 nếu bằng và 0 nếu ngược lại
Các toán tử quan hệ và các toán
tử Logic (1)
 Các phép so sánh sau tạo ra các biểu thức logic có
giá trị kiểu Boolean
Các toán tử quan hệ và các toán
tử Logic (2)
 Ví dụ:

 Các biểu thức logic trả về


0 nếu false(sai)
1 nếu true(đúng)
Các toán tử quan hệ và các toán
tử Logic (3)
 Bảng chân trị cho các toán tử Logic

 Thứ tự ưu tiên

 Ví dụ: 10>5&&!(10<9)||3<=4 => đúng (1)


Các toán tử Bitwise
 Toán tử Bitwise giúp kiểm tra,
gán hay thay đổi các bit thật sự
trong 1 byte của word.
 Chỉ dùng cho kiểu char và int.
Toán tử ?
 Toán tử ? thực hiện như lệnh if-else.
 Cú pháp:
 E1 ? E2 : E3
 Ví dụ: X = (10 > 9) ? 100 : 200;
=>X=100

X = (10 >15 )? 100 : 200;


=>X=200
Toán tử con trỏ & và *
 Ví dụ:
int *p; //con tro so nguyen
int count=5, x;
p = &count;
=>Đặt vào biến m địa chỉ bộ nhớ của biến count

 Toán tử * trả về nội dung của ô nhớ mà một con trỏ


đang chỉ vào
 Ví dụ:
x = *p; // x=5
Toán tử dấu phẩy
 Ví dụ:
 x = (y=3,y+1);
 Trước hết gán 3 cho y rồi gán 4 cho x.

 Được sử dụng để kết hợp các biểu thức lại với


nhau.
 Bên trái của dấu (,) luôn được xem là kiểu void.
 Biểu thức bên phải trở thành giá trị của tổng các
biểu thức được phân cách bởi dấu phẩy.
Tổng kết về độ ưu tiên
 Tổng kết về độ ưu tiên
Phép gán được viết gọn lại
x= x <phép toán> y;
có thể được viết gọn lại (short form):
Các tập tin thư viện thông
dụng
 stdio.h: Định nghĩa các hàm vào/ra chuẩn (standard
input/output):printf(), scanf(), getc(), putc(), gets(), puts(), fflush(),
fopen(), fclose(), fread(), fwrite(), getchar(), putchar(), getw(),
putw()…
 conio.h: Định nghĩa các hàm vào ra trong chế độ DOS: clrscr(),
getch(), getche(), getpass(), cgets(), cputs(), putch(), clreol(),…
 math.h: Định nghĩa các hàm tính toán: abs(), sqrt(), log(). log10(),
sin(), cos(), tan(), acos(), asin(), atan(), pow(), exp(),…
 alloc.h: Định nghĩa các hàm liên quan đến việc quản lý bộ nhớ:
calloc(), realloc(), malloc(), free(), farmalloc(), farcalloc(), farfree(),

 io.h: Định nghĩa các hàm vào ra cấp thấp: open(), _open(), read(),
_read(), close(), _close(), creat(), _creat(), creatnew(), eof(),
filelength(), lock(),…
 graphics.h: Định nghĩa các hàm liên quan đến đồ họa: initgraph(),
line(), circle(), putpixel(), getpixel(), setcolor(), …
Cấu trúc của 1 chương trình C
(1)
 Cấu trúc một chương trình C
 Tiền xử lý và biên dịch
 Prototype
 Các tập tin thư viện thông dụng
Cấu trúc của 1 chương trình C
(2)

Các chỉ
thị tiền
Chương
xử lý
trình
chính
Định nghĩa
kiểu mới
Prototype

Khai báo
biến Cài đặt
ngoài các
hàm
Tiền xử lý và biên dịch (preprocess
and compile)

 Các chỉ thị định hướng (directive):


 #include…, #define…
 Có thể chứa các lệnh phức tạp như if-else.
 Bộ tiền xử lý (preprocessor) sẽ thông dịch các
directive và xóa bỏ nó trước khi cung cấp cho trình
biên dịch C.
#define
 #define khai báo một tên
macro (macro symbol).
 Sau đó, mỗi lần tên
macro này xuất hiện, nó
sẽ được thay thế bởi giá
trị của nó.
Chia chương trình ra các
module (1)
 1 chương trình phức tạp có thể được chia ra vài module
Chia chương trình ra các
module (2)
 Vấn đề: testmodule.c phải biết
các prototype của foor và bar.
 Giải pháp 1 (tệ):
 Chèn tay các prototype vào các file
.c có dùng nó.
 Bất lợi: Mỗi khi prototype bị thay
đổi => phải chỉnh lại prototype trong
tất cả các file .c dùng nó.
 Giải pháp 2 (tốt):
 Lưu các prototype vào 1 file riêng
biệt mymodule.h (h: header).
 Dùng #include mymodule.h ở đầu
các chương trình có dùng nó.
#include
 Với #include, bộ tiền xử lý sẽ thêm và thay thế token
#include filename bằng nội dung của filename.

 Các header file sẽ được tìm ở đâu?


 #include <file.h>: tìm file.h trong thư mục đã được xác định
trong INCLUDE DIRECTORIES. Hoặc trong /usr/include
(linux)
 #include “C:\\TC\\file.h”: tìm file.h trong đường dẫn
Header file
 Các header file có thể chứa:
 Prototype cho các hàm (function)
 Định nghĩa kiểu (structs, unions,
enums, typedefs)
 (Định nghĩa các class trong C++)
 #define macro
 #pragma cho compiler
 Các biến toàn cục
 Cài đặt trực tiếp các hàm
Hết chương
LẬP TRÌNH CĂN BẢN
Phần 2 - Chương 3
CÁC CÂU LỆNH ĐƠN
TRONG C
P.D.Nghiệp
Nội dung chương này
 Câu lệnh
 Khái niệm câu lệnh
 Phân loại
 Các lệnh đơn
 Lệnh gán
 Lệnh nhập giá trị từ bàn phím cho biến
 Lệnh xuất giá trị của biểu thức lên màn hình
Khái niệm câu lệnh
 “1 câu lệnh xác định 1 công việc mà chương
trình phải thực hiện”
 Kết thúc bởi ;
Phân loại
 Có 2 loại
 Lệnh đơn
 Không chứa 1 lệnh nào khác
 Gồm: lệnh gán, nhập, xuất
 Lệnh có cấu trúc
 Chứa các lệnh khác
 Gồm:
 cấu trúc điều kiện rẽ nhánh
 cấu trúc điều kiện lựa chọn
 cấu trúc lặp
 cấu trúc lệnh hợp thành
Các lệnh đơn
 Lệnh gán
 Lệnh nhập giá trị từ bàn phím cho biến

 Lệnh xuất giá trị của biểu thức lên màn hình
Lệnh gán (1)
 Ví dụ:

 Cú pháp:
<Tên biến> = <biểu thức>;
 Ý nghĩa: Gán giá trị cho 1 biến
 Gán giá trị ngay tại lúc khai báo:
Lệnh gán (2)
 Kiểu của biểu thức và của biến phải giống nhau

Error: "Cannot convert ‘char *’ to ‘int’"


Lệnh gán (3)
 Thường thì có sự chuyển đổi kiểu tự động nếu có thể.

Chuyển được
Lệnh gán (4)
 Kết quả chương trình sau là gì?
Lệnh gán (5)
 Trong C, các chuyển đổi kiểu sau được làm tự động.

 Những chuyển đổi trên đảm bảo không làm mất đi sự chính xác
(loss of precision).
 Việc chuyển đổi theo các hướng khác có thể làm mất sự chính xác
 Ví dụ:
Lệnh gán (6)
 Ép kiểu (casting type)
Lệnh nhập giá trị từ bàn phím
cho biến (1)
 scanf đọc dữ liệu từ bàn phím và gán vào biến

 Chuỗi định dạng (format string): để qui định kiểu dữ


liệu, cách biểu diễn, độ rộng, số chữ số thập phân, …
Lệnh nhập giá trị từ bàn phím
cho biến (2)
 scanf phải lưu giá trị vào 1 biến
 scanf(“%d”,anInt): không đúng, vì anInt xác định giá
trị hiện hành của 1 biến.
 scanf(“%d”,&anInt): đúng, vì địa chỉ của anInt đã
được xác định.
Ví dụ - Dùng Standard Input
Lệnh xuất giá trị của biểu thức
lên màn hình (1)

 Cần ít nhất 1 đối số.


 Đối số đầu tiên là 1 chuỗi
 Chuỗi có thể chứa:

 Ví dụ:

Output
Lệnh xuất giá trị của biểu thức
lên màn hình (2)
 Nếu muốn in ra các biến và biểu thức, ta truyền nó vào
printf như các đối số.
 Các định dạng (format) khác nhau cho các kiểu giá trị
khác nhau (dùng %).
Lệnh xuất giá trị của biểu thức
lên màn hình (3)
 Các định dạng:
Ví dụ - Output từ C
Hết chương
Giải thích thêm về printf
Hết chương
LẬP TRÌNH CĂN BẢN
Phần 2 - Chương 4
CÁC LỆNH CÓ CẤU TRÚC
P.D.Nghiệp
Nội dung chương này
 Khối lệnh trong C
 Cấu trúc rẽ nhánh

 Cấu trúc lựa chọn

 Cấu trúc vòng lặp

 Các câu lệnh “đặc biệt”


Khối lệnh trong C (1)
 Là 1 dãy các khai báo cùng với các câu lệnh nằm
trong cặp dấu ngoặc móc { và }.
Khối lệnh trong C (2)
1 khối lệnh có thể chứa nhiều khối lệnh khác gọi
là khối lệnh lồng nhau (không hạn chế).
Phạm vi các biến
 Có thể khai báo các biến cùng
tên trong các khối.
 Nếu một biến được khai báo bên
ngoài khối lệnh và không trùng
tên với biến bên trong khối lệnh
thì nó cũng dùng được bên trong
khối.
 Một khối lệnh con có thể sử
dụng các biến bên ngoài, nhưng
điều ngược lại không đúng.
Cấu trúc rẽ nhánh (if)

 Statement được thực hiện nếu boolean_expression có giá trị


đúng (true), !=0.

 Khối else là tùy chọn

boolean_expression !=0 => Statement1 được thực hiện


boolean_expression ==0 => Statement2 được thực hiện
Ví dụ - Lệnh if
#include <stdio.h>
#include <conio.h>
int main (){
float a;
printf("Nhap a = "); scanf("%f",&a);
if (a !=0 )
printf("Nghich dao cua %f la %f",a,1/a);
getch();
return 0;
}
Ví dụ - Lệnh if-else
#include <stdio.h>
#include <conio.h>
int main (){
float a;
printf("Nhap a = "); scanf("%f",&a);
if (a !=0 )
printf("Nghich dao cua %f la %f",a,1/a);
else
printf(“Khong the tim duoc nghich dao cua
a”);
getch();
return 0;
}
Câu lệnh và khối lệnh
 C cho phép nhóm các câu lệnh liên tiếp vào 1 khối.
 1 khối lệnh có thể được dùng như 1 lệnh đơn.
 Ví dụ:
Nhầm lẫn khi dùng if

 Chương trình trên sai ở đâu?


Chú ý khi dùng if-else
 Câu lệnh if-else lồng nhau
 else sẽ kết hợp với if gần nhất chứa có else
 Trong trường if bên trong không có else thì phải viết nó
trong cặp dấu {} để tránh sự kết hợp else if sai.
 Ví dụ
Cấu trúc lựa chọn
(switch-case) (1)
 C cung cấp 1 cấu trúc đẹp - dùng 1 dãy các câu lệnh if.
Cấu trúc lựa chọn
(switch-case) (2)
 switch-case có thể đưa đến mã máy (machine code) hiệu quả
hơn (vì jump tables có thể được dùng)
Cấu trúc lựa chọn
(switch-case) (3)
 Cú pháp:
 Tính giá trị của biểu thức expr trước.
 Nếu giá trị expr bằng value1 thì thực
hiện statement_sequence1 rồi thoát.
 Nếu giá trị expr khác value1 thì so
sánh nó với value2, nếu bằng value2
thì thực hiện statement_sequence2 rồi
thoát.
 Cứ như thế, so sánh tới giá trị n.
 Nếu tất cả các phép so sánh trên đều
sai thì thực hiện default_statements
của trường hợp default.
Cấu trúc lựa chọn
(switch-case) (4)
 Các chú ý:
 Kiểu của expr và các valuei phải là kiểu số nguyên
(int, chat, long, …).
 Nếu break/return vắng mặt, câu lệnh trong các case
bên dưới có thể được thực hiện cho đến khi gặp
break/return hoặc kết thúc lệnh switch.
Ví dụ - switch-case (1)
Ví dụ - switch-case (2)

In ra số ngày
của 1 tháng
Cấu trúc lặp
 Cho phép lặp lại thực hiện 1 công việc nhiều lần.
 Có 2 loại:
 Lặp với số lần xác định
 for

 Lặp với số lần không xác định


 while

 do-while
Vòng lặp for (1)
for (Biểu thức 1; biểu thức 2; biểu thức 3) <Công việc>;

 Thứ tự thực hiện:


B1: Tính giá trị biểu thức 1
B2: Tính giá trị biểu thức 2
 Nếu giá trị biểu thức 2 là
sai (==0) => thoát khỏi for
 Nếu giá trị biểu thức 2 là
đúng (!=0) => thực hiện
<Công việc>
B3: Tính giá trị biểu thức 3
rồi quay lại B2
Vòng lặp for (2)
Vòng lặp for (2)
 Chương trình in dãy số nguyên từ 1..10

output
Vòng lặp for (3)
 Nhập số nguyên n. Tính tổng các số nguyên từ 1..n.

output
Vòng lặp while (1)
while (Biểu thức điều kiện) <Công việc>;

 <Công việc>: có thể là 1 câu


lệnh hay 1 khối lệnh.
 Các bước thực hiện:
- Kiểm tra Biểu thức điều
kiện trước.
- Nếu điều kiện sai (==0) thì
thoát khỏi lệnh while.
- Nếu điều kiện đúng (!=0)
thì thực hiện công việc rồi
quay lại kiểm tra điều kiện
tiếp.
Vòng lặp while (2)
 Chương trình in dãy số nguyên từ 1..10

output
Vòng lặp while (3)
 Nhập số nguyên n. Tính tổng các số nguyên từ 1..n.

output
Vòng lặp do-while (1)
do
<Công việc>
while (<Biểu thức điều kiện>);

 <Công việc>: có thể là 1 câu lệnh


hay 1 khối lệnh.
 Các bước thực hiện:
 Công việc được thực hiện trước, sau đó
mới kiểm tra điều kiện.
 Nếu điều kiện sai thì thoát khỏi lệnh do-
while.
 Nếu điều kiện còn đúng thì thực hiện
công việc rồi quay lại kiểm tra điều kiện
tiếp.
Vòng lặp do-while (2)
 Chương trình in dãy số nguyên từ 1..10

output
Vòng lặp do-while (3)
 Nhập số nguyên n.
Tính tổng các số
nguyên từ 1..n.

output
So sánh các vòng lặp
 Vòng lặp for/while:
 Kiểm tra điều kiện trước thực hiện công việc sau.
 Công việc có thể không được thực hiện lần nào.
 Vòng lặp kết thúc khi nào điều kiện sai.
 Vòng lặp do-while
 Thực hiện công việc trước kiểm tra điều kiện sau.
 Công việc được thực hiện ít nhất 1 lần.
 Vòng lặp kết thúc khi nào điều kiện sai.
Câu lệnh đặc biệt
 Lệnh break
 Dùng để thoát khỏi vòng lặp hoặc switch-case.
 Tiếp tục thực hiện lệnh liền sau đó.

 Lệnh continue
 Trong vòng lặp, khi gặp lệnh continue, chương trình
sẽ bỏ qua các câu lệnh sau continue
 for: quay lên tính trị cho biểu thức 3, rồi kiểm tra điều
kiện coi có lặp tiếp không.
 while/do-while: kiểm tra điều kiện coi có lặp tiếp không.
Hết chương
LẬP TRÌNH CĂN BẢN
Phần 2 - Chương 5
CHƯƠNG TRÌNH CON
P.D.Nghiệp
Nội dung chương này
 Ví dụ
 Khái niệm về hàm trong C

 Xây dựng một hàm

 Truyền tham số cho hàm

 Hàm đệ qui
Ví dụ (1)
 In ra 50 ký tự ‘*’ và 50 ký tự ‘+’
Ví dụ (2)
 Đâu là ưu điểm của việc dùng hàm?
Khái niệm về hàm trong C (1)
 Để tránh rườm rà và mất thời gian khi viết chương trình,
những đoạn chương trình lặp đi lặp lại nhiều lần được viết
trong 1 module.
 Chia chương trình thành nhiều module, mỗi module giải quyết
1 công việc nào đó.
 Mỗi module như trên được gọi là 1 chương trình con.
 Các module dễ dàng được kiểm tra tính đúng đắn trước khi
được ráp nối vào chương trình.
Khái niệm về hàm trong C (2)
 Ví dụ: Tìm số lớn nhất trong 3 số a, b, và c.
Khái niệm về hàm trong C (3)
 Có 2 loại hàm:
 Hàm chuẩn
 Hàm tự định nghĩa
Hàm chuẩn (hàm thư viện)
 Được định nghĩa sẵn bởi ngôn ngữ lập trình và được chứa vào các thư
viện.
 Muốn sử dụng phải khai báo #include <tên thư viện.h>
 Một số thư viện thường dùng trong C:
 stdio.h : Thư viện chứa các hàm vào/ ra chuẩn (standard input/output):
printf(), scanf(), getc(), putc(), gets(), puts(), fflush(), fopen(), fclose(),
fread(), fwrite(), getchar(), putchar(), getw(), putw(), …
 conio.h : Thư viện chứa các hàm vào ra trong chế độ DOS (DOS console):
clrscr(), getch(), getche(), getpass(), cgets(), cputs(), putch(), clreol(), …
 math.h: Thư viện chứa các hàm tính toán: abs(), sqrt(), log(). log10(), sin(),
cos(), tan(), acos(), asin(), atan(), pow(), exp(), …
 alloc.h: Thư viện chứa các hàm liên quan đến việc quản lý bộ nhớ: calloc(),
realloc(), malloc(), free(), farmalloc(), farcalloc(), farfree(), …
 io.h: Thư viện chứa các hàm vào ra cấp thấp: open(), _open(), read(), _read(),
close(), _close(), creat(), _creat(), creatnew(), eof(), filelength(), lock(), …
 graphics.h: Thư viện chứa các hàm liên quan đến đồ họa:initgraph(), line(),
circle(), putpixel(), getpixel(), setcolor(), …
Hàm tự định nghĩa (hàm người
dùng) (1)
 Do người lập trình tự tạo ra nhằm đáp ứng nhu cầu xử lý của
mình.

 Cấu trúc của một hàm tự thiết kế:


Hàm tự định nghĩa (hàm người
dùng) (2)
 Cú pháp gọi hàm:<Tên hàm>([Danh sách các tham số])
 Ví dụ: Tìm UCLN của 2 số tự nhiên:
Nguyên tắc hoạt động của hàm
 Trongchương trình, khi gặp một lời gọi hàm thì
các bước sau được thực hiện:
 Nếu hàm có tham số, trước tiên các tham số sẽ được
gán giá trị thực tương ứng.
 Chương trình sẽ thực hiện tiếp các câu lệnh trong thân
hàm bắt đầu từ lệnh đầu tiên đến câu lệnh cuối cùng.
 Khi gặp lệnh return hoặc dấu } cuối cùng trong thân
hàm, chương trình sẽ thoát khỏi hàm để trở về chương
trình gọi nó.
 Thực hiện tiếp tục những câu lệnh của chương trình.
Truyền tham số cho hàm (1)
 Ví dụ: Hoán đổi nội dung của 2 biến
Truyền tham số cho hàm (2)
 Ta vẫn chưa hoán vị được!
 Tại sao?
 2 tham số a và b của hoanvi là tham số hình thức được truyền
bằng giá trị (tham trị).
1 tham trị được coi như 1 biến cục bộ của hàm,
chứa dữ liệu đầu vào cho hàm.
 Còn 2 tham số a,b của hoanvi trong lời gọi hàm trong main() là
tham số thực.
 Khi chương trình con được gọi để thi hành, tham trị được cấp ô
nhớ và nhận giá trị là bản sao giá trị của tham số thực.
 Do đó, mọi sự thay đổi trên tham trị không ảnh hưởng gì đến
tham số thực tương ứng.
Truyền tham số cho hàm (3)
 Hãy xem chương trình sau
Truyền tham số cho hàm (4)
 Tại sao ta đã hoán vị được?
 2 tham số a và b của hoanvi là tham số hình thức
được truyền bằng địa chỉ (tham biến) – con trỏ.
 Khi chương trình con (ctc) được gọi để thi hành, tham
biến chứa địa chỉ tham số thực, ô nhớ của tham số
thực được dùng trực tiếp trong ctc qua biến con trỏ.
 Do đó, mọi sự thay đổi trên tham biến đều ảnh
hưởng đến tham số thực tương ứng.
Hàm đệ quy
 Một hàm được gọi là đệ quy nếu bên trong thân
hàm có lệnh gọi đến chính nó.
 Ví dụ:
unsigned int giaithua_dequy(int n)
1 nếu n=0 {
n!=
n*(n-1)! nếu n#0 if (n==0)
return 1;
else
return n*giaithua_dequy(n-1);
}
Đặc điểm cần lưu ý khi viết hàm
đệ quy
 Hàm đệ quy phải có 2 phần:

 Phần dừng: là trường hợp nguyên tố.


 Ví dụ: n=0 trong tính n!

 Phần đệ quy: là phần có gọi lại hàm đang được định


nghĩa.
 Ví dụ: nếu n>0 thì n! = n * (n-1)!
Ưu và khuyết điểm của đệ quy
 Làm chương trình dễ đọc, dễ hiểu và vấn đề được nêu bật rõ
ràng hơn.
 Đệ quy tốn bộ nhớ nhiều hơn và tốc độ thực hiện chương trình
chậm hơn không đệ quy.
 Tùy từng bài cụ thể mà ta quyết định có nên dùng đệ quy hay
không.
 Có những trường hợp không dùng đệ quy thì không giải quyết
được bài toán.
Hết chương

Hết chương
LẬP TRÌNH CĂN BẢN
Phần 2 - Chương 6
KIỂU MẢNG
P.D.Nghiệp
Nội dung chương này
 Giới
thiệu kiểu mảng trong C
 Mảng 1 chiều

 Mảng nhiều chiều


Giới thiệu kiểu mảng trong C (1)
 Ví dụ:
int a[10];

=> Hình ảnh của a trong bộ nhớ như sau:


Giới thiệu kiểu mảng trong C (2)
 “Mảng là một tập hợp các phần tử cố định có cùng một kiểu,
gọi là kiểu phần tử”.

 Kiểu phần tử có thể là có kiểu bất kỳ:


 ký tự
 số
 1 struct
 1 mảng khác (=> mảng của mảng hay mảng nhiều chiều)
 …;
Giới thiệu kiểu mảng trong C (3)
 Ví dụ: Lưu trữ 1 đa giác trong đồ họa:

typedef struct { Points[1]


int x;
int y; Points[0]
} Point; Points[2]

typedef struct{
Point Points[100]; Points[3]

int nPoints;
} Polygon;
Giới thiệu kiểu mảng trong C (4)
 Ta có thể chia mảng làm 2 loại:
 Mảng 1 chiều
 Mảng nhiều chiều
Mảng 1 chiều (1)
 Xét dưới góc độ toán học, mảng 1 chiều giống như một vector.
 Mỗi phần tử của mảng 1 chiều có giá trị không phải là một
mảng khác.
 Khai báo mảng với số phần tử xác định
 Ví dụ: float a[100];
 Cú pháp: <Kiểu> <Tên mảng ><[số phần tử]>;

 Khai báo mảng với số phần tử không xác định


 Ví dụ: float a[];
 Cú pháp: <Kiểu> <Tên mảng> <[]> ;
Mảng 1 chiều (2)
 Vừa khai báo vừa gán giá trị
<Kiểu> <Tên mảng> []= {Các giá trị cách nhau bởi dấu phẩy} ;
=> Số phần tử có thể được xác định bằng sizeof()
Số phần tử=sizeof(tên mảng)/sizeof(kiểu)

 Khai báo mảng là tham số hình thức của hàm


 không cần chỉ định số phần tử của mảng là bao nhiêu
Mảng 1 chiều (3)
 Ví dụ: Gán giá trị ngay lúc khai báo
int primes[] = {2,3,5,7,11,13};
Sẽ tương đương với:
int primes[6];
primes[0] = 2;
primes[1] = 3;
primes[2] = 5; =>sizeof(primes)/sizeof(int)=6
primes[3] = 7;
primes[4] = 11;
primes[5] = 13;
Truy xuất từng phần tử của mảng (1)
 Cú pháp:
Tên biến mảng[Chỉ số]

 Ví dụ 1:
int a[10];
a[0]=5; a[1]=5; a[2]=33; a[3]=33; a[4]=15;
printf(“%d %d %d %d %d”, a[0], a[1], a[2], a[3], a[4]);
Truy xuất từng phần tử của mảng (2)
 Ví dụ 2: Vừa khai báo vừa gán trị cho 1 mảng 1 chiều các số
nguyên. In mảng số nguyên này lên màn hình.
Truy xuất từng phần tử của mảng (3)
 Ví dụ 3: Đổi một số nguyên dương thập phân thành số nhị phân.
Truy xuất từng phần tử của mảng (4)
 Ví dụ 4: Nhập vào một dãy n số và sắp xếp các số theo thứ tự tăng.
Truy xuất từng phần tử của mảng (5)
 Ví dụ 5: Chương trình sau sẽ hiển thị kết quả gì?

Sửa lỗi này thế nào?

Các phần tử của mảng a[0], …, a[11]. Việc truy cập a[12] sẽ vượt ra
bên ngoài mảng, ô nhớ của biến b.
Mảng nhiều chiều
 Mảng nhiều chiều là mảng có từ 2 chiều trở lên.
 Điều đó có nghĩa là mỗi phần tử của mảng là một mảng khác.
 Người ta thường sử dụng mảng nhiều chiều để lưu các ma
trận, các tọa độ 2 chiều, 3 chiều…
Khai báo mảng 2 chiều tường minh
 Cú pháp:
<Kiểu> <Tên mảng><[Số phần tử chiều 1]><[Số phần tử chiều 2]> ;
 Ví dụ:
float m[8][9]; // mảng 2 chiều có 8*9 phần tử là số thực
Khai báo mảng 2 chiều không tường minh
 Để khai báo mảng 2 chiều không tường minh, ta vẫn phải chỉ
ra số phần tử của chiều thứ hai (chiều cuối cùng).

 Cú pháp:
<Kiểu> <Tên mảng> <[]><[Số phần tử chiều 2]>;
 Ví dụ:
float m[][9];

 Cách khai báo này cũng được áp dụng trong trường hợp:
 vừa khai báo vừa gán trị
 mảng 2 chiều là tham số hình thức của 1 hàm.
Truy xuất từng phần tử của mảng 2 chiều
 Dùng:
Tên mảng[Chỉ số 1][Chỉ số 2]
Ví dụ (1)
 Viết chương trình cho phép nhập 2 ma trận a, b có m dòng n cột, thực hiện
phép toán cộng hai ma trận a,b và in ma trận kết quả lên màn hình.
Ví dụ (2)
Hết chương

Hết chương
LẬP TRÌNH CĂN BẢN
Phần 2 - Chương 7
KIỂU CON TRỎ
P.D.Nghiệp
Nội dung chương này
 Giới thiệu kiểu dữ liệu con trỏ
 Khai báo và sử dụng biến con trỏ

 Con trỏ và mảng

 Con trỏ và tham số hình thức của hàm


Giới thiệu kiểu dữ liệu con trỏ
(pointer) (1)
 1 con trỏ là 1 biến được dùng để chứa địa chỉ của ô nhớ
trong bộ nhớ.
 Kích thước của biến con trỏ luôn là 2 byte.

 Ví dụ 1:
float a=3.145;
float* ptr; //ptr is a pointer
ptr=&a;

pointer ptr variable a

FFF2 3.145
FFF2 (address)
Giới thiệu kiểu dữ liệu con trỏ (2)
 Ví dụ 2:
In Linux
Khai báo và sử dụng biến con trỏ
 Khai báo biến con trỏ
 Các thao tác trên con trỏ
Khai báo biến con trỏ
int a, b, *pa, *pb; //pa và pb sẽ chỉ đến biến int
float f, *pf; //pa và pb sẽ chỉ đến biến float
void *ptr; //ptr sẽ chỉ vào bất kỳ biến kiểu gì
 Cú pháp:
<Kiểu> *<Tên con trỏ>;

 Ý nghĩa: Khai báo một biến có tên là Tên con trỏ dùng để
chứa địa chỉ của các biến có kiểu Kiểu.
Các thao tác trên con trỏ
 Gán địa chỉ của biến cho biến con trỏ
 Nội dung của ô nhớ con trỏ chỉ tới
 Cấp phát vùng nhớ cho biến con trỏ
 Cấp phát lại vùng nhớ cho biến con trỏ
 Giải phóng vùng nhớ cho biến con trỏ
 Một số phép toán trên con trỏ
Gán địa chỉ của biến cho biến con trỏ +
Nội dung của ô nhớ con trỏ chỉ tới (1)
 Dùng & để lấy ra địa chỉ bộ nhớ (memory address) của 1 biến
int a=6;
int* c= &a; // &a là địa chỉ bộ nhớ của biến a
 Dùng * để truy cập (access) đến nội dung (content) của biến
mà 1 con trỏ đang chỉ đến
int a=6;
int* c= &a;
*c=7; /*Thay đổi nội dung của biến a bằng cách
dùng địa chỉ của nó được chứa trong
con trỏ c*/
tương đương với
a=7;
Gán địa chỉ của biến cho biến con trỏ +
Nội dung của ô nhớ con trỏ chỉ tới (2)
Gán địa chỉ của biến cho biến con trỏ +
Nội dung của ô nhớ con trỏ chỉ tới (3)
 Lưu ý:

=> Error!
Vì đã cho 1 con trỏ chỉ đến 1 biến khác kiểu với nó
Cấp phát vùng nhớ cho biến con trỏ(1)
 Có 2 cách để dùng được biến con trỏ
1. Cho nó chứa địa chỉ của 1 vùng nhớ đang tồn tại
int a=6;
int* c= &a; // &a là địa chỉ bộ nhớ của biến a
1. Cấp phát 1 vùng nhớ mới, rồi cho con trỏ chỉ đến
int* ptr;
ptr = (int*)malloc(sizeof(int));
*ptr=6;
pointer ptr

FFFA 6
FFFA (address)
Cấp phát vùng nhớ cho biến con trỏ(2)
 void *malloc(size_t size): Cấp phát vùng nhớ có kích thước là
size (byte)
 void *calloc(size_t nitems, size_t size): Cấp phát vùng nhớ có
kích thước là nitems*size (byte)

 Ví dụ:
int a, *pa, *pb;
pa = (int*)malloc(sizeof(int)); /* Cấp phát vùng nhớ có kích thước
bằng với kích thước của một số nguyên */
pb= (int*)calloc(10, sizeof(int)); /* Cấp phát vùng nhớ có thể chứa
được 10 số nguyên*/
Cấp phát lại vùng nhớ
cho biến con trỏ
int a, *pa;
pa = (int*)malloc(sizeof(int)); /*Cấp phát vùng nhớ có
kích thước 2 byte*/
pa = realloc(pa, 6); /*Cấp phát lại vùng nhớ có
kích thước mới là 6 byte*/
 void *realloc(void *block, size_t size):
 Ý nghĩa:
 Cấp phát lại 1 vùng nhớ do con trỏ block quản lý, vùng nhớ này có
kích thước mới là size; khi cấp phát lại thì nội dung vùng nhớ trước
đó được copy đến vùng nhớ mới.
 Kết quả trả về của hàm là địa chỉ đầu tiên của vùng nhớ mới. Địa chỉ
này có thể khác với địa chỉ được chỉ ra khi cấp phát ban đầu. Kết quả
là NULL nếu không cấp phát được.
Giải phóng vùng nhớ cho biến con trỏ
 void
free(void *block): Giải phóng vùng nhớ
được quản lý bởi con trỏ block

 Ví dụ
free(pa);
free(pb);
=> giải phóng vùng nhớ do
2 biến con trỏ pa & pb đang chỉ đến
Một số phép toán trên con trỏ
 Phép gán =
 Phép so sánh == và !=

 Cộng, trừ con trỏ với 1 số nguyên

 Gán NULL cho 1 con trỏ


Phép gán và phép so sánh
 Ví dụ: Hiện tại ta có: p a
int a=10, b=15; FFF2 10
int *p, *q; FFF2 (address)
float *f; q b
p=&a; q=&b; FFFA 15
Bây giờ thì phép so sánh: FFFA (address)
(p!=q) => true (1)
p a
Thực hiện tiếp lệnh gán: FFFA 10
p=q;
FFF2 (address)
Bây giờ thì: q b
(p==q) => true (1) FFFA 15
Lệnh f=p; =>Error, do khác kiểu FFFA (address)
Nhưng lệnh f=(float*)p; =>No error
Cộng, trừ con trỏ với 1 số
nguyên
 Ta có thể cộng (+), trừ (-) 1 con trỏ với 1 số nguyên N nào đó
 Kết quả trả về là 1 con trỏ. Con trỏ này chỉ đến vùng nhớ cách
vùng nhớ của con trỏ hiện tại N phần tử.
 Ví dụ: Cho đoạn chương trình sau:
int *pa;
int *pb, *pc;
pa = (int*) malloc(20); /*Cấp phát vùng nhớ 20 byte=10 số nguyên*/
pb = pa + 7;
pc = pb - 3;
 Lúc này hình ảnh của pa, pb, pc như sau:
Gán NULL cho 1 con trỏ
 Ví dụ:
int x=25;
int *ptr;
ptr=&x;
ptr=NULL;

 Lệnh gán ptr=NULL => cho con trỏ ptr không


trỏ vào (không chứa địa chỉ) vùng nhớ nào cả
Con trỏ và mảng (1)
 Mảng và con trỏ có mối liên hệ với nhau
 Thay vì truy cập 1 phần tử mảng bằng chỉ số của nó, ta có thể dùng
1 con trỏ

 Để truy cập phần tử thứ i, a[i]:


y= *(pa+i);
 Chú ý: pa+1 không phải cộng 1 vào pa. Thay vào đó, địa chỉ là
pa+sizeof(*pa)
Con trỏ và mảng (2)
 1 mảng có thể được xem như một con trỏ. Con trỏ này
đang chỉ đến phần tử đầu tiên của mảng. Do đó:

 1 mảng có thể được dùng làm


tham số cho 1 hàm
Con trỏ và mảng (3)
 x sẽ có giá trị của a[i] nào, tại mỗi lệnh gán sau?
Con trỏ và tham số hình thức của hàm (1)
 Ví dụ 1: Viết 1 hàm để tăng giá trị của biến lên 1
Con trỏ và tham số hình thức của hàm (2)

 Đâu là sự khác nhau giữa tham số a và b?


Con trỏ và tham số hình thức của hàm (3)
 Ví dụ 2: Viết hàm hoán đổi nội dung 2 biến
Con trỏ và tham số hình thức của hàm (4)
 Ví dụ 3: Viết lại hàm hoán vị như sau:
Con trỏ và tham số hình thức của hàm (5)
 Tại sao hàm trên có thể hoán vị được?
Hết chương

Hết chương
LẬP TRÌNH CĂN BẢN
Phần 2 - Chương 8
CHUỖI KÝ TỰ
P.D.Nghiệp
Nội dung chương này
 Khái niệm
 Khai báo

 Các thao tác trên chuỗi ký tự


Khái niệm
 Chuỗi ký tự là một dãy gồm các ký tự hoặc một mảng
các ký tự được kết thúc bằng ký tự ‘\0’ (ký tự NULL
trong bảng mã Ascii).
 Các hằng chuỗi ký tự được đặt trong cặp dấu nháy
kép “”.
Khai báo
 Khai báo theo mảng
 Khai báo theo con trỏ

 Vừa khai báo vừa gán giá trị


Khai báo theo mảng
 Cú pháp:
char <Biến> [Chiều dài tối đa];
 Ví dụ: char Ten[12];
=> bộ nhớ sẽ cung cấp 12+1 bytes để lưu trữ
nội dung của chuỗi ký tự Ten;
byte cuối cùng lưu trữ ký tự ‘\0’ để chấm dứt chuỗi
Ten: ‘\0’

Ten[0] Ten[12]
 Ghi chú:
 Chiều dài tối đa của biến chuỗi: 1..255 bytes.
 Không nên khai báo thừa để tránh lãng phí bộ nhớ.
Khai báo theo con trỏ
 Cú pháp: char *<Biến>;
 Ví dụ: char *Ten;
 Trong khai báo này, bộ nhớ sẽ dành 2 byte để lưu trữ địa chỉ
của biến con trỏ Ten đang chỉ đến.
 Chưa cung cấp nơi để lưu trữ dữ liệu.
 Muốn có chỗ để lưu trữ dữ liệu, ta phải gọi đến hàm malloc()
hoặc calloc() có trong “alloc.h”, sau đó mới gán dữ liệu cho
biến.

Tieu Dong Tu
Vừa khai báo vừa gán giá trị
 Cú pháp:
char <Biến>[]=<”Hằng chuỗi”>;
 Ví dụ:

Vua khai bao vua gan trị : Mau nang hay la mau mat em

 Ghi chú: Chuỗi được khai báo là một mảng các ký tự nên các thao
tác trên mảng có thể áp dụng đối với chuỗi ký tự.
Các thao tác trên chuỗi ký tự
 Nhập xuất chuỗi
 Nhập chuỗi từ bàn phím
 Xuất chuỗi lên màn hình
 Một số hàm xử lý chuỗi (trong string.h)
Nhập chuỗi từ bàn phím
 Dùng hàm gets()
 Cú pháp:
gets(<Biến chuỗi>)
 Ví dụ:
char Ten[20];
gets(Ten);

 Ta cũng có thể sử dụng hàm scanf() để nhập dữ liệu cho biến


chuỗi, tuy nhiên lúc này ta chỉ có thể nhập được một chuỗi
không có dấu khoảng trắng.
 Dùng hàm cgets() (trong conio.h)
Xuất chuỗi lên màn hình
 Để xuất một chuỗi (biểu thức chuỗi) lên màn hình, ta sử dụng hàm puts().
 Cú pháp: puts(<Biểu thức chuỗi>)
 Ví dụ: Nhập vào một chuỗi và hiển thị trên màn hình chuỗi vừa nhập:

 Ngoài ra, ta có thể sử dụng hàm printf(), cputs() (trong conio.h) để hiển thị
chuỗi lên màn hình.
Một số hàm xử lý chuỗi (trong string.h)
 Cộng chuỗi - Hàm strcat()
 Xác định độ dài chuỗi - Hàm strlen()
 Đổi một ký tự thường thành ký tự hoa - Hàm toupper()
 Đổi chuỗi chữ thường thành chuỗi chữ hoa, hàm strupr()
 Đổi chuỗi chữ hoa thành chuỗi chữ thường, hàm strlwr()
 Sao chép một phần chuỗi, hàm strncpy()
 Trích một phần chuỗi, hàm strchr()
 Tìm kiếm nội dung chuỗi, hàm strstr()
 So sánh chuỗi, hàm strcmp()
 So sánh chuỗi, hàm stricmp()
 Khởi tạo chuỗi, hàm memset()
 Đổi từ chuỗi ra số, hàm atoi(), atof(), atol() (trong stdlib.h)
Cộng chuỗi - strcat() (1)
 Cú pháp:
char *strcat(char *des, const char *source)

 Hàm này có tác dụng ghép chuỗi nguồn (source) vào chuỗi
đích (des).
 Trả vể con trỏ chỉ đến chuỗi kết quả.
Cộng chuỗi - strcat() (2)
 Vídụ: Nhập vào họ lót và tên của một người, sau
đó in cả họ và tên của họ lên màn hình.
Xác định độ dài chuỗi - strlen()
 Cú pháp: int strlen(const char* s)
 Ví dụ: Xác định độ dài 1 chuỗi nhập từ bàn phím.
Đổi một ký tự thường thành ký tự
hoa - toupper()
 Cú pháp:
char toupper(char c)

 Hàm này (trong ctype.h) được dùng để chuyển


đổi 1 ký tự thường thành ký tự hoa.
Đổi chuỗi chữ thường thành chuỗi
chữ hoa - strupr() (1)
 Cú pháp:
char *strupr(char *s)

 Hàm này được dùng để chuyển đổi chuỗi chữ thường thành
chuỗi chữ hoa.
 Kết quả trả về là 1 con trỏ chỉ đến chuỗi kết quả.
Đổi chuỗi chữ thường thành chuỗi
chữ hoa - strupr() (2)
 Ví dụ: Nhập vào một chuỗi ký tự từ bàn phím. Sau đó sử dụng
hàm strupr() để chuyển đổi chúng thành chuỗi chữ hoa.
Đổi chuỗi chữ hoa thành chuỗi
chữ thường - strlwr()
 Cú pháp:
char *strlwr(char *s)

 Hàm này được dùng để chuyển đổi chuỗi chữ hoa thành chuỗi
chữ thường.
 Kết quả trả về là 1 con trỏ chỉ đến chuỗi kết quả.
Sao chép chuỗi - strcpy() (1)
 Cú pháp:
char *strcpy(char *Des, const char *Source)

 Hàm này được dùng để sao chép toàn bộ nội dung của chuỗi
nguồn vào chuỗi đích.
Sao chép chuỗi - strcpy() (2)
 Ví dụ: Viết chương trình cho phép chép toàn bộ chuỗi nguồn
vào chuỗi đích
Sao chép một phần chuỗi - strncpy()
và Trích một phần chuỗi - strchr()
 Sao chép một phần chuỗi
 Cú pháp:
char *strncpy(char *Des, const char *Source, size_t n)
 Chép n ký tự đầu tiên của chuỗi nguồn sang chuỗi đích.

 Trích một phần chuỗi, hàm strchr()


 Cú pháp :
char *strchr(const char *str, int c)
 Trích ra chuỗi con của str bắt đầu từ ký tự c cho đến hết chuỗi.
 Ghi chú:
 Nếu ký tự c không có trong chuỗi, kết quả trả về là NULL.
 Kết quả trả về của hàm là một con trỏ, con trỏ này chỉ đến ký tự c
đầu tiên trong chuỗi str.
Tìm kiếm nội dung chuỗi - strstr()
(1)
 Cú pháp:
char *strstr(const char *s1, const char *s2)

 Hàm này được dùng để tìm kiếm sự xuất hiện đầu


tiên của chuỗi s2 trong chuỗi s1.
 Kết quả trả là 1 con trỏ chỉ đến phần tử đầu tiên của
chuỗi s1 có chứa chuỗi s2 hoặc giá trị NULL nếu
chuỗi s2 không có trong chuỗi s1.
Tìm kiếm nội dung chuỗi - strstr()
(2)
 Ví dụ: Viết chương trình sử dụng hàm strstr() để lấy ra một
phần của chuỗi gốc bắt đầu từ chuỗi “hoc”.
So sánh chuỗi - strcmp() (1)
 Cú pháp:
int strcmp(const char *s1, const char *s2)

 So sánh 2 chuỗi s1 và s2 với nhau.


 Kết quả trả về là 1 số int:
 <0 nếu s1 < s2
 ==0 nếu s1==s2
 >0 nếu s1 > s2
 Tương tự:
int stricmp(const char *s1, const char *s2)
 So sánh không phân biệt kí tự hoa/thường
So sánh chuỗi - strcmp() (2)
 Ví dụ:
#include <conio.h> #include <conio.h>
#include <stdio.h> #include <stdio.h>
#inlude <string.h> #inlude <string.h>
void main(){ void main(){
char s1[10]=“Chao”, s2[10]=“chao”; char s1[10]=“chao”, s2[10]=“chao”;
printf(“%d”,strcmp(s1,s2)); printf(“%d”,strcmp(s1,s2));
getch(); getch();
} }

-32 0
Khởi tạo chuỗi - memset()
 Cú pháp: void *memset(char *Des, int c, size_t n)
 Đặt n ký tự đầu tiên của chuỗi Des là ký tự c.
 Giá trị trả về: chuỗi Des.
 Nằm trong thư viện: string.h và mem.h
Đổi từ chuỗi ra số - atoi(), atof(),
atol() (trong stdlib.h)
 Cú pháp :
int atoi(const char *s) : chuyển chuỗi thành số nguyên
long atol(const char *s) : chuyển chuỗi thành số nguyên dài
float atof(const char *s) : chuyển chuỗi thành số thực
 Nếu chuyển đổi không thành công, kết quả trả về
của các hàm là 0.
 Ví dụ:
atoi(“1234”)=> 1234
Hết chương
LẬP TRÌNH CĂN BẢN
Phần 2 - Chương 9
KIỂU CẤU TRÚC
P.D.Nghiệp
Nội dung chương này
 Kiểu cấu trúc trong C
 Các thao tác trên biến kiểu cấu trúc

 Con trỏ và cấu trúc


Kiểu cấu trúc trong C
 Khái niệm
 Định nghĩa kiểu cấu trúc

 Khai báo biến cấu trúc


Khái niệm
 Kiểu cấu trúc (struct) là kiểu dữ liệu bao gồm nhiều thành
phần có kiểu khác nhau, mỗi thành phần được gọi là một
trường (field)
 Nó khác với kiểu mảng (nơi mà các phần tử có cùng kiểu)
 Ví dụ:

1 struct:

1 mảng:
Định nghĩa kiểu cấu trúc +
Khai báo biến cấu trúc (1)
 Cách 1:
struct <Tên cấu trúc> { struct SinhVien{
<Kiểu> <Trường 1>; char MSSV[10];
char HoTen[40];
<Kiểu> <Trường 2>; struct NgayThang
…….. NgaySinh;
<Kiểu> <Trường n>; int Phai;
char DiaChi[40];
} [biến 1, biến 2]; };

Ví dụ: ⇒Khaibáo biến:


struct NgayThang{
struct NgayThang NgaySinh;
unsigned char Ngay;
struct SinhVien SV;
unsigned char Thang;
unsigned int Nam; struct <Tên cấu trúc> tên_biến;
};
Định nghĩa kiểu cấu trúc +
Khai báo biến cấu trúc (2)
 Chú ý:
 struct không tên:
A và B là các struct có 2 thành
phần x và y.
struct này không có tên, nên ngoài
A và B, ta không thể định nghĩa
thêm các biến khác được.

 Tuy nhiên
A và B là các biến có kiểu
struct point.
Sau này ta có thể khai báo thêm
các biến khác có kiểu struct point
này.
Định nghĩa kiểu cấu trúc +
Khai báo biến cấu trúc (3)
 Cách 2:
typedef struct { typedef struct{
<Kiểu> <Trường 1> ; char MSSV[10];
char HoTen[40];
<Kiểu> <Trường 2> ; NgayThang NgaySinh;
…….. int Phai;
<Kiểu> <Trường n> ; char DiaChi[40];
} SinhVien;
} <Tên cấu trúc>;
⇒Khai báo biến:
Ví dụ:
typedef struct{
unsigned char Ngay; NgayThang NgaySinh;
unsigned char Thang; SinhVien SV;
unsigned int Nam;
<Tên cấu trúc> tên_biến;
} NgayThang;
Các thao tác trên biến kiểu cấu
trúc
 Truy xuất đến từng trường của biến cấu trúc
 Khởi tạo cấu trúc
Truy xuất đến từng trường (field)
của biến cấu trúc (1)
 Cú pháp: <Biến cấu trúc>.<Tên trường>

 Ví dụ 1: Chương trình cho phép đọc dữ liệu từ bàn phím cho


biến mẩu tin SinhVien và in biến mẩu tin đó lên màn hình:
Truy xuất đến từng trường (field)
của biến cấu trúc (2)
Truy xuất đến từng trường (field)
của biến cấu trúc (3)
 Kết quả của 1 lần nhập:
Truy xuất đến từng trường (field)
của biến cấu trúc (4)
 Lưu ý:
 Các biến cấu trúc có thể gán cho nhau
 Ví dụ:

s=SV; // gán để lấy giá trị toàn bộ cấu trúc

 Ta không thể thực hiện được các thao tác sau đây cho biến
cấu trúc:
 Sử dụng các hàm xuất nhập trên biến cấu trúc
 Các phép toán quan hệ, các phép toán số học và logic
Khởi tạo cấu trúc
 Biếncấu trúc có thể được khởi tạo giá trị ban
đầu lúc khai báo

 Ví dụ:

struct NgayThang NgaySinh ={29, 8, 1986};


Con trỏ và cấu trúc
 Khai báo
 Sử dụng các con trỏ kiểu cấu trúc

 Truy cập các thành phần của cấu trúc đang được
quản lý bởi con trỏ
Khai báo (1)
 Cú pháp:
struct <Tên cấu trúc> * <Tên biến con trỏ>;

 Ví dụ 1:
struct NgayThang *p;
hoặc
NgayThang *p; // Nếu có dùng typedef
Khai báo (2)
 Ví dụ 2:

 Truy cập đến các trường:

 Nếu dùng con trỏ thì:


Sử dụng các con trỏ kiểu cấu trúc
 Có 2 cách:
 Phải cấp phát bộ nhớ cho nó
 Cho nó chỉ vào (chứa địa chỉ) biến đang tồn tại

 Ví dụ:
struct NgayThang *p;

p=(struct NgayThang *)malloc(sizeof(struct NgayThang));
p->Ngay=29; p->Thang=8; p->Nam=1986;
Hoặc
struct NgayThang Ngay = {29,8,1986};
p = &Ngay;
Truy cập các thành phần của cấu trúc
đang được quản lý bởi con trỏ (1)
 Với khai báo sau:
struct NgayThang *p;

 Ta có thể truy cập đến các trường của nó như sau:

p->Ngay p->Thang
hoặc
(*p).Ngay (*p).Thang
Truy cập các thành phần của cấu trúc
đang được quản lý bởi con trỏ (3)
 Ví dụ
Hết chương
LẬP TRÌNH CĂN BẢN
Phần 2 - Chương 10
KIỂU TẬP TIN
P.D.Nghiệp
Nội dung chương này
 Một số khái niệm về tập tin
 Các thao tác trên tập tin

 Truy cập tập tin văn bản

 Truy cập tập tin nhị phân


Một số khái niệm về tập tin (file) (1)
 Tại sao ta cần đến kiểu tập tin?
 Cho phép lưu trữ dữ liệu ở bộ nhớ ngoài (đĩa).

 Khi kết thúc chương trình thì dữ liệu vẫn còn do


đó chúng ta có thể sử dụng nhiều lần.
 Kích thước lớn dữ liệu không hạn chế.
Một số khái niệm về tập tin (file) (2)
 Có 3 loại dữ liệu kiểu tập tin:
 Tập tin văn bản (Text File)
 Dùng để ghi các ký tự lên đĩa (dưới dạng mã Ascii)
 Có chứa:
 Ký hiệu ‘\n’ : xuống dòng
 Kí tự EOF (End Of File) có mã Ascii là 26: nằm ở cuối tập tin
 Tập tin định kiểu (Typed File)
 Gồm nhiều phần tử có cùng kiểu: char, int, long, struct …
 Được lưu trữ trên đĩa dưới dạng một chuỗi các byte liên tục.
 Tập tin không định kiểu (Untyped File)
 Gồm các cấu trúc dữ liệu mà ta không quan tâm đến nội dung
hoặc kiểu của nó.
 Ta chỉ lưu ý đến các yếu tố vật lý của tập tin như độ lớn, ...
Một số khái niệm về tập tin (file)(3)
 Biến tập tin
 Được dùng để đại diện cho một tập tin
 Các thao tác lên tập tin sẽ được thực hiện thông qua biến này
 Con trỏ tập tin
 Tại mỗi thời điểm, sẽ có một vị trí của tập tin mà tại đó việc
đọc/ghi thông tin sẽ xảy ra
 Ta hình dung có 1 con trỏ đang chỉ đến vị trí đó
 Sau khi đọc/ghi xong dữ liệu, con trỏ sẽ chuyển dịch thêm một
phần tử về phía cuối tập tin.
 Sau phần tử dữ liệu cuối cùng của tập tin là dấu kết thúc tập tin
EOF
Các thao tác trên tập tin
 Khai báo biến tập tin
 Mở tập tin

 Đóng tập tin

 Kiểm tra đến cuối tập tin hay chưa?

 Di chuyển con trỏ tập tin về đầu tập tin - Hàm


rewind()
Khai báo biến tập tin
 Cú pháp:
FILE <Danh sách các biến con trỏ>;
 Các biến trong danh sách phải là các con trỏ và
được phân cách bởi dấu phẩy(,).
 Ví dụ:

FILE *f1,*f2;
Mở tập tin (1)
 Cú pháp:
FILE *fopen(char *Path, const char *Mode)
Ý nghĩa:
 Trả về con trỏ tập tin của tập tin được mở
 Trả về NULL nếu có lỗi
Mở tập tin (2)
 Path: chuỗi chỉ đường dẫn đến tập tin trên đĩa
 Type: chuỗi xác định cách thức mà tập tin sẽ mở. Các giá trị
có thể của Mode:
Mở tập tin (3)
 Ví dụ: Mở một tập tin tên TEST.txt để ghi.
FILE *f;
f = fopen(“TEST.txt”, “w”);
if (f!=NULL){
// Các câu lệnh để thao tác với tập tin
// Đóng tập tin
}
=> mở tập tin để ghi
=> nếu tập tin đã tồn tại rồi thì tập tin sẽ bị xóa và một
tập tin mới được tạo ra
Đóng tập tin
 Cú pháp: int fclose(FILE *f)
 Ghi dữ liệu còn lại trong vùng đệm vào tập tin và đóng lại tập tin
 f là con trỏ tập tin được mở bởi hàm fopen()
 Giá trị trả về là 0 báo rằng việc đóng tập tin thành công
 Giá trị trả về là EOF nếu có xuất hiện lỗi

 Cú pháp: int fcloseall()


 Đóng tất cả các tập tin lại
 Trả về tổng số các tập tin được đóng lại
 Nếu không thành công, kết quả trả về là EOF
Kiểm tra đến cuối tập tin hay
chưa?
 Cú pháp:
int feof(FILE *f)
 Ý nghĩa:
 Kiểm tra xem đã chạm tới cuối tập tin hay chưa.
 Trả về EOF nếu cuối tập tin được chạm tới, ngược lại
trả về 0.
Di chuyển con trỏ tập tin về đầu
tập tin - Hàm rewind()
 Cú pháp:
void rewind(FILE *f)

 Ý nghĩa:
 Làm cho con trỏ quay về đầu tập tin như khi mở nó
Truy cập tập tin văn bản
 Ghidữ liệu lên tập tin văn bản
 Đọc dữ liệu từ tập tin văn bản
Ghi dữ liệu lên tập tin văn bản (1)
 Hàm putc()
int putc(int c, FILE *f)

 Được dùng để ghi một ký tự lên một tập tin văn bản
đang được mở (liên kết với con trỏ f) để làm việc
 c chứa mã Ascii của ký tự
 Hàm này trả về EOF nếu gặp lỗi
Ghi dữ liệu lên tập tin văn bản (2)
 Hàm fputs()
int fputs(const char *buffer, FILE *f)

 Được dùng để ghi một chuỗi ký tự chứa trong vùng


đệm lên tập tin văn bản
 Hàm này trả về giá trị 0 nếu buffer chứa chuỗi rỗng và
trả về EOF nếu gặp lỗi
Ghi dữ liệu lên tập tin văn bản (3)
 Ví dụ: Viết chương trình ghi chuỗi ký tự lên tập tin văn bản
D:\\Baihat.txt
Ghi dữ liệu lên tập tin văn bản (3)
 Hàm fprintf()
fprintf(FILE *f, const char *format, varexpr)

 Được dùng để ghi dữ liệu có định dạng lên tập tin văn bản.
 format: chuỗi định dạng (giống với các định dạng của hàm
printf())
 varexpr: danh sách các biểu thức, mỗi biểu thức cách nhau
dấu phẩy (,)
Đọc dữ liệu từ tập tin văn bản (1)
 Hàm getc()
int getc(FILE *f)

 Được dùng để đọc dữ liệu từ tập tin văn bản đang được mở
để làm việc (liên kết với f)
 Hàm này trả về mã Ascii của một ký tự được đọc (kể cả
EOF)
Đọc dữ liệu từ tập tin văn bản (2)
 Hàm fgets()
char *fgets(char *buffer, int n, FILE *f)

 Được dùng để đọc 1 chuỗi ký tự từ tập tin văn bản đang được mở
(liên kết với con trỏ f)
 Đọc cho đến khi đủ n ký tự hoặc gặp ký tự xuống dòng ‘\n’ (ký
tự này cũng được đưa vào chuỗi kết quả) hay gặp ký tự kết thúc
EOF (ký tự này không được đưa vào chuỗi kết quả)
 buffer: chỉ đến cùng nhớ đủ lớn chứa các ký tự nhận được
 Ký tự NULL (‘\0’) tự động được thêm vào cuối chuỗi kết quả lưu
trong vùng đệm
 Hàm trả về địa chỉ đầu tiên của vùng đệm khi không gặp lỗi và
chưa gặp ký tự kết thúc EOF. Ngược lại, hàm trả về giá trị NULL
Đọc dữ liệu từ tập tin văn bản (3)
 Hàm fscanf()
fscanf(FILE *f, const char *format, varlist)

 Được dùng để đọc dữ liệu từ tập tin văn bản vào danh
sách các biến theo định dạng.
 format: chuỗi định dạng (giống hàm scanf())
 varlist: danh sách các biến mỗi biến cách nhau dấu
phẩy (,).
Đọc dữ liệu từ tập tin văn bản (4)
 Ví dụ: Viết chương trình chép tập tin D:\Baihat.txt ở trên sang
tập tin D:\Baica.txt.
Truy cập tập tin nhị phân
 Ghi dữ liệu lên tập tin nhị phân
 Đọc dữ liệu từ tập tin nhị phân

 Di chuyển con trỏ tập tin

 Ví dụ
Ghi dữ liệu lên tập tin nhị phân
 Hàm fwrite()
size_t fwrite(const void *ptr, size_t size, size_t n, FILE *f)

 ptr: con trỏ chỉ đến vùng nhớ chứa thông tin cần ghi lên tập
tin.
 n: số phần tử sẽ ghi lên tập tin.
 size: kích thước của mỗi phần tử.
 f: con trỏ tập tin đã được mở.
 Giá trị trả về của hàm này là số phần tử được ghi lên tập tin.
Giá trị này bằng n trừ khi xuất hiện lỗi.
Đọc dữ liệu từ tập tin nhị phân
 Hàm fread()
size_t fread(const void *ptr, size_t size, size_t n, FILE *f)

 ptr: con trỏ chỉ đến vùng nhớ sẽ nhận dữ liệu từ tập tin
 n: số phần tử được đọc từ tập tin
 size: kích thước của mỗi phần tử
 f: con trỏ tập tin đã được mở
 Giá trị trả về của hàm này là số phần tử đã đọc được từ
tập tin. Giá trị này bằng n hay nhỏ hơn n nếu đã chạm
đến cuối tập tin hoặc có lỗi xuất hiện
Di chuyển con trỏ tập tin
 Hàm fseek()
int fseek(FILE *f, long offset, int whence)
 Được dùng để di chuyển con trỏ tập tin đến vị trí chỉ định
 f: con trỏ tập tin đang thao tác
 offset: số byte cần dịch chuyển con trỏ tập tin kể từ vị trí trước đó. Phần
tử đầu tiên là vị trí 0.
 whence: vị trí bắt đầu để tính offset, ta có thể chọn điểm xuất phát là

 Kết quả trả về của hàm là 0 nếu việc di chuyển thành công. Nếu không
thành công, 1 giá trị khác 0 (đó là 1 mã lỗi) được trả về.
Ví dụ
 Viết chương trình ghi lên tập tin CacSo.Dat 3 giá trị số (thực,
nguyên, nguyên dài). Sau đó đọc các số từ tập tin vừa ghi và
hiển thị lên màn hình
Hết chương

You might also like