CLang-lect01 - 2023-Đã Gộp

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 569

Tổng quan lập trình máy tính

huydq@soict.hust.edu.vn
eureka!
Giải thuật
Tập các chỉ lệnh hướng dẫn quá
trình thực hiện một công việc

Chương trình
Máy tính có thể làm gì ?
Không nhiều… Máy tính chỉ có thể hiểu
các con số!
• Lưu trữ, tìm kiếm các con số (nhanh và
chính xác).
• Thực hiện các phép tính cộng, trừ, nhân,
chia sô học (cũng nhanh và chính xác).
• So sánh số học.
• Di chuyển và thực hiện các chỉ lệnh tính
toán trong một danh sách.
Máy tính có thể làm gì nữa ?
• Các tính toán phức tạp hơn có thể được
thực hiện từ tổ hợp các tính toán căn bản.
• Kết nối với các thiết bị ngoại vi để thực
hiện nhập xuất dữ liệu (input/output)
– Input : bàn phím, con chuột, …
– Output : màn hình, máy in
• Mọi thứ đều được làm với các con số.
Mô hình máy tính Von Neumann
Chương trình máy tính là gì?
• Một chuỗi các chỉ lệnh nhằm giải quyết
một mục đích nhất định
• Các chỉ lệnh được thực hiện một cách
tuyến tính.
• Không có chỉ lệnh nào được thực hiện
khi chỉ lệnh trước đó chưa hoàn thành.
• Chương trình có thể được biểu diễn bằng
một ngôn ngữ lập trình.
Ngôn ngữ máy

• Là ngôn ngữ mà máy tính hiểu được.


• đối với chúng ta, ngôn ngữ máy chỉ là chuỗi số với 0 và 1.
• Không có một ngôn ngữ máy chung cho máy tính.
– Mỗi bộ vi xử lí có một ngôn ngữ riêng củanó
• Con người không thể làm việc trực tiếp với ngôn ngữ máy.
• Nhưng máy tính không thể hiểu ngôn ngữ khác
Ngôn ngữ bậc cao

• Assembly – ngôn ngữ máy tính


được mã hoá bằng văn bản
(vẫn không thuận tiện).
• Ngôn ngữ thông dịch
(java, perl)
– Chương trình được
chuyển thành ngôn
ngữ máy trong quá
trình mỗi lần thực
hiện
• Ngôn ngữ biên dịch (C, pascal)
– Chương trình được
dịch thành ngôn ngữ
máy một lần trước khi
thực hiện
Quá trình giải quyết vấn đề
“Oe oe! con đói rồi"
Vấn đề
Can phai pha 100ml
Phân tích sữa cho con bú
ðặc tả Vð 1. Rửa bình
2. Cho 13g sữa vào bình
Thiết kế 3. ðun nước sôi
4. Pha nước ấm 40OC
Giải thuật 5. Cho 100ml ấm vào bình
Thi hành ruabinh();
chosua(2);
dunnuocsoi();
Chương trình
phanuocam(40);
Dịch chonuocam(100)
01001110101100101010101010010
10101010100110010101010101001
Chương trình 01101001110101010101001001011
chạy 10100111101010101111101010100
0110100001101...
Giải thuật

• Một chuỗi các chỉ lệnh đặc tả các bước yêu


cầu để thực hiện một vài nhiệm vụ nhất định
• Một số ví dụ về giải thuật trong cuộc sống
– Chỉ dẫn nấu ăn
– Hướng dẫn lắp đặt một thiết bị
– Các luật thực hiện một trò chơi
– Chỉ dẫn đi đường từ A đến B
– Hướng dẫn sửa chữa xe máy
– v.v.
Giải thuật nấu cơm
• Chuẩn bị ðầu vào (input)
– 0,5 kg gạo, 1 lít nước
• Các bước thực hiện
– Vo gạo (0,5 kg) Xử lí (processing)
– ðổ (1 lít) nước vào nồi
– ðun sôi nồi nước
– Cho gạo vào nồi
– Chờ đến khi nước cạn
– Vặn nhỏ lửa ðầu ra (output)
– Chờ 15 phút bắc nồi ra
• Kết quả
– Nồi cơm chín cho 5 người ăn
Các thành phần một giải thuật
• Biến và giá trị
• Các chỉ lệnh
– Tuần tự
– Lựa chọn
– Lặp
– Các thủ tục con
Ngoài ra cũng cần có thêm mô tả, chú thích
để trợ giúp dễ hiểu giải thuật hơn
Giá
trị
• Biểu diễn một đại lượng thể hiện sự đo
lường hay một trạng thái nào đó
• Có thể là số hay chữ (vd., số nhà, tên
phố, …)
• Thường mỗi giá trị đều có đơn vị ngầm
định đi theo nó
• Ví dụ:
– Giá trị cho cân gạo, cho lít nước trong giải
thuật nấu cơm
Biế
n
• Là vật chứa các giá trị - nơi mà giá trị
được lưu trữ
• Ví dụ
Biến Giá trị

10 cái kẹo
Bình này có
thể dùng để 50 g đường
chứa
3 cái bánh
v.v
Kiểu của biến
• Biến có thể được hạn chể để chỉ chứa
một kiểu cụ thể nào đó của giá trị
• Ví dụ : cân (gạo) hay lít (nước)
Chỉ lệnh
• Là lệnh chỉ dẫn thực hiện một việc mà
– đơn giản
– không nhập nhằng
– hệ thống đã biết về nó để có thể thực hiện
– có thể đưa ra hướng dẫn thực hiện trên giá trị
và biến của giải thuật
Lời khuyên về các chỉ lệnh

• Khi viết một giải thuật hãy tạo các chỉ


lệnh thật đơn giản và không bị nhập
nhằng
• Ví dụ:
Vo gạo (1 kg) rồi đổ • Vo gạo (1 kg).
nước (1 lít) vào nồi và • ðổ nước vào nồi (1 lít).
đun sôi • ðun sôi.
Cấu trúc tuần tự
• Là một dãy các chỉ lệnh
• ... được thực hiện một cách tuần tự có lệnh
sau và lệnh trước
• Ví dụ:
1. Vo gạo (0,5 kg)
2. ðổ (1 lít) nước vào nồi
3. ðun sôi nồi nước
4. Cho gạo vào nồi
5. Chờ đến khi nước cạn
6. Vặn nhỏ lửa
7. Chờ 15 phút bắc nồi ra
Lựa chọn

• Một lệnh mà có thể lựa chọn một trong


hai chuỗi lệnh cần thực hiện
• Lựa chọn này dựa trên một điều kiện
đúng/sai chỉ được chỉ ra
if (nếu)…
then (thì)…
else (không thì)…
Ví dụ tính số hữu tỉ
Nhập một số input N
nguyên
if (N khác 0)
ðiều kiện lựa chọn then
{
output 1/N
Thực hiện khi điều }
kiện lựa chọn đúng
else
{
Thực hiện khi điều
output “vô định"
kiện lựa chọn sai }
Câu hỏi ?
Hai giải thuật dưới đây có cho cùng một kết quả ?
Giải thuật 1 Giải thuật 2
input N input N
if (N khác 0) if (N khác 0)
then
then
{
output 1/N {
} output 1/N
else }
{
output “vô định"
} output “vô định"

Giải thuật 2 đưa ra cả hai kết quả khi N khác 0


Vòng lặp
• Là lệnh cho phép thực hiện lặp một khối
chỉ lệnh ...
– ...theo một điều kiện đúng sai
– điều kiện được kiểm tra mỗi lần thực hiện lặp
• Phân biệt hai dạng lặp
– while…do…: kiểm tra điều kiên ngay từ lần
lặp đầu tiên
– do…while…(hoặc repeat…until…): lần lặp
đầu tiên điều kiện không được kiểm tra
Ví dụ về vòng lặp
Thực hiện liệt kê dãy số lẻ trong khoảng 1..100

Tạo biến sốlẻ với giá trị sốlẻ = 1


bắt đầu từ 1
while (sốlẻ nhỏ hơn 100)
Vòng lặp được thực do
hiện với điều kiện sốlẻ
nhỏ hơn 100
{
output sốlẻ
Liệt kê sốlẻ với giá trị tăng thêm 2 vào sốlẻ
hiện tại cho mỗi lần lặp
và tăng giá trị của sốlẻ }
lên 2 đơn vị

Chú ý: Với while…do…, điều kiện lặp được kiểm tra ngay từ lần đầu tiên
Câu hỏi ?

Hai giải thuật dưới đây có cho cùng một kết quả ?
Giải thuật 1 Giải thuật 2
sốlẻ = 1 sốlẻ = 1
while (sốlẻ nhỏ hơn 100) while (sốlẻ nhỏ hơn 100)
do do
{ {
output sốlẻ tăng thêm 2 vào sốlẻ
tăng thêm 2 vào sốlẻ output sốlẻ
} }

Giải thuật 2 liệt kê các số lẻ từ 3…101


Ví dụ tính tổng dãy số
Tìm sự khác nhau hai giải thuật dưới đây ?
Giải thuật 1 Giải thuật 2
a=0 a=0
tổng = 0 tổng = 0
while (a > 0) do do
{ {
input a input a
tăng thêm a vào biến tổng tăng thêm a vào biến tổng
} } while (a > 0)
output tổng output tổng

Trong giải thuật 1, lần nhập a đầu tiên không được thực hiện vì điều
kiện lặp không thoả mãn. Ngược lại trong giải thuật 2 a được nhập giá
trị cho lần đầu tiên mà không cần kiểm tra điều kiện.
Thủ tục
• Là một chuỗi các chỉ lệnh được đặt tên
• Vì vậy bạn có thể
– tham chiếu đến nó (thông qua tên)
– ...sử dụng lại bằng cách copy các chỉ lệnh trong thủ
tục vào giải thuật của bạn
• Thủ tục được sử dụng trong lập trình có cấu trúc
để phân chia chương trình thành các thành
phần nhỏ với nhiều tên gọi khác nhau
– Thủ tục (procedure)
– Hàm (function)
– Chương trình con (sub-routine)
Ví dụ về thủ tục

Procedure Nấu_cơm Làm bữa ăn tối


{ {
Vo gạo (0,5 kg) Nấu_cơm
ðổ (1 lít) nước vào nồi Luộc_rau Gọi thủ tục
ðun sôi nồi nước Rang_thịt
Cho gạo vào nồi Dọn_cơm
Chờ đến khi nước cạn }
Vặn nhỏ lửa
Chờ 15 phút bắc nồi ra Khai báo thủ tục
}
Tổng kết
• Quá trình giải quyết vấn đề
– Vấn đề ® Giải thuật ® Chương trình
• Ngôn ngữ lập trình
– Bậc cao vs. ngôn ngữ máy
• Các thành phần của giải thuật
– Biến và giá trị
– Các chỉ lệnh:
• Tuần tự, Lựa chọn, Lặp, Thủ tục
Ngôn ngữ lập trình C

huydq@soict.hust.edu.vn

1
Nội dung
➢Tổng quan về ngôn ngữ C

➢Các thành phần của chương trình C

➢Chương trình biên dịch

2
Tại sao học ngôn ngữ C ?
• Là ngôn ngữ có tính uyển chuyển cao
– Lập trình có cấu trúc
– Có khả năng hỗ trợ các thao tác ở mức thấp
• Có tính khả chuyển cao
• Dịch ra chương trình mã máy có kích thước nhỏ
và rất hiệu quả khi thực hiện
• Được sử dụng rộng rãi trong lập trình
chuyên nghiệp
• Là ngôn ngữ nền tảng của một số ngôn ngữ
khác (C++, Java, Perl, awk)

3
Phân biệt C và C++
• Điểm giống nhau:

• Có cùng cú pháp và cách viết code.


• Cấu trúc code giống nhau
• Bộ biên dịch code giống nhau (không phải 100%, tuy nhiên
các bộ biên dịch/compiler mới đều hỗ trợ cả 2 ngôn ngữ)
• Mô hình bộ nhớ giống nhau và đều khá gần với phần cứng
• Sử dụng chung các khái niệm như stack, heap, file-scope,
static variables …
• ..

4
Phân biệt C và C++
• Điểm khác nhau:
C C++
Phát triển bởi Dennis Ritchie từ 1969 Phát triển bởi Bjarne Stroustrup vào
tới 1973. 1979.
Không hỗ trợ lập trình hướng đối Hỗ trợ lập trình hướng đối tượng như:
tượng. đa hình, đóng gói, kế thừa, đối tượng
Đuôi mở rộng file c là .c Đuôi mở rộng file C++ là .cpp
Là con của C. Code C có thể chạy bởi
Là cha của C++ C++ nhưng code C++ không thể chạy
bởi C.
Ngoài 32 từ khóa của C còn bổ sung
Có 32 từ khóa
thêm nhiều từ khóa mới
…. …

5
Lịch sử ngôn ngữ C
• CPL Combined Programming Language (Barron et al., 1963)

• BCPL Basic CPL (Richards, 1969)


• ngôn ngữ lập trình máy tính thủ tục, mệnh lệnh, và cấu trúc.
• Ban đầu được định dùng để viết trình biên dịch cho các ngôn
ngữ khác

• B (Thompson, 1970)
• có nguồn gốc từ BCPL, tên thu gọn từ BCPL
• thiết kế cho các ứng dụng đệ quy, không dùng chữ số, độc
lập với máy tính, VD: như phần mềm hệ thống và ngôn ngữ.

6
Lịch sử ngôn ngữ C
• C K&R C (Ritchie, 1972)
• phát triển bởi Dennis M. Ritchie tại Bell Labs của AT&T

• thực thi lần đầu tiên trên máy tính DEC PDP-11

• để khắc phục các vấn đề của các ngôn ngữ trước

• xây dựng hệ điều hành Unix

• 1978: C được giới thiệu trong phiên bản đầu của cuốn sách
"The C programming language”- Kernighan và Ritchie

7
Lịch sử ngôn ngữ C
• C được bổ sung thêm những tính năng và khả năng mới .
• Đồng thời tồn tại nhiều phiên bản nhưng không tương thích
nhau.
• Mã nguồn C có thể chạy tôt trên compiler này nhưng trên
compiler khác thì bị lỗi -> cần phải có một tiêu chuẩn thống
nhất
• C89, ANSI C, American National Standards Institute C
(X3J11, 1989)
• C99 (JTC1/SC22/WG14, ISO/IEC 9899, 1999)
• C11,2011

8
Nội dung
➢Tổng quan về ngôn ngữ C

➢Các thành phần của chương trình C

➢Chương trình biên dịch

9
Chương trình C đầu tiên
Hello World – Chào thế giới
Giải thuật Chương trình C
#include <stdio.h>

int main()
output “Hello World!” {
printf(“Hello World!”);

return 0;
}

10
Cấu trúc căn bản chương trình C

Khai báo tệp tiêu đề mô tả


thư viện hàm vào ra Chương trình C
#include <stdio.h>
Hàm thực hiện chính của
chương trình int main()
{
Bắt đầu hàm chính
printf(“Hello World!”);
Lệnh in ra màn hình
return 0;
}
Kết thúc hàm chính

11
Cú pháp của C
• Chương trình được viết dựa trên
– Các từ khóa: là các từ được định nghĩa từ trước
dành riêng cho ngôn ngữ, ví dụ: main, if, do, while,

– Các bộ kí tự dấu: sử dụng với mục đích đặc thù
trong chương trình như tạo 1 khối lệnh { }, tạo một
chuỗi kí tự “”, …

– Các tên định danh: do người sử dụng khai báo


dùng đại diện cho 1 biến hay 1 chương trình con
trong chương trình

12
Từ khoá C
• Điều khiển luồng: if, else, return, switch, case, default

• Điều khiển lặp : for, do, while, break, continue

• Kiểu: int, long, short, float, double, char, void, signed, unsigned,
static, const

• Cấu trúc: struct, typedef, union, enum, sizeof

• Từ khoá khác: extern, auto, register, volatile, main

• Chú ý: Trong C phân biệt giữa chữ hoa và chữ thường

13
Các bộ dấu thường dùng

• {…} Tạo một khối lệnh của chương trình


• “…” Tạo một chuỗi kí tự cần hiển thị
• /* … */ Tạo chú thích trong chương trình
• ; Dấu kết thúc một lệnh
• và các dấu của một biểu thức như +, -,*, /, (), …

14
Định danh - Tên
• Identifier
• Khi khai báo 1 biến hay 1 chương trình con, người
lập trình cần phải đặt tên cho nó.
• Quy tắc đặt tên của người sử dụng
– Chỉ dụng chữ cái, chữ số và kí tự gạch nối (_) để đặt tên
– Tên phải bắt đầu bằng chữ cái hoặc dấu _
– Phân biệt chữ hoa và chữ thường trong các tên
• Những tên nào sau đây là hợp lệ ?
– tong, 2k, trung binh, lon_nhat, u2, %totnghiep

15
Định danh – Tên (tiếp)
• Lưu ý:
1.Định danh gồm nhiều từ -> tách các từ bằng cách sử dụng dấu _.
VD: danh_sach_sinh_vien dễ đọc và dễ hiểu hơn so với danhsachsinhvien.

2.Định danh nên có tính chất gợi nhớ,


• VD: lưu trữ thông tin về sinh viên -> biến nên được đặt tên là
danh_sach_sinh_vien hay ds_sv...
• ngược lại, danh_sach_sinh_vien chỉ
• nên dùng cho: đối tượng liên quan đến sinh viên,
• không nên cho: các đối tượng chứa thông tin về cán bộ viên chức.
• Đặt tên có tính gợi nhớ -> chương trình dễ đọc hiểu.

3.Ngôn ngữ C phân biệt chữ cái thường và chữ cái hoa
VD: dinh_danh khác Dinh_danh.

16
Định danh – Tên (tiếp)
• Lưu ý (tiếp):
4. Hằng đặt tên bằng chữ hoa,
biến, hàm, cấu trúc: chữ thường.

Ví dụ:

Định danh Loại đối tượng


HANG_SO_1, _CONSTANT_2 hằng
a, b, i, j, count biến
nhap_du_lieu, tim_kiem, xu_li hàm
sinh_vien, mat_hang cấu trúc

17
Ví dụ viết chương trình
Tạo hàm chính cho
chương trình
In các số từ 0 đến 9 int main()
{

đặt dem = 0
while (dem nhỏ hơn 10)
do
{
output dem
tăng 1 vào dem
} return 0;
}

18
Ví dụ (tiếp)
#include <stdio.h>

In các số từ 0 đến 9
int main()
{ Khai báo tiêu đề

đặt dem = 0
while (dem nhỏ hơn 10)
do
{
output dem
tăng 1 vào dem
return 0;
} }

19
Ví dụ (tiếp)
#include <stdio.h>

In các số từ 0 đến 9 /* In tu 0 toi 9 */

int main()
{ Chú thích
đặt dem = 0
while (dem nhỏ hơn 10)
do
{
output dem
tăng 1 vào dem
return 0;
} }

20
Ví dụ (tiếp)
#include <stdio.h>

In các số từ 0 đến 9 /* In tu 0 toi 9 */


int main()
{
int dem;
đặt dem = 0
while (dem nhỏ hơn 10)
do Khai báo biến
{
output dem
tăng 1 vào dem
} return 0;
}

21
Ví dụ (tiếp)
#include <stdio.h>

In các số từ 0 đến 9 /* In tu 0 toi 9 */


int main()
{
int count;

đặt dem = 0 dem = 0;


while (dem nhỏ hơn 10)
do Gán giá trị cho
{ biến
output dem
tăng 1 vào dem
} return 0;
}

22
Ví dụ (tiếp)
#include <stdio.h>

In các số từ 0 đến 9 /* In tu 0 toi 9 */


int main()
{
int dem;
Tạo vòng lặp
đặt dem = 0 dem = 0;
while (dem nhỏ hơn 10) while ( dem < 10 )
do {
{
output dem
}
tăng 1 vào dem
return 0;
} }

23
Ví dụ (tiếp)
#include <stdio.h>

In các số từ 0 đến 9 /* In tu 0 toi 9 */


int main()
{
int dem;
đặt dem = 0
while (dem nhỏ hơn 10) dem = 0;
while ( dem < 10 )
do {
{ printf(“%d\n”, dem);
output dem
tăng 1 vào dem }
return 0;
}
}

24
Ví dụ (tiếp)
#include <stdio.h>

In các số từ 0 đến 9 /* In tu 0 toi 9 */


int main()
{
int dem;
đặt dem = 0
while (dem nhỏ hơn 10) dem = 0;
while ( dem < 10 )
do {
{ printf(“%d\n”, dem);
output dem dem = dem + 1;
tăng 1 vào dem }
} return 0;
}

25
Chương trình sau làm gì?
#include <stdio.h>

int main(){
float num;

printf(“Enter a number: “);


scanf(“%f”, &num);

if ( num < 0 ) {
printf(“%f is negative”, num);
} else {
printf(“%f is positive”, num);
}

return 0;
}

26
Giải thuật của chương trình
/* Tìm dấu của một số */
input num
if (num < 0) then
{
output “số âm”
}
else
{
output “số dương”
}

27
Nội dung
➢Tổng quan về ngôn ngữ C

➢Các thành phần của chương trình C

➢Chương trình biên dịch

28
Ngôn ngữ lập trình bậc cao
10100110 01110110
#include <stdio.h> 00100110 00000000
11111010 11111010
int main() 01001110 10100110
{ 11100110 10010110
printf(“Hello World”); 11001110 00101110
10100110 01001110
11111010 01100110
return 0; 01001110 10000110
} etc...

Mã nguồn Mã máy

• Chương trình dịch sẽ dịch 1 chương trình viết ở


ngôn ngữ bậc cao -> ngôn ngữ máy

29
Biên dịch chương trình C

Pha dịch Pha liên kết

Lỗi có thể sảy ra tại pha dịch hay pha liên kết

30
Trình biên dịch
• Để dịch chương trình thì cần phải có trình biên dịch, ví
dụ: gcc
• Trình biên dịch C luôn hỗ trợ các tham số để thực hiện
2 pha của quá trình dịch.
• Ví dụ: gcc -c để thực hiện pha dịch, và
• gcc -o để thực hiện pha liên kết.

• Có thể sử dụng 1 câu lệnh để thực hiện đồng thời cả


2 pha trên 1 tệp chương trình nguồn.
– $gcc -o <tệp đích> <tệp nguồn>
– VD: $gcc -o hello hello.c

31
IDE: Môi trường hỗ trợ lập trình
• Lập trình : lặp đi lặp lại soạn mã nguồn, thực hiện dịch, chạy
tìm lỗi và sửa
• Thực hiện độc lập = các công cụ khác nhau: ví dụ
• soạn thảo bằng emacs,
• dịch chương trình bằng gcc,
• gỡ lỗi bằng gdb
• Tích hợp các công cụ vào 1 môi trường duy nhất để hỗ trợ
việc lập trình dễ dàng hơn.
• IDE (Integrated Development Environment ) bao gồm 3 bộ
công cụ chính là:
• trình biên soạn (Editor)
• trình biên dịch (Compiler)
• trình gỡ rối (Debugger).

32
Sản phẩm IDE
• Trong Linux:
– Kdevelop
– Code::Blocks
• Trong Mac:
– Xcode
• Trong Window:
– Dev-C++,
– Turbo C++,
– Visual C++,
– etc
33
• 1998.
KDevelop • miễn phí.

• sử dụng nền tảng plug-in-> người dùng có thể bổ sung và gỡ bỏ plug để cài đặt chính
xác tính năng cần thiết
• tích hợp tính năng hỗ trợ hiện trạng do đó nhiều loại cài đặt plug-in có thể được sử
dụng trong những dự án cụ thể.
• hỗ trợ 15 ngôn ngữ lập trình cùng với nhiều tính năng hỗ trợ cho mỗi ngôn ngữ.
• tích hợp trình gỡ rối, hệ thống kiểm soát phiên bản, wizard ứng dụng, tính năng chia
code

34
Code::BLOCKS
• IDE C, C++ và Fortran miển phí.
• phần mềm mã nguồn mở với giấy phép GPLv3 (không có chi phí ẩn, không thu thập
thông tin cá nhân).
• phần mềm đa nền tảng (Cross-platform) có thể cài đặt trên Linux, Mac, Windows (sử
dụng wxWidgets).
• Thêm tính năng mở rộng thông qua các plugin.

35
Dev-C++
• IDE nguồn mở
• chỉ hỗ trợ nền tảng Windows
• Nhanh chóng tạo Windows, console, thư viện tĩnh và DLL.
• Chỉnh sửa và biên dịch các tệp tài nguyên.
• Hỗ trợ trình biên dịch dựa trên GCC.
• Có thể tùy chỉnh cú pháp tô sáng trình soạn thảo.

36
• Microsoft Visual Studio
Visual C++ • Dung lượng lớn, máy tính phải có cấu hình cao
• phần mềm trả phí.
• IDE hỗ trợ lập trình C/C++ tốt nhất
• Hỗ trợ đa nền tảng, đa hệ điều hành và nhiều ngôn ngôn ngữ lập trình.
• Hỗ trợ xây dựng, phát triển nhiều ứng dụng có quy mô lớn, an toàn và bảo mật cao.
• Hỗ trợ nhiều hệ quản trị cơ sở dữ liệu:SQL Server, oracle, Access…
• Cơ chế debug và phát hiện lỗi nhanh, tự động phát sinh code, hướng đối tượng…
• Hỗ trợ Git tích hợp, hơn 10k plugin được hỗ trợ và miễn phí

37
• lựa chọn hàng đầu cho người dùng sử
X code dụng hệ điều hành Mac.
• Hỗ trợ nhiều ngôn ngữ phổ biến
• Đây là một IDE hoàn toàn miễn phí

38
• 2008
SUBLIME TEXT • trình soạn thảo mã nguồn đa nền tảng
• Giao diện người dùng hiện đại
• kích thước nhỏ gọn.
• Điều hướng dễ dàng và nhanh chóng đến các từ, dòng và ký hiệu.
• Có thể tự động tạo một chỉ mục toàn dự án của mọi chức năng, phương thức
và lớp.
• API tùy biến cao và mạnh mẽ.
• Cú pháp tô sáng và tự động sửa lỗi.

39
Visual Studio Code •

2015
Miễn phí
• Đa nền tảng]
• phần mềm biên tập mã nguồn
• Hỗ trợ gỡ lỗi, tô sáng cú pháp, hoàn thành mã thông minh, đoạn mã, tái cấu trúc mã
và Git được nhúng.
• Có thể thay đổi chủ đề, phím tắt, tùy chọn và cài đặt các tiện ích mở rộng bổ sung
chức năng.

40
C PROGRAMMING
LANGUAGE

TS. Đỗ Quốc Huy


Bộ môn Khoa Học Máy Tính
huydq@soict.hust.edu.vn

1
Nội Dung Khóa Học
1st Tổng quan lập trình máy tính
2nd Ngôn ngữ lập trình C
3rd Kiểu dữ liệu
4th Vào ra dữ liệu
5th Biểu thức trong ngôn ngữ C
6th Lệnh rẽ nhánh
7th Lệnh lặp (1)
8th Lênh lặp (2)
9th Hàm
10th Mảng
11th Con trỏ
12th Chuỗi
13th Cấu trúc dữ liệu
14th Vào ra với file
15th Final Exam

2
Câu hỏi bài cũ
https://forms.office.com/r/Gt8iM7FSBP
Kiểu dữ liệu

➢Khai báo biến và hằng


➢Các kiểu dữ liệu
• Số nguyên
• Số thực
• Ký tự
• Chuỗi
• Logic

4
(Nhắc lại) Giá trị

• Biểu diễn một đại lượng thể hiện sự đo lường


hay một trạng thái nào đó
• Có thể là số hay chữ (vd., số nhà, tên phố, …)
• Thường mỗi giá trị đều có đơn vị ngầm định đi
theo nó
• Ví dụ:
– Giá trị cho cân gạo, cho lít nước trong giải thuật nấu
cơm

5
(Nhắc lại) Biến
• Là vật chứa các giá trị - nơi mà giá trị được
lưu trữ
• Ví dụ
Biến Giá trị

10 cái kẹo
Bình này có
thể dùng để 50 g đường
chứa
3 cái bánh
v.v

6
1.1.Biến (variable)
• Biến là đối tượng lưu giữ dữ liệu và có thể thay đổi giá trị khi
chương trình được thực thi.

• Biến được đặt (allocate) trong các ô nhớ thuộc bộ nhớ chính của
máy tính.

• Số ô nhớ dành cho 1 biến phụ thuộc kiểu dữ liệu của biến.
• VD mỗi biến kiểu float chiếm 4 byte trong bộ nhớ.

• Tên biến phải được đặt theo quy tắc đặt tên.
Biến
• Trong C, 1 biến phải được khai báo bởi người sử dụng với 1 tên
định danh được đặt theo quy tắc:

– VD: tong, dem, _abc, i, j, n

• Biến không chỉ được khai báo với tên mà còn phải có kiểu
(biến của C luôn là biến có kiểu).

– VD:
int i, j;
char ch;
Đâu là biến?
#include <stdio.h>
int main()
{
int tong=0, dem=0, sopt, sosau;

printf(“So phan tu trong day so:");


scanf("%d", &sopt);

while (dem < sopt)


{
scanf("%d", &sosau);
tong += sosau; dem++;
}

printf(“Tong la %d\n",tong);
return 0;
}
Kết quả
#include <stdio.h>
int main()
{
int tong=0, dem=0, sopt, sosau;

printf(“So phan tu trong day so:");


scanf("%d", &sopt);

while (dem < sopt)


{
scanf("%d", &sosau);
tong += sosau; dem++;
}

printf(“Tong la %d\n",tong);
return 0;
}
Khai báo biến
• Một biến phải được khai báo trước khi sử dụng
• Cú pháp khai báo:
<KieuDuLieu> TenBien;
<KieuDuLieu> TenBien1, …, TenBien_N;
• Ví dụ:
//Khai báo biến x là một số nguyên 4 byte có dấu
int x;
//Khai báo các biến y, z là các số thực 4 byte
float y,z;
//Sau khi khai báo, có thể sử dụng
x = 3; y = x + 1;

11
Khai báo biến
• Sau khi khai báo, biến chưa có giá trị xác định.
• Biến cần được gán giá trị trước khi sử dụng
• C cho phép kết hợp khai báo và khởi tạo biến
KieuDuLieu TenBien = GiaTriBanDau;
KieuDuLieu Bien1 = GiaTri1, BienN = Gia_TriN;

• Ví dụ:
//Khai báo biến nguyên a và khởi tạo gia tri bằng 3
int a = 3;
//Khai báo biến thực x,y và khởi tạo giá tri bằng 5.0 và 7.6
float x = 5.0, y = 7.6;

12
1.2. Hằng
• Hằng là đại lượng có giá trị không đổi trong chương
trình.
• Giá trị hằng do người lập trình xác định.
• Các khái niệm
• Literals (Nghĩa đen)
• Constant (Biến hằng)
• Definition (macro)
• Các loại hằng
• Hằng số nguyên
• Hằng số thực
• Hằng ký tự
• Hằng chuỗi/xâu kỹ tự

13
Hằng số nguyên (int literal)
Trong C, hằng số nguyên có thể biểu diễn dưới các dạng:
cơ số 10, 8, 16

Hệ 10 Hệ 16 Hệ 8

2011 0x7DB 03733


396 0x18C 0614

14
Hằng số thực (floating point literal)
• Trong C, hằng số thực có thể biểu diễn dưới các dạng
• Dạng số thực dấu phẩy tĩnh
• Dạng số thực dấu phẩy động

Số thực dấu phẩy tĩnh Số thực dấu phẩy động


3.14159 31.4159E-1
123.456 12.3456E+1 hoặc
1.23456E+2

15
Hằng ký tự (character literal)
• Hằng ký tự có thể biểu diễn theo hai cách
• Đặt ký hiệu của ký tự giữa hai dấu nháy đơn
• Dùng mã ASCII của ký tự:
• Số thứ tự của ký tự đó trong bảng mã ASCII
• Là số nguyên→tuân thủ quy tắc biểu diễn số nguyên

Ký tự Dùng nháy đơn Dùng mã ASCII


Chữ cái A ‘A’ 65, 0x41, 0101
Dấu nháy đơn ‘\’’ 39, 0x27, 047
Ký tự tab ‘\t’ 9, 0x09, 011

16
Hằng xâu ký tự (string literal)
• Hằng xâu kí tự được biểu diễn bằng dãy các kí tự trong
cặp dấu nháy kép.
• Ví dụ:
• “ngon ngu lap trinh C”
• “Tin hoc dai cuong”
• “Dai hoc Bach Khoa Ha Noi”

17
Khai báo biến hằng
• Dùng từ khóa const
• Cú pháp:
const Kiểu Tên_hằng = giá_trị;
• Ví dụ:
const int MAX_SINH_VIEN = 50;
const char CNTT[20] = “Cong nghe thong tin”;
const float DIEM_CHUAN = 23.5;

18
Khai báo hằng (dùng macro)
• Dùng chỉ thị #define
Không có dấu ;
• Cú pháp:
#define Tên_hằng Giá_trị
• Ví dụ:
#define MAX_SINH_VIEN 50
#define CNTT “Cong nghe thong tin”
#define DIEM_CHUAN 23.5

19
Khai báo hằng
Chú ý:

• Giá trị của các hằng phải được xác định ngay khi khai
báo.

• #define là chỉ thị tiền xử lý


• Khi chương trinh được biên dịch, <Tên_hằng> sẽ được thay
thế bằng <Giá_trị>
• Dễ đọc, dễ thay đổi
• Dễ chuyển đổi giữa các nền tảng phần cứng hơn

20
Kiểu dữ liệu

➢Khai báo biến và hằng


➢Các kiểu dữ liệu
• Số nguyên
• Số thực
• Ký tự
• Chuỗi
• Logic

21
Các kiểu dữ liệu

• Một kiểu dữ liệu là một tập hợp các giá trị mà một dữ liệu
thuộc kiểu dữ liệu đó có thể nhận được.

• Ví dụ: Một đối tượng kiểu char của C sẽ là


• Một số nguyên (Số nguyên có dấu, 1 byte)
• Giá trị thuộc khoảng: [-128…127]

• Trên một kiểu dữ liệu, xác định một số phép toán đối với các
dữ liệu thuộc kiểu dữ liệu tương ứng.

22
Ví dụ kiểu số nguyên (int)
Một số phép toán được định nghĩa trên kiểu dữ liệu số
nguyên của C
Tên phép toán Ký hiệu Ví dụ
Đảo dấu -
Cộng;Trừ;Nhân + - *
Chia lấy nguyên / 17/3 → 5
Chia lấy phần dư % 17%3 → 2
So sánh > < >= <= == !=
Phép toán trên bit: & | ^ ~ << >> 3^17 →18
AND; OR; XOR; NOT, ~3 → -4
Shift,…

23
Các kiểu cơ bản
• Mọi biến trong C đều dùng để lưu trữ con số
– char : thường dùng lưu 1 số biểu diễn kí tự
– short : lưu 1 số nguyên nhỏ (thường 16-bits)
– int : lưu 1 số nguyên chuẩn (thường 32-bits)
– long : lưu 1 số nguyên lớn
– float : lưu 1số thực
– double : lưu 1 số thực với độ chính xác cao
Số nguyên
• Được lưu trữ dưới dạng số nhị phân trong bộ nhớ máy tính
– VD: 10100110 là số mấy dạng thập phân?

• Có nhiều kiểu số nguyên khác nhau trong C, kích thước lưu trữ
khác nhau:
char, short, int, long.

• Để khai báo 1 biến nguyên


• có dấu, dùng từ khóa: signed
• không dấu, dùng từ khóa: unsigned.
– VD: signed int i; unsigned int u;
Kích thước số nguyên
Type Bits Possible Values
char 8 -128 to 127
short 16 -32768 to 32767
unsigned short 16 0 to 65535
int 32 -2147483648 to 2147483647
long 32 -2147483648 to 2147483647
unsigned int 32 0 to 4294967295
long long 64 -9e18 to + 8e18

Cần chú ý tới kích thước của các loại số để tránh tràn số
trong các phép tính
Tìm giá trị phép tính
unsigned char a=128;
unsigned char b=128;
unsigned char c = a + b;

• Cho biết c có giá trị là bao nhiêu?


Hằng số nguyên
• Để lưu các giá trị vào 1 biến ta cần có cách biểu diễn giá
trị dưới dạng 1 hằng số.

• Biểu diễn dưới dạng số thập phân


– VD: 123456, -123456

• Biểu diễn dưới dạng số hexa (với tiền tố 0x)


– VD: 0x12AB,0xFFFF

• Biểu diễn dưới dạng số bát phân (bắt đầu số bằng số 0)


– VD: 0123456

• Cần chú ý 2 giá trị 123456 và 0123456 là 2 giá trị hoàn


toàn khác nhau.
Ví dụ
short i = 0,j = 0;
short a = 0xFFFF;
int x,y;
x = y = 123456;
char k = 0xFF;

• Hãy cho biết giá trị của biến k? (biết char là biến
nguyên có dấu kích thước 1 byte).
Số thực
• Cũng có 2 kiểu số thực “ngắn” và “dài” trong ngôn
ngữ C tương ứng là: float và double

• Mọi hàm toán học trong C đều thực hiện trên số


thực kiểu double.

• Chỉ nên sử dụng float khi cần tiết kiệm không gian
bộ nhớ và bạn chỉ có các số thực giá trị nhỏ.
Kích thước số thực
Kiểu Cỡ lưu trữ Dãy giá trị Độ chính xác

float 4 byte 1.2E-38 tới 6 vị trí thập phân


3.4E+38
double 8 byte 2.3E-308 tới 15 vị trí thập phân
1.7E+308

long double 10 byte 3.4E-4932 tới 19 vị trí thập phân


1.1E+4932
Kích thước số thực
• số thực được lưu trong bộ nhớ dưới dạng số nhị phân, → nó có
tính rời rạc mà không thể có tính liên tục như số thực trong tự
nhiên.

• → 1 giá trị thực bất kì trong tự nhiên là x, máy tính sẽ tìm 1 giá trị
thực biểu diễn gần đúng nhất với x để lưu trữ nó.
VD:0.666666666 lưu với kiểu float sẽ là 0.666667

• → mọi phép tính số thực trong máy tính chỉ là phép tính “gần
đúng”.

• Chọn loại số thực có kích thước biểu diễn càng lớn thì các phép
tính có độ chính xác càng cao.
-> dùng kiểu double sẽ lưu được chính xác 0.666666666
Hằng số thực
• Sử dụng dấu chấm
– VD: 123.456, -123.456
• Sử dụng kí pháp khoa học (E)
– VD: 12.456e-2
• Ví dụ:
double x,y,z; x = 0.1;
y = 2.456E5;
z = 0;
Kí tự cũng là số!
• Các kí tự được lưu trữ như là 1 số nguyên nhỏ (1
byte) trong bộ nhớ
• Để lưu trữ kí tự ta dùng kiểu char
• Mỗi kí tự tương đương với 1 số duy nhất trong bảng
mã ASCII.
• Có 2 cách biểu diễn hằng kí tự trong C:
• kí tự trong cặp nháy đơn
• số mã ASCII của kí tự.
• VD: kí tự ‘A’ có vị trí 65 trong bảng mã ASCII. Do đó
2 khai báo sau là tương đương:
– char c = ‘A’;
– char c = 65;
Các kí tự điều khiển
• Trong bảng mã ASCII
• các kí tự có mã từ 32 (kí tự trắng) là kí tự hiển thị,
• 32 kí tự đầu tiên (mã 0 -> mã 31) được dụng làm kí tự điều
khiển
– VD: kí tự mã 13 điều khiển xuống dòng, mã 9 điều khiển tao 1
khoảng tab, …
• Các hằng kí tự điều khiển được biểu diễn bằng cách thêm
tiền tố ‘\’. Ví dụ:

– ’\n’ Biểu diễn kí tự mã 13 xuống dòng


– ’\t’ Biểu diễn kí tự mã 9 khoảng tab
– ’\0’ Biểu diễn kí tự nul mã 0
– ’\’’ Biểu diễn kí tự ’
– ’\\’ Biểu diễn một kí tự \
Chuỗi kí tự
• Là 1 nhóm các kí tự kết hợp thành 1 chuỗi văn bản
• Các chuỗi kí tự được biểu diễn trong cặp nháy kép
“”
• (cặp nháy đơn ‘’ chỉ biểu diễn 1 kí tự).
• Ví dụ:
– “Hello world!”
– “Line1\nLine2\nLine3”
– “I\’m a teacher”
Logic
• Trong C, mọi con số ngoài giá trị thông thường còn
thể hiện 1 giá trị logic,
• sai nếu số có giá trị = 0,
• đúng nếu số có giá trị khác 0.
– Thông thường trong lập trình 1 được dùng để biểu diễn
giá trị đúng về logic.
• Ví dụ:
int i = 1;
if ( i )
{
printf(“giá trị đúng”);
}
Chương trình ví dụ

#include <stdio.h>
#include <limits.h>

int main()
{
printf("Kich co luu tru cho so nguyen (int) la: %d \n", sizeof(int));
return 0;
}
In giá trị lớn nhất và nhỏ nhất kiểu float

#include <stdio.h>
#include <float.h>

int main() {
printf("Lop luu tru cho so thuc (float) la: %d \n", sizeof(float));
printf("Gia tri so thuc duong nho nhat la: %E\n", FLT_MIN );
printf("Gia tri so thuc duong lon nhat la: %E\n", FLT_MAX );
printf("Do chinh xac: %d\n", FLT_DIG );
return 0;
}
Các kiểu đơn
Tên kiểu Ý nghĩa Kích thước Miền dữ liệu
char Kí tự; 1 byte -128  127
Số nguyên có dấu

short int Số nguyên có dấu 2 byte -32.76832.767


int Số nguyên có dấu 2 hoặc 4 byte

long Số nguyên có dấu 4 byte -2,147,483,648


 2,147,483,647
long int
float Số thực dấu phẩy 4 byte  3.4E-38 
động,  3.4E+38
độ chính xác đơn
double Số thực dấu phẩy 8 byte 1.7E-308 
động,  1.7E+308
độ chính xác kép

41
Các kiểu kết hợp
Với số nguyên, thêm từ khóa unsigned để chỉ ra số không dấu
Kiểu dữ liệu Ý nghĩa Kích thước Miền dữ liệu
unsigned char Số nguyên 1 byte 0  255
không dấu

unsigned short Số nguyên 2 byte 065.535


không dấu
unsigned int Số nguyên 2 hoặc 4 byte
không dấu
unsigned long Số nguyên 4 byte 0  4,294,967,295
unsigned long int không dấu
long double Số thực dấu 10 byte 3.4E-4932 
phẩy động, 1.1E+4932
void Là kiểu đặc biệt, không có kích thước

42
VÀO/RA DỮ LIỆU
SOICT-HUST
Nội Dung Khóa Học
1st Tổng quan lập trình máy tính
2nd Ngôn ngữ lập trình C
3rd Kiểu dữ liệu
4th Vào ra dữ liệu
5th Biểu thức trong ngôn ngữ C
6th Lệnh rẽ nhánh
7th Lệnh lặp (1)
8th Lênh lặp (2)
9th Hàm
10th Mảng
11th Con trỏ
12th Chuỗi
13th Cấu trúc dữ liệu
14th Vào ra với file
15th Final Exam

2
Nội Dung Bài Học

• Xuất dữ liệu
• Cú pháp
• Các đặc tả dữ liệu in
• Khuôn dạng xuất dữ liệu

• Nhập dữ liệu

3
Vào ra dữ liệu
• Để đọc và xuất dữ liệu trong C, ta sử dụng 2 hàm
thư viện cơ bản có trong tệp tiêu đề <stdio.h>
• printf() – in ra màn hình,
• có thể chấp nhận tham số là biến để in giá trị
của nó
• scanf() – lấy giá trị từ thiết bị đầu vào chuẩn
và gán nó cho các biến
Hàm in dữ liệu (ra màn hình)

• printf()
• Mục đích

• Hiển thị ra màn hình các loại dữ liệu cơ bản


Số nguyên, số thực, kí tự, xâu kí tự
• Tạo một số hiệu ứng hiển thị đặc biệt
Xuống dòng, sang trang,…
• Cú pháp
printf(xau_dinh_dang [, DS_tham_so]);

5
Cú pháp
printf(xau_dinh_dang [, DS_tham_so]);
• Xau_dinh_dang: Là một xâu qui định cách thức hiển thị
dữ liệu ra màn hình máy tính.
• Bao gồm các nhóm kí tự định dạng
• Nhóm kí tự định dạng thứ k xác định quy cách hiển thị tham
số thứ k trong DS_tham_số
• Số lượng tham số trong DS_tham_số bằng số lượng nhóm các kí tự
định dạng trong xâu_định_dạng.
• DS_tham_so: Danh sách các biến/biểu thức sẽ được hiển
thị giá trị lên màn hình theo cách thức được qui định
trong xau_dinh_dang.

6
Cú pháp (tiếp)
printf(xau_dinh_dang [, DS_tham_so]);
• Cách sử dụng đơn giản nhất: chỉ để in 1 chuỗi
printf ("Hello world!");

• In giá trị 1 biến nguyên:


int number = 42;
printf ("Some number = %d",number);

Kết quả in: Some number = 42

%d trong xâu định dạng được gọi là kí tự định dạng


dữ liệu.
Ví dụ
#include <stdio.h>
void main() {
int a = 5;
float x = 1.234;
printf("Hien thi mot bieu thuc nguyen %d và
mot so thuc %f",2 * a, x);
}

Kết quả: Hien thi mot bieu thuc nguyen 10 va mot so thuc
1.234000

8
printf()
• Chuỗi định dạng trong lệnh
printf("<chuỗi định dạng>", <ds biến hoặc giá trị>);

• Phải tương ứng với danh sách tham số về số lượng, kiểu


dữ liệu và thứ tự.

• Luôn được đặt bên trong cặp dấu “”

• Bao gồm 1 hoặc nhiều thành phần:


• Ký tự văn bản (Text characters)
• Các ký tự không in được:tab (\t), xuống dòng (\n),
khoảng trắng, …
• Kí tự định dạng
Nhóm ký tự định dạng
• Bắt đầu với %
• Không được in ra mà được thay thế vào đó là 1 giá trị!
– Ví dụ: %d chỉ ra giá trị thay thế là của 1 số nguyên được in ra
dưới dạng thập phân.
• Mỗi nhóm kí tự định dạng chỉ dùng cho 1kiểu dữ liệu”
Ví dụ: %d dùng cho kiểu nguyên, %f dùng cho kiểu thực
• DS_tham_so phải phù hợp với các nhóm kí tự định dạng
trong xau_dinh_dang về:
• Số lượng;
• Thứ tự
Nếu không phù hợp sẽ hiển thị
• Kiểu dữ liệu; ra kết quả không như ý
printf(” %d ”, 3.14); →-31457

10
Các ký tự định dạng
Ký tự Kiểu dữ liệu Kết quả
%i, %d int, char Số thập phân
%o int, char Số hệ 8
(không có 0 đằng trước)
%x %X int, char Số hệ hexa
(chữ thường/chữ hoa)
%u unsigned int/char Số thập phân

11
Các ký tự định dạng
Ký tự Kiểu dữ liệu Kết quả
%ld, %li long Số thập phân
%lo long Số hệ 8
(không có 0 đằng trước)
%lx, %LX long Số hệ hexa
(chữ thường/chữ hoa)
%lu unsigned long Số thập phân
Nhận xét: Với kiểu long, thêm ký tự l ngay sau dấu %

12
Các ký tự định dạng
Ký tự Kiểu dữ liệu Kết quả
%f float/double Số thực dấu phẩy tĩnh
%e, %E float/double Số thực dấu phẩy động
%c int, char Kí tự đơn lẻ
%s char [] Hiển thị xâu kí tự kết
thúc bởi ‘\0’
%% Hiển thị kí tự %

13
In giá trị theo nhiều định dạng
#include <stdio.h>

int main()
{
char c = ‘A';

printf(“In c theo ki tu la %c\n", c);


printf(“In c theo so nguyen la %d\n", c);
printf(“In c theo hexa la %x", c);

return 0;
}

Output: In c theo ki tu la A
In c theo so nguyen la 65
In c theo hexa la 41
In theo khuôn dạng
• Để tạo khuôn in đẹp ta sử dụng các kí tự
điều khiển như ‘\n’, ‘\t’, …
• Có thể sử dụng thêm các lựa chọn để tạo
khuôn cho giá trị in như sau:
% [-] [fwidth] [.p] X
– [fwidth] độ rộng để in giá trị
– [-] căn lề trái (mặc định căn lề phải).
– [.p] số ô dành để in số sau dấu chấm.
Ví dụ
• printf("\n%3d %15s %-3c", 1, "nguyen van a", 'g');
• printf("\n%3d %15s %3c", 2, "tran van b", 'k');

1 nguyen van ag
2 tran van b k

16
Ví dụ
Giá trị Đặc tả In ra
42 %6d | 42|
42 %-6d |42 |
'z' %3c | z|
'z' %-3c |z |
2.71828 %10f | 2.71828|
2.71828 %10.2f | 2.71|
2.71828 %-10.2f |2.71 |
2.718 %.4f |2.7180|
2.71828 %10e |2.71828e+00|
"printf" %s |printf|
"printf" %10s | printf|
In các ký tự đặc biệt
• In các chữ cái

• Hầu hết các ký tự đều có thể được in ra

• Một số ký tự ”có vấn đề” như là dấu nháy kép "

• Phải được biểu diễn bởi ký tự thoát xâu


• Biểu diễn bới dấu \ và một ký tự đặc biệt
In các ký tự đặc biệt
Ký tự đặc biệt Ý nghĩa
\a Báo thức hoặc tiếng bíp
\b Dấu Backspace
\f Form Feed
\n Xuống dòng mới (LF)
\r Về đầu dòng(CR)
\t Dấu Tab ngang
\v Dấu Tab dọc
\\ Dấu ngạch chéo ngược
\' Dấu nháy đơn
\" Dấu nháy kép
\? Dấu chấm hỏi
Chú ý
• Nếu số chỗ cần để hiển thị dữ liệu lớn hơn được cung cấp trong định
dạng  Tự động cung cấp thêm chỗ mới để hiển thị đầy đủ, không cắt
bớt nội dung của dữ liệu.
• Ví dụ:
printf(“%2d”, 1234); → 1234
printf(“%6.3f”, 123.456); → 123.456
printf(“%12.6e”, 123.456); →1.234560e+02
printf(“%12.3e”, 123.456); →1.235e+02

20
Nội Dung Bài Học

• Xuất dữ liệu

• Nhập dữ liệu
• Cú pháp
• Định dạng dữ liệu nhập

21
Hàm nhập dữ liệu
• scanf()
• Mục đích
• Dùng để nhập dữ liệu từ bàn phím
• Ký tự đơn lẻ
• Chuỗi ký tự
• Số nguyên: hệ 10, 8, 16
• Số thực
Dấu phẩy tĩnh; Dấu phẩy động
• Cú pháp
scanf(xau_dinh_dang[,DS_dia_chi]);

22
Cú pháp
scanf(xau_dinh_dang [, DS_dia_chi]);
• Xau_dinh_dang: Gồm các ký tự được qui định cho từng
loại dữ liệu được nhập vào.
• Ví dụ: dữ liệu định nhập kiểu nguyên thì xâu định dạng là :
%d
• DS_dia_chi: bao gồm địa chỉ của các biến (toán tử &),
phân tách nhau bởi dấu phẩy (,)
• Phải phù hợp với các kí tự định dạng trong
xau_dinh_dang về số lượng, kiểu, thứ tự

23
scanf()
• Ví dụ
int i; char ch; float x;
scanf ("%d%c%f", &i, &ch, &x);

– Câu lệnh này cho phép chương trình đợi người sử dụng
nhập các giá trị số nguyên, kí tự và số thực

– & có nghĩa lấy địa chỉ của biến trong bộ nhớ

– Phải truyền địa chỉ của biến nhận giá trị cho hàm scanf().
Định dạng nhập
• Các kí tự định dạng nhập không hoàn toàn
giống cho xuất mà thương có tính chính xác hơn
%c – giá trị nhập là kí tự
%d – giá trị nguyên kiểu int
%ld – giá trị nguyên kiểu long
%x – giá trị nguyên dạng hexa
%f – giá trị thực kiểu float
%lf – giá trị thực kiểu double
%s – chuỗi kí tự
Ví dụ #include <conio.h>
#include <stdio.h>
void main(){
// khai bao bien
int a; float x;
char ch; char str[30];
// Nhap du lieu
printf(“Nhap vao mot so nguyen:”);
scanf(“%d”,&a);
printf(“\nNhap vao mot so thuc:”);
scanf(“%f”,&x);
printf(“\n Nhap vao mot ki tu:”);
scanf(“%c”,&ch);

26
Ví dụ
printf(“\nNhap vao mot xau ki tu:”);
scanf(“%s”,str);

// Hien thi du lieu vua nhap vao


printf(“\nNhung du lieu vua nhap vao”);
printf(“\nSo nguyen : %d”,a);
printf(“\nSo thuc : %5.2f”,x);
printf(“\nKy tu : %c”,ch);
printf(“\nXau ky tu : %s”,str);
}

27
Ví dụ→Kết quả thực hiện

28
Ví dụ: tính diện tích hình tròn
/* Chương trình tính diện tích hình tròn */
#include <stdio.h>

int main()
{
float r, s;

printf(“Nhap ban kinh hinh tron: ");


scanf("%f",&r);

s = 3.14*r*r;
printf(“Dien cua hinh tron s=%f", s);

return 0;
}
Ví dụ: Đọc 2 số nguyên, đưa ra tổng,
hiệu, tích…
#include <stdio.h>
int main() {
int A, B;
printf("Nhap vao 2 so nguyen : "); scanf("%d %d“,&A,&B);
printf("\n");
printf("Tong %d + %d = %d \n", A, B, A + B);
printf("Hieu %d - %d = %d\n", A, B, A - B);
printf("Tich %d x %d = %d\n", A, B, A * B);
printf("Thuong %d / %d = %.3f\n", A, B, (float)A / B);
printf("Chia nguyen %d / %d = %d\n", A, B, A / B);
printf("Chia du %d %% %d = %d\n", A, B, A % B);
printf("\n");
return 0;
}
30
Ví dụ: Đọc 2 số nguyên, đưa ra tổng,
hiệu, tích…

31
Ghi chú
• Thông tin được gõ vào từ bàn phím, được lưu ở vùng
đệm trước khi được xử lý bởi hàm scanf()→Hàm scanf()
đọc từ vùng đệm
#include <stdio.h>
int main(){
int a, b;
scanf("%d",&a);
scanf("%d",&b);
printf ("%d %d", a, b);
return 0;
}

32
Lỗi thường gặp
• Tìm lỗi trong đoạn chương trình sau

float a, b, c;
scanf("%f", a);
scanf("%d", &b);
scanf("%f", &c);
scanf("%f", &d);
Ví dụ nhập xuất số nguyên khác kiểu
#include <stdio.h>

int main(){
int i ;
scanf("%o", &i);
printf("%d", i);
return 0;
}

Input: 70
Output: 56
Quét dữ liệu
• Giá trị lưu vào các biến được quét dựa trên dữ liệu nhập của
người sử dụng.
• Quá trình quét được thực hiện tuần tự và có thể dừng nếu có lỗi
khi quét.
• Ví dụ:
int i = 0;
char ch = '*';
float x = 0;
scanf("%d%c%f",&i,&ch,&x); • Nếu nhập : 1x2.3
printf ("%d %c %f\n",i,ch,x); • Kết quả in: 1 x 2.300000

• Nếu nhập : 1 x 2.3


• Kết quả in: 1 0.000000
Các quy tắc cần lưu ý
• Khi đọc số
• Hàm scanf() quan niệm rằng mọi kí tự số, dấu chấm (‘.’) đều
là kí tự hợp lệ.
• Số thực dấu phẩy động, chấp nhận ký tự e/E

• Khi gặp các dấu phân cách như tab, xuống dòng hay dấu
cách (space bar), scanf() sẽ hiểu là kết thúc nhập dữ liệu
cho một số

36
Các quy tắc cần lưu ý (tiếp)
• Khi đọc kí tự
• mọi kí tự có trong bộ đệm của thiết bị vào chuẩn đều là
hợp lệ, kể cả các kí tự tab, xuống dòng hay dấu cách

• Khi đọc xâu kí tự:


• nếu gặp các kí tự dấu trắng, dấu tab hay dấu xuống
dòng thì nó sẽ hiểu là kết thúc nhập dữ liệu cho một
xâu kí tự.

37
Bỏ qua kí tự
• Trong định dạng của xâu nhập ta có thể
chỉ ra một số kí tự cần bỏ qua khi quét
• Ví dụ:
scanf("%d-%d-%d", &day, &month, &year);

• Bỏ qua kí tự - giữa các con số ngày tháng năm


• Nếu xâu nhập là: 1-1-2000 thì ta có day=1,
month=1, year=2000
Kết quả trả về bởi scanf()
• Dạng tổng quát của scanf() là
n = scanf ("...", <ds địa chỉ biến>);
– Giá trị n trả lại là số phần tử đọc được hoặc giá trị
EOF thể hiện không còn dữ liệu để quét.
• Ví dụ:
n=scanf("%d-%d-%d", &day, &month, &year);
– Nếu nhập: 1-1-2000 thì day=1, month=1, year=2000,
n=3
– Nếu nhập: 1/1/2000 thì day=1 và sau đó dừng quét
trả về n=1
Kiểm tra khi nhập
• Khi nhập giá trị cho 1 biến nên kiểm tra xem việc
nhập có thành công hay không với đoạn chương
trình như dưới đây

int n;
printf(“n = ”);
if (scanf(“%d", &n) != 1)
printf(“Không nhập được giá trị cho n”);
BIỂU THỨC TRONG
NGÔN NGỮ C
huydq@soict.hust.edu.vn
Nội Dung Chính

• Biểu thức

• Toán tử

2
Biểu thức
• Kết hợp các giá trị bằng toán tử hoặc gọi hàm
để tạo giá trị mới
• Giá trị trả về luôn có kiểu xác định
• Các toán tử có thể được phân loại
– theo ngôi (1 hoặc 2 toán hạng)
– theo chức năng (toán học, logic, …)
Mục đích sử dụng
• Làm vế phải của lệnh gán.
• Làm toán hạng trong các biểu thức khác.
• Làm tham số thực sự trong lời gọi hàm.
• Làm biểu thức kiểm tra trong các cấu trúc điều khiển
• Cấu trúc lặp: for, while, do while.
• Cấu trúc rẽ nhánh: if, switch.

4
Các loại biểu thức

• Biểu thứ số học

• Biểu thức quan hệ

• Biểu thức logic

5
Biểu thức số học
• Là biểu thức mà giá trị của nó là các đại lượng số học
(số nguyên, số thực).
• Sử dụng các toán tử là các phép toán số học (+,-,*,/…)

• Các toán hạng là các đại lượng số học (hằng số, biến, biểu
thức khác).

• Chú ý: phép chia số nguyên/số nguyên → số nguyên

6
Ví dụ biểu thức
1 + 2 * 3 - 4 / 5
= 1 + (2 * 3) - (4 / 5)

6.2
Ví dụ (tiếp)
1 + 2 * 3 - 4 / 5 =
1 + (2 * 3) - (4 / 5)

Chia hai số nguyên cho


kết quả vẫn là một số
nguyên
Ví dụ (tiếp)

1 + 2 * 3 - 4 / 5 =
1 + (2 * 3) - (4 / 5)
7
= 0
Ví dụ (tiếp)

• Dùng số thực để tạo biểu thức có kết quả là số thực

1 + 2 * 3 - 4.0 / 5
= 1 + (2 * 3) - (4.0 / 5)
= 1 + 6 - 0.8
= 6.2
Biểu thức quan hệ
• Là những biểu thức có sử dụng các toán tử quan hệ như lớn hơn,
nhỏ hơn, khác nhau…
• Chỉ có thể trả về 1 trong 2 giá trị logic Đúng (TRUE) hoặc Sai
(FALSE)
• Ví dụ

11
Biểu thức logic
• Là biểu thức trả về các giá trị logic Đúng/Sai
• Các phép toán logic gồm có
• AND VÀ logic, sử dụng toán tử &&
• OR HOẶC logic, sử dụng toán tử ||
• NOT PHỦ ĐỊNH, sử dụng toán tử !

• Biểu thức quan hệ là trường hợp riêng của biểu thức logic.

• Biểu thức logic cũng trả về một giá trị số học 0/1

12
Biểu thức logic →Ví dụ

13
Dùng int như lôgic
• Trong C, các giá trị lôgic được biểu diễn bằng
số nguyên
– Giá trị nguyên 0 là sai.
– Giá trị nguyên khác 0 là đúng (thường dùng số 1)

• Mọi biểu thức trong C đều trả về kết quả số

• Một biểu thức lôgic được đánh giá là đúng (true)


sẽ trả về giá trị 1, ngược lại là 0.
Nội Dung Chính

• Biểu thức

• Toán tử

15
Các toán tử chính
• Toán tử số học
• Toán tử quan hệ
• Toán tử logic
• Toán tử logic bit
• Toán tử gán

16
Các toán tử số học
Toán Ý nghĩa Kiểu dữ liệu của toán Ví dụ
tử hạng (int a = 12; float x=3.0)
- Đảo dấu float, double, int, long,.. -12, -12.34, - a, - x
(Số nguyên hoặc thực)
+ Cộng float, double, int, long,.. 12 + x
- Trừ float, double, int, long,.. 12.0 - x
* Nhân float, double, int, long,.. 12 * 3.0
(Số nguyên hoặc thực) 12 * 3
/ Chia Nếu có ít nhất 1 toán 17.0/3.0 → 5.666667
hạng là số thực 17/3.0 → 5.666667
17.0/3 → 5.666667
/ Chia lấy thương Số nguyên int, long,.. 17/3 → 5
% Chia lấy số dư Số nguyên: int, long,.. 17%3 → 2

17
Các toán tử quan hệ
<, >, <=, >=, ==, !=
• Dùng cho phép so sánh giá trị 2 toán hạng
• Kết quả phép so sánh là một số nguyên
• 1 nếu quan hệ có kết quả là đúng,
• 0 nếu quan hệ có kết quả sai
• Ví dụ:
• 6 > 4 → Trả về giá trị 1
• 6 < 4 → Trả về giá trị 0
• int b = (x !=y);
Nếu x và y khác nhau, biểu thức đúng và b mang giá trị 1. Ngược lại
biểu thức sai và b mang giá trị 0

18
Toán tử lôgic

• ...dùng để xây dựng các biểu thức lôgic


• Và (&&)
• Hoặc (||)
• Phủ định (!)
• So sánh bằng (==)
• So sánh khác (!=)
• So sánh lớn bé (<, >, <=, >=)
Các toán tử logic (tiếp)
• Và logic ( && ) :
• Cho kết quả đúng (trả về giá trị 1) khi cả 2 toán hạng đều đúng
(khác 0)
• Ví dụ: 3 < 5 && 4 < 6 →1; 3 < 5 && 5 > 6 → 0
• Hoặc logic ( || ):
• Cho kết quả sai (trả về giá trị 0) chỉ khi cả 2 toán hạng đều sai
(bằng 0)
• Ví dụ: 4 || 5 < 3 → 1; 5 < 5 || 2 > 6 → 0
• Phủ định logic ( ! ):
• Cho kết quả đúng (1) hoặc sai (0) khi toán hạng là sai (0) hoặc
đúng (khác 0)
• Ví dụ: !3 → 0; !(2 > 3) → 1;

20
Ví dụ
Tránh nhầm
lẫn với và bit
(&)
Tránh nhầm
lẫn với hoặc
5 && 4 → 1 bit (|)
Tránh nhầm
lẫn với đảo
bit (~) 1 || 4 → 1

! 0 → 0

! 0 || 0 && 2 → 1
“Ngắn mạch”
• Không nhất thiết phải sử dụng tất cả các toán hạng để
đánh giá một biểu thức lôgic, chỉ cần đánh đến khi giá trị
biểu thức được xác lập

1 || 2

0 || 1 || 2

1 && 0 && -1
Ví dụ toán tử so sánh
Tránh nhầm
lẫn với phép
gán (=)
3 == 4 → 0

3 != 4 → 1

3 < 4 → 1

3 < 4 && 5 > 2 → 1


Lỗi thường gặp
#include <stdio.h>

/* Loi thuong gap */

int main()
{
int score; Giá trị trả về
luôn là 1
scanf("%d", &score);

if ( score == 9 || 10 )
{
printf(“Xuat sac\n");
}
return 0; Giá trị trả về là
0 hoặc 1
}
Lỗi thường gặp (tiếp)
#include <stdio.h>

/* Chuong trinh dung */

int main()
{
int score;

scanf("%d", &score);

if ( score == 9 || score == 10 )
{
printf(“Xuat sac\n");
}
return 0;
}
Lỗi thường gặp (tiếp)
#include <stdio.h>

/* Loi thuong gap */

int main()
{
int score; Giá trị trả về
luôn là 1
scanf("%d", &score);

if ( 8 <= score <= 10 )


{
printf(“Gioi\n");
}
return 0; Giá trị trả về là
0 hoặc 1
}
Lỗi thường gặp (tiếp)
#include <stdio.h>

/* Chuong trinh dung */

int main()
{
int score;

scanf("%d", &score);

if ( 8 <= score && score <= 10 )


{
printf(“Gioi\n");
}
return 0;
}
Toán tử trên bit
• Toán tử trên bit (bitwise operator) được sử dụng với kiểu
số nguyên
Và nhị phân: Op1 & Op2
Hoặc nhị phân : Op1 | Op2
Hoặc có loại trừ nhị phân: Op1 ^ Op2
Đảo bít nhị phân : ~ Op
Dịch trái n bit: Op << n
Dịch phải n bit: Op >> n

28
Toán tử trên bit
• Một biểu thức chỉ sử dụng các toán tử trên bit không
phải là biểu thức lôgic.
• Kết quả của biểu thức này là một số nguyên bất kì

5 & 4 → 4

1 | 4 → 5

~0 → 0xFFFF
Toán tử trên bit (tiếp)
char Op1 = 83, Op2 = -38, Op = 3;
char r = Op1 & Op2; char r = Op1 | Op2;
01010011 01010011
11011010 11011010
r = 0 1 0 1 0 0 1 0 → (82) r = 1 1 0 1 1 0 1 1 → (-37)
char r = Op1 ^ Op2; char r = ~ Op2;
01010011 11011010
11011010 r = 0 0 1 0 0 1 0 1 → (37)
r = 1 0 0 0 1 0 0 1 → (-119)
unsigned char r = Op1 | Op2; r = 1 1 0 1 1 0 1 1 → 219
30
Toán tử trên bit (tiếp)
char Op1 = 83, Op2 = -38, Op = 3;
char r = Op1 >> Op;
01010011
r = 0 0 0 0 1 0 1 0 → (10)
char r = Op2 >> Op; unsigned char Op =218;
11011010 unsigned char r =Op >> 3;
r =1 1 1 1 1 0 1 1 → (-5) 11011010
r =0 0 0 1 1 0 1 1 → (27)
char r = Op2 << 2;
r = 0 1 1 0 1 0 0 0 →(104)
(unsigned) int r = Op2<<2 →?
31
Toán tử gán
Biến = Biểu_thức;

• Là toán tử được sử dụng thường xuyên


• Biểu thức bên phải dấu bằng được tính toán
• Giá trị của biểu_thức được gán cho biến ở vế trái
• Ví dụ:
• int a, b, c;
• a = 3;
• b = a + 5;
• c = a * b;

32
Toán tử gán
• Gán (=) cũng là một toán tử. Vì vậy sử dụng toán tử
này tạo ra một biểu thức và trả về giá trị
• Kết quả của biểu thức gán là giá trị bên phải của
biểu thức
• Ví dụ:
(x = 4) → 4
(y = 0) → 0
• Có thể tạo 1 biểu thức với một chuỗi toán tử gán
x=y=z=4
Lỗi thường gặp (tiếp)
#include <stdio.h>

/* Loi gap nhieu trong C */

int main()
{
int score; Nhầm với
phép gán
scanf("%d", &score);

if (score = 9 || score = 10)


{
printf(“Gioi!\n");
}
return 0;
}
Lỗi thường gặp (tiếp)
#include <stdio.h>

/* Probably the most common C error. */

int main()
{
int score;

scanf("%d", &score);

if (score == 9 || score == 10)


{
printf(“OK!\n");
}
return 0;
}
Một số toán tử gán mở rộng
Toán tử Ví dụ Biểu thức tương đương

+= x += 5 x=x+5

-= x -= 5 x=x-5

*= x *= 5 x=x*5

/= x /= 5 x=x/5

%= x %= 5 x=x%5
Toán tử tăng giảm
• ++ là toán tử tăng
++i tương đương với i = i + 1
• -- là toán tử giảm
--j tương đương với j = j - 1
• Có 2 dạng viết: tiền tố (++i) và hậu tố (i++)
• Chúng khác nhau ở giá trị trả về của biểu thức.
Ví dụ nếu i = 5
– Tiền tố trả về giá trị sau khi đã cộng, (++i) → 6
– Hậu tố trả về giá trị trước khi cộng, (i++) → 5
– Trong cả hai trường hợp giá trị của i đều tăng lên 1
Toán tử lấy địa chỉ
&Tên_biến
• Biến thực chất là một vùng nhớ của máy tính được đặt
tên → tên của biến
• Mọi ô nhớ trên bộ nhớ máy tính đều được đánh địa chỉ.
→ Mọi biến đều có địa chỉ 1
.
..
..
. 157

a D6 158

07 159

• Ví dụ: .
160

.
.
short int a = 2006;
&a là địa chỉ của ô nhớ chứa giá trị biến a

38
Toán tử điều kiện
• Có thể viết một biểu thức mà giá trị của nó phụ
thuộc vào một điều kiện

<Điều kiện> ? <Biểu thức 1> : <Biểu thức 2>

– Viết biểu thức tính max của a và b

int max,a,b;

max = ( a > b ) ? a : b;
Ép kiểu
• Phép gán chỉ thực hiện trên biến và giá trị có cùng kiểu
• C chuyển kiểu tự động cho phép gán nếu sự chuyển kiểu
đó không làm mất thông tin.
char → int → long int → float → double → long double
• Ví dụ chuyển từ int về float
int a;
float f;
f = a; /* OK */
a = f; /* KO */
• Trong trường hợp bị mất thông tin ta phải thực hiện ép
kiểu. Ví dụ nếu chuyển từ float về int
a = (int) f;
Thứ tự ưu tiên các toán tử
Mức Toán tử Chức năng Chiều
1 -> . [ ] () ++ hậu tố --hậu tố Lựa chọn, chỉ số… →
2 ++ -- ~ ! + - * & () sizeof Toán tử 1 ngôi, ép kiểu,… 
Toán tử số học lớp nhân →

Chiều kết hợp với các toán hạng


3 * / %
4 + - Toán tử số học lớp cộng →
5 >> << Dịch bit →
6 < <= > >= Toán tử quan hệ →
7 == != Bằng, khác →
8 & AND nhị phân →
9 ^ XOR nhị phân →
10 | OR nhị phân →
11 && AND logic →
12 || OR logic →
13 ?: Toán tử phỏng điều kiện 
14 = *= += <<= &= ... Toán tử gán 
41
Thứ tự ưu tiên các toán tử
• Nguyên tắc
• Biểu thức con trong ngoặc được tính toán trước
• Phép toán một ngôi đứng bên trái toán hạng được kết hợp với
toán hạng đi liền nó.
• Toán hạng đứng cạnh hai toán tử
• Nếu hai toán tử có độ ưu tiên khác nhau thì toán tử nào có độ ưu tiên
cao hơn sẽ kết hợp với toán hạng
• Nếu hai toán tử cùng độ ưu tiên thì dựa vào trật tự kết hợp của các
toán tử để xác định toán tử được kết hợp với toán hạng.
• Ví dụ
a < 10 && 2 * b < c  ( a < 10 ) && ( ( 2 * b ) < c )
Chú ý: int x = 5, a = 5 * x++; → a = 25, x = 6

42
Ví dụ
const int N=10; int a= 3, b=4, c;
float S= 0.0; c = a++ * ++b;
int b;
S = N/3 +1;
a= ? b= ? c= ?
b=(S>4);

S= ? b = ?
int k ,num=30;
k = num>5 ? (num <=10 ? 100 : 200): 500;
k=?

43
Ví dụ
const int N=10; int a= 3, b=4, c;
float S= 0.0; c = a++ * ++b;
int b;
S = N/3 +1;
a=4 b=5 c=15
b=(S>4);

S= 4 b = 0
int k ,num=30;
k = num>5 ? (num <=10 ? 100 : 200): 500;
k=200

44
Ví dụ
7+5&&4<2+3-2/3||5>2+1
(7+5)&&4<2+3-(2/3)||5>(2+1)
12 &&4<(2+3-0) ||(5>3)
12 &&(4<5)||1
(12&&1)||1
1||1
= 1
C PROGRAMMING
LANGUAGE

TS. Đỗ Quốc Huy


Bộ môn Khoa Học Máy Tính
huydq@soict.hust.edu.vn

1
Nội Dung Khóa Học
1st Tổng quan lập trình máy tính
2nd Ngôn ngữ lập trình C
3rd Kiểu dữ liệu
4th Vào ra dữ liệu
5th Biểu thức trong ngôn ngữ C
6th Lệnh rẽ nhánh
7th Lệnh lặp (1)
8th Lênh lặp (2)
9th Hàm
10th Mảng
11th Con trỏ
12th Chuỗi
13th Cấu trúc dữ liệu
14th Vào ra với file
15th Final Exam

2
Cấu trúc điều khiển
• Lệnh rẽ nhánh
• if
• if/else
• Lệnh lựa chọn switch
Lệnh if

Cú pháp:
if ( <biểu thức điều kiện> ) <câu lệnh>

• Xác định điều kiện 1 lệnh hay 1 khối lệnh được


thực hiện
• Thi hành chỉ thị lựa chọn trong 1 giải thuật
• Nếu điều kiện là đúng (kết quả của biểu thức khác
0), câu lệnh hoặc khối lệnh được thực hiện.
Lưu Đồ Khối if
if (biểu_thức_điều_kiện ) lệnh_1;
lệnh_kế_tiếp;

true
Biểu_thức_điều Lệnh_1
_kiện?

false

Lệnh_kế_tiếp
Lưu Đồ Khối if
Nếu (number %2 != 0) thì in_ra(“số lẻ”);
lệnh_kế_tiếp;

Quyết định có thể được


đưa ra dựa trên biểu thức
bất kỳ.
true
number %2 != 0 print “Số lẻ” zero - false
nonzero - true

false
Ví dụ:
3 - 4 là true
Lệnh_kế_tiếp
Ví dụ
• Nhập vào một số nguyên rồi kiểm tra có phải là số lẻ
#include <stdio.h>
int main()
{
int number;

printf(“Nhap so integer: ");


scanf("%d", &number);
Không có
then ở đây
if (number % 2 != 0)
printf(“%d la so le“, number);

return 0;
}
Lỗi thường gặp

Nhầm toán tử
so sánh == Không có
dấu ; ở đây

if (number % 2 = 0);
{
printf(“%d\n”, number);
printf(“La so chan");
}

Dấu ; tạo một lệnh rỗng sau mệnh đề if


Chú ý lỗi

• Lỗi sai ký pháp


• Gây ra bởi trình biên dịch

• Lỗi logic:
• Ảnh hưởng khi thực hiện chương trình
• Không nguy hiểm: chương trình chạy nhưng cho ra kết quả
không chính xác
• Nguy hiểm: chương trình kết thúc bất thường
Lệnh đơn và Lệnh ghép (compound)

• Câu lệnh là các dòng chỉ lệnh trong chương trình


kết thúc bằng dấu chấm phảy (;)
• Ví dụ:
int bien_1, bien_2=1;
bien_1 = 2;
bien_2 += bien_1;
return bien_2;

• 1 dấu chấm phảy đơn là 1 lệnh rỗng


Lệnh đơn và Lệnh ghép (compound)
• Lệnh đơn:
• Là biểu thức theo sau bởi dấu ‘;’
• Ví du: x= 0; i++; printf(“Hello”);
• Lệnh ghép (khối lệnh)
• Là tập hợp các câu lệnh (đơn và ghép) được đặt trong cặp
ngoặc nhọn { }
• C cho phép khai báo biến trong một khối lệnh
• Phần khai báo phải nằm trước các câu lệnh
• Chú ý:
• Lệnh ghép có thể đặt tại bất cứ chỗ nào mà cú pháp cho phép đặt 1 câu
lệnh đơn
• Không đặt dấu ‘;’ sau một khối lệnh đặt trong ngoặc nhọn

11
Lệnh ghép/Khối lệnh
• 1 khối lệnh bao gồm các câu lệnh đặt trong cặp dấu { }
{
lệnh_1;
lệnh_2;
....
lệnh_n;
}

{
V.d. number = number + 1;
printf("%d\n", number);

}
Cấu trúc lồng nhau
• Trong lệnh ghép có thể chứa lệnh ghép khác
{
lệnh;
{
lệnh;
{
lệnh;
....
}
…..
}
…..
}
13
Cấu trúc lồng nhau
• Có thể khai báo biến trong khối lệnh
{//Khai báo đối tượng cục bộ trong khối
lenh;
{//Khai báo đối tượng cục bộ trong khối
lenh;
...
} Nếu các đối tượng được
… khai báo trùng tên nhau ?
}

14
Ví dụ

15
Ví dụ→Kết quả thực hiện

Biến địa phương / Biến toàn cục

16
Lệnh else
if ( <điều kiện> )
<câu lệnh 1>
else
<câu lệnh 2>

• Chỉ có thể đi sau 1 lệnh if


• Câu lệnh liền sau else được thực hiện
nếu khối lệnh if không được thực hiện
Lưu đồ của cấu trúc if/else
if (biểu_thức_điều_kiện == true)
lệnh_1;
else
lệnh_2;
lệnh_kế_tiếp;
Ví dụ
• Kiểm tra tính chẵn lẻ của một số nguyên
#include <stdio.h>

int main()
{
int number;

printf(“Nhap integer: ");


scanf("%d", &number);

if (number % 2 != 0)
printf("%d la so le\n", number);
else
printf("%d la so chan\n", number);

return 0;
}
Ví dụ: So sánh 2 số thực

20
Ví dụ: So sánh 2 số thực được nhập vào

if(a < b)
max = b;
max = a > b ? a : b;
else
max = a;
21
Lỗi thường gặp

if (number % 2 != 0)
Không có
dấu ; ở đây {
printf(“%d\n”, number);
printf(“La so le");
};
else
{
printf(“%d\n”, number);
printf(“La so chan");
}
Lệnh if lồng (else-if)
Ví dụ
if (expr1) if (ch >= ’a’ && ch <= ’z’)
statement1 {
printf(“%c la chu thuong”, ch);
else if (expr2) }
else if (ch >= ’A’ && ch <= ’Z’)
statement2 {
else if (expr3) printf(“%c la chu hoa”. ch);
}
statement3 else if (ch >= ’0’ && ch <= ’9’)
{
else printf(“%c la chu so”, ch);
statement4 }

Cấu trúc lệnh if lồng được sử dụng khi có sự lựa chọn với nhiều điều kiện
khác nhau
Cấu trúc if / if… else lồng nhau
Cấu trúc if.. và if …else có thể lồng nhau
– Khi đó else sẽ tương ứng với if (phía trên,
chưa có else) gần nhất
if (đ/k_1) if (đ/k_1)
if (đ/k_2) { if (đ/k_2)
lệnh_1; lệnh_1;
else }
lệnh_2; else
lệnh_2;
24
Cấu trúc if / if… else lồng nhau →Ví dụ
int a, b, c = 10;
if (a==0) a ≠ 0, b=? →c=
if (b == 0) 10
c = 20; a=0, b=0 →c = 20
else
c = 30; a=0, b ≠ 0 →c =
30
if (a==0){ a ≠ 0, b=? →c=
if (b == 0) 30
c = 20; a=0, b=0 →c = 20
}else
c = 30; a=0, b ≠ 0 →c=
10
25
Bài tập
• Viết chương trình tính số ngày của một tháng.

• Giải thuật
if (tháng 4, 6, 9, 11)
output “30 ngày”
else if (tháng 2)
output “28 hoặc 29 ngày”
else
output “31 ngày”
#include <stdio.h>

int main() {
unsigned char thang;
printf("Nhap vao thang:");
scanf("%d",&thang);
if (thang == 4 || thang == 6 || thang == 9 || thang == 11)
printf("30 ngay");
else if (thang == 2)
printf("28 hoac 29 ngay");
else if (thang > 0 && thang < 13)
printf("31 ngay");
else printf("Du lieu khong hop le");
return 0;
}
Cấu trúc điều khiển
• Lệnh rẽ nhánh if
• Lệnh lựa chọn switch
Lệnh lựa chọn switch
switch (bieu_thuc)
{
case gia_tri_1: lenh_1; [break];
case gia_tri_2: lenh_2; [break];

[default: lenh_n+1; [break];]
}
Câu_lệnh_tiếp
29
Lệnh switch

• Một lệnh đa lựa chọn dựa trên các giá trị nguyên
– Giống như khi sử dụng cấu trúc lệnh if lồng
• Biểu thức phải có giá trị nguyên

• Khi thực hiện giá trị của biểu thức được tính và trường
hợp case có giá trị khớp với biểu thức sẽ được thực
hiện

• Nếu không có case nào tương ứng, tập lệnh tuỳ chọn
default được thực hiện
Lệnh lựa chọn switch
Tính g/trị
Biểu thức

Sai Sai Sai


g/trị b/t=G/trị 1 g/trị b/t=G/trị 2  default ?

Đúng Đúng Đúng

Lệnh_1 Lệnh_2 Lệnh_n+1

Sai Sai Sai


 break ?  break ?  break ?
Đúng Đúng Đúng

Câu_lệnh_tiếp
31
sử dụng break
• Khi một case của lệnh switch được tìm thấy, các lệnh
được thực hiện bắt đầu từ điểm này
• Nó tiếp tục thực hiện tất cả các lệnh tiếp theo trừ khi gặp phải một
break
• Lệnh break làm chương trình nhảy lập tức sang câu lệnh sau lệnh
switch
int a = 1;
switch ( a ) {
case 1: Output:
printf("a=1\n"); a=1
case 2: a=2
printf("a=2\n");
break;
case 3:
printf("a=3\n");
}
Cấu trúc lựa chọn switch→Ví dụ 1
Lập trình đọc từ bàn phím một số
nguyên 1N 10 và đưa ra từ tiếng Anh
tương ứng

33
Cấu trúc lựa chọn switch→Ví dụ 1

34
Cấu trúc lựa chọn switch→ Thực hiện

35
Cấu trúc lựa chọn switch→Ví dụ 2
Nhập vào số nguyên không âm, đưa
ra ngày trong tuần tương ứng (theo
số dư khi chia cho 7).

36
Cấu trúc lựa chọn switch→Ví dụ 2

37
Cấu trúc lựa chọn switch →Thực hiện

39
Cấu trúc lựa chọn switch
Có thể sử dụng đặc điểm Không có lệnh
break chương trình sẽ tự động chuyển xuống
thực hiên các câu lệnh tiếp sau để viết chung
mã lệnh cho các trường hợp khác nhau
nhưng được xử lý như nhau

40
Ví dụ 3
printf(“Yes/No (Y/N)?”);
scanf(“%c”, &ch)
switch (ch)
{
case 'y' :
case 'Y' :
printf(“say yes”);
break;
default :
printf(“say no”);
}
Ví dụ 4
• Xác định học lực dựa trên điểm kiểm tra
switch (diem) if (diem==9||diem==10)
{ {
case 9: printf(“Loai gioi\n");
case 10: }
printf(“Loai gioi\n"); else if (diem==7||diem==8)
break; {
case 7: printf(“Loai kha\n");
case 8: }
printf(“Loai kha\n"); else if (diem==5||diem==6)
break; {
case 5: printf(“Loai TB\n");
case 6: }
printf(“Loai TB\n"); else
break; {
default: printf(“Loai yeu\n");
printf(“Loai yeu\n"); }
}
Bài tập

Trong một năm các tháng có 30 ngày là 4, 6, 9, 11


còn các tháng có 31 ngày là 1, 3, 5, 7, 8, 10, 12.
Riêng tháng hai có thể có 28 hoặc 29 ngày.
Hãy viết chương trình nhập vào 1 tháng, sau đó đưa
ra kết luận tháng đó có bao nhiêu ngày

43
Cấu trúc lựa chọn switch→ Ví dụ

44
Cấu trúc lựa chọn switch→ Thực hiện

45
Cấu trúc lựa chọn switch→Lưu ý
• Giá trị của biểu thức trong cấu trúc switch
phải là số nguyên (kiểu đếm được)
– Phải có kiểu dữ liệu là char, int, long
• Các giá trị sau từ khóa case (gia_tri_1,
gia_tri_2,.. ) cũng phải là số nguyên

Điều kiện trong cấu trúc if / if..else cho phép


làm việc với các kiểu dữ liệu khác số nguyên

46
Bài tập
1. Viết chương trình nhập 2 (hoặc 3) số
nguyên và tìm giá trị lớn nhất của chúng.
2. Viết chương trình để giải phương trình
ax+b=0 với a, b là các hệ số được nhập
vào từ bàn phím.
Ví dụ: Giải phương trình ax + b = 0

48
C PROGRAMMING
LANGUAGE

TS. Đỗ Quốc Huy


Bộ môn Khoa Học Máy Tính
huydq@soict.hust.edu.vn

1
Nội Dung Khóa Học
1st Tổng quan lập trình máy tính
2nd Ngôn ngữ lập trình C
3rd Kiểu dữ liệu
4th Vào ra dữ liệu
5th Biểu thức trong ngôn ngữ C
6th Lệnh rẽ nhánh
7th Lệnh lặp (for…)
8th Lênh lặp (while,do…while)
9th Hàm
10th Mảng
11th Con trỏ
12th Chuỗi
13th Cấu trúc dữ liệu
14th Vào ra với file
15th Final Exam

2
Vòng lặp (loop)

• Được sử dụng để thực hiện lặp nhiều lần cùng một


lệnh hay khối lệnh
• C hỗ trợ tạo vòng theo nhiều cách thức mềm dẻo để
quyết định số lần lặp và khi nào thì kết thúc vòng lặp
• Các loại vòng lặp hỗ trợ trong ngôn ngữ C là
– for
– while
– do…while
Lệnh for

for ( <khởi tạo> ; <điều kiện> ; <cập nhật> )


<câu lệnh>

• Là một dạng của vòng lặp while với các thành phần khởi
tạo và cập nhật được chỉ ra trong câu lệnh
• Các thành phần trong câu lệnh for là tuỳ chọn. Khi điều
kiện lặp không được chỉ rõ thì nó được lấy giá trị ngầm
định là đúng.
• Cập nhật luôn thực hiện sau câu lệnh của vòng lặp
Cấu trúc lặp for
Dùng để thực hiện nhiều lần một công việc

for([b.thuc_1];[b.thuc_2];[b.thuc_3]) Lệnh;

• b.thuc_1: Khởi tạo giá trị ban đầu cho vòng lặp
• b.thuc_2: Điều kiện tiếp tục vòng lặp
• b.thuc_3: Thay đổi biến điều khiển của vòng lặp
• Lệnh: Có thể là lệnh đơn lệnh kép hoặc lệnh rỗng

5
Lưu đồ

6
Sử dụng
int i;
for(i = 0; i < 100; i++) Câu_lệnh;

int i;
for(i = 0; i < 100; i+=2)Câu_lệnh;

int i;
for(i = 100; i > 0; i--) Câu_lệnh;

7
Ví dụ 1
• Hãy đưa ra màn hình các số
nguyên dương nhỏ hơn 10.
• Cách làm: có 9 số nguyên dương
nhỏ hơn 10 là 1, 2, 3, 4, 5, 6, 7,
#include <stdio.h>
8 và 9. #include <conio.h>
• Để in 9 số nguyên dương này ta void main() {
cần sử dụng một biến nguyên int i;
đặt tên là i. for (i = 1;i <= 9;i++)
• Bước 1: gán cho i giá trị bằng 1.
• Bước 2: đưa ra màn hình giá trị
printf(“%5d”,i);
của i. // ta dành 5 vị trí để in mỗi số
• Bước 3: tăng giá trị của i thêm 1 đơn getch();
vị. }
• Bước 4: kiểm tra nếu giá trị của i ≤ 9
thì quay về bước 2, nếu giá trị của
i > 9 thì chuyển sang bước 5. }
• Bước 5: kết thúc.
LƯU Ý

➢Theo chuẩn C89: biến phải được khai báo trước khi sử dụng
trong vòng for.

int i;
for (i = 0; i < n; i++)

➢for (int i = 0; i < n; i++)


• Là cú pháp được giới thiệu trong chuẩn C99.
• Để sử dụng cách viết này cần bật chế độ C99 bằng cách truyền
tham số -std=c99 cho GCC.
VD :
gcc -std=c99 -o outputfile sourcefile.c
Ví dụ 2
• Đọc một tập số nguyên và #include <stdio.h>
in tổng của chúng int main() {
• tổng = 0
int tong = 0, dem, sopt, sosau;
• đếm = 0
printf(“So phan tu trong day so:");
• input sốpt
scanf("%d", &sopt);
• while (đếm < sốpt) do
for (dem = 0; dem < sopt; dem++) {
{ scanf("%d", &sosau);
• input sốsau tong += sosau;
• cộng sốsau vào }
tổng
• thêm 1 vào đếm printf(“Tong la %d\n",tong);
return 0;
}
}

• output tổng
Tìm các số nguyên lẻ nhỏ hơn 100 (1)

11
Tìm các số nguyên lẻ nhỏ hơn 100 (2)

12
Nhập n và tính n!

13
Nhập n và tính tổng S = 1+1/2+..+1/n

14
Tìm số 3 chữ số thỏa mãn abc=a3+b3+c3

15
Tìm số 3 chữ số thỏa mãn abc=a3+b3+c3

16
Chú ý
Không nhất thiết phải có đầy đủ các
thành phần trong vòng lặp for

int getchar(): đọc ký tự từ vùng đệm bàn


phím. Nếu vùng đệm rỗng, đợi người dùng
gõ dãy ký tự (cho tới khi ấn phím Enter), sẽ
trả về ký tự đầu
putchar(int c): đưa ký tự ra màn hình

17
Chú ý
1. Biểu thức khởi tạo
char c; int i=0; Hello world
for( ; (c=getchar())! = '\n’ ; i++) Hello world
putchar(c); So ky tu: 11
printf(“\nSo ky tu: %d”,i);
2. Biểu thức điều khiển
for(i=0 ; ; c=getchar(), i++) Hello world
if(c==‘\n’) break;
printf(“\nSo ky tu: %d”,i); So ky tu: 12
3. Thân vòng lặp
for(i=0 ; getchar() != ‘\n’, i++); Hello world
printf(“\nSo ky tu: %d”,i); So ky tu: 11

18
Lỗi thường gặp
for (dem=0; dem<sopt; )
{
scanf("%d", &sosau);
Biến đếm không được
cập nhật sau mỗi lần lặp
tong += sosau;
}

for (dem=0; dem<sopt; dem++);


{
scanf("%d", &sosau); Dấu ; không có ở đây
tong += sosau;
}

for (dem=0, dem<sopt, dem++)


{
scanf("%d", &sosau); Dấu phảy không phải là dấu
tong += sosau; phân cách các thành phần
}
Dấu phẩy
• Trong vòng for dấu phẩy (,) được dùng để phân cách nhiều lệnh
trong cùng một nhóm.

• Ví dụ:
for (i=0, j=100; i<=j; i++,j--)
printf(”(%d, %d\n)”, i, j);

Output: (0, 100)


(1, 99)

(49, 51)
(50, 50)
Bài tập
1. Viết chương trình in các số có 2 chữ số có tổng
là 10, ví dụ 19, 28,…
Bài tập 2
Viết chương trình in 100 số đầu tiên của dãy số
sau: 1 2 3 5 8 13 21…
Lời giải (bài 2)
first = 1; second = 2;
for (count=1; count<=100; count++)
{
printf(“%5d”, first);
tmp = first + second;
first = second;
second = tmp;
}
C PROGRAMMING
LANGUAGE

TS. Đỗ Quốc Huy


Bộ môn Khoa Học Máy Tính
huydq@soict.hust.edu.vn

1
ĐIỀU KHIỂN LẶP (2)

while và do … while
Lệnh while

while ( <điều kiện> ) <câu lệnh>

• Thực hiện lặp 1 lệnh hay 1 khối lệnh theo 1


điều kiện
• Kiểm tra điều kiện ngay khi bắt đầu vào vòng lặp

• Kết thúc vòng lặp khi điều kiện trở nên sai (có
giá trị bằng 0)
Lưu đồ

Tính toán giá trị của


Biểu thức điều kiên

Lệnh

Biểu thức != 0
Đúng

Sai

4
Ví dụ
• Đọc một tập số nguyên và in
tổng của chúng #include<stdio.h>
int main()
• tổng = 0 {
• đếm = 0 int tong=0, dem=0, sopt, sosau;
printf(“So phan tu trong day so:");
• input sốpt
scanf("%d", &sopt);
Không có
• while (đếm < sốpt) do while (dem < sopt) do ở đây
{
{
scanf("%d", &sosau);
• input sốsau
tong += sosau; dem++;
• cộng sốsau vào tổng }
thêm 1 vào đếm printf(“Tong la %d\n",tong);
} return 0;
• output tổng }
Ví dụ (tiếp)
#include <stdio.h>
int main()
{ sopt sosau tong dem
int tong=0, dem=0, sopt, sosau;

printf(“So phan tu trong day so:");


? ? 0 0
scanf("%d", &sopt);
3
while (dem < sopt)
{ 4 4 1
scanf("%d", &sosau);
tong += sosau;
-1 3 2
dem++; 2 5 3
}

printf(“Tong la %d\n",tong);
return 0;
}
Lỗi thường gặp
while (dem < sopt)
scanf("%d", &sosau); Chỉ có lệnh scanf là
tong += sosau; được lặp nhiều lần
dem++;

while (dem < sopt);


{ Lệnh lặp chỉ là lệnh
scanf("%d", &sosau); rỗng (lệnh chỉ có
tong += sosau; dấu ;)
dem++;
}
while (dem < sopt)
{ Lệnh in bị lặp lại
scanf("%d", &sosau); nhiều lần
tong += sosau;
dem++;
printf(“Tong la %d\n",tong);
}
Kết thúc nhập với EOF

• Trong ví dụ tính tổng dãy số trước, để xác định sự


kết thúc của dãy số ta phải xác đếm số phần tử
trong dãy trước khi bắt đầu nhập dãy.

• xác định kết thúc của 1 dãy số được nhập thông qua
dấu hiệu EOF trả về bởi hàm scanf.

• Trong quá trình nhập dữ liệu, nếu nhấn phím tắt


Ctrl+D trong Unix (hoặc Ctrl+Z trong DOS) thì hàm
scanf sẽ trả về EOF.
Ví dụ
#include <stdio.h>
• Đọc một tập số nguyên
và in tổng của chúng int main()
{
(ver 2)
int aNum, sum = 0;

tổng = 0 while (scanf("%d", &aNum)!=EOF)


{
while (chưa kết thúc dãy) do sum += aNum;
{ }
input sốsau
cộng sốsau vào tổng printf("Sum is %d\n", sum);
} return 0;
}
output tổng
Cho biết kết quả thực hiện chương trình

Kết quả

10
So sánh while và for
#include <stdio.h> #include <stdio.h>
int main() int main()
{ {
int tong=0, dem; int tong=0, dem;
int sopt, sosau; int sopt, sosau;

printf(“So phan tu: "); printf(“So phan tu: ");


scanf("%d", &sopt); scanf("%d", &sopt);
dem = 0;
while (dem < sopt) for (dem=0; dem<sopt; dem++)
{ {
scanf("%d", &sosau); scanf("%d", &sosau);
tong += sosau; tong += sosau;
dem++; }
}
printf(“Tong la %d\n",tong); printf(“Tong la %d\n",tong);
return 0; return 0;
} }
Lệnh do…while

do {
<tập lệnh>
} while ( <điều kiện> );

• Là vòng lặp mà lần lặp đầu tiên điều kiện không được
kiểm tra
• Tập lệnh trong vòng lặp được thực hiện ít nhất 1 lần,
ngay cả khi điều kiện lặp không bao giờ đúng
Lưu đồ

Lệnh

Tính toán giá trị của


Biểu thức điều kiên

Đúng
Biểu thức != 0

Sai

13
Ví dụ
#include <stdio.h>
• Tính tổng của một dãy số
nguyên với số cuối cùng là int main()
nhập là 0
{
int tong=0, so;
• tổng =0
printf(“Nhap cac so trong day\n");
do
do
{ {
• input số scanf("%d", &so);
• cộng số vào tổng
tong += so;
} while (số khác 0)
}while (so != 0);

• output tổng printf(“Tong la %d\n",tong);


return 0;
}
Kiểm tra giá trị bằng do…while

int n;

do {
printf(“Input a positive number: ”);
n = -1;
fflush(stdin);
if (scanf(“%d”, &n) != 1)
printf(“Input data is not a number\n”);
}while (n<0);

printf(“The number is %d\n”, n);


Kết quả chạy

• Input a positive number: abc


• Input data is not a number
• Input a positive number: -2
• Input a positive number: 5
• The number is 5
Ví dụ
• Nhập vào điểm của một sinh viên, nếu
điểm đó không  [0, 10] thì thông báo cho
người dùng nhập lại.
• Thực hiện:
– Nếu dùng lệnh if
→ Chỉ kiểm tra được 1 lần
– Sử dụng for
Chưa biết trước số lần lặp.
→ Sử dụng vòng lặp không cần xác định trước
số lần lặp: while / do while

17
Dùng vòng lặp while

18
Dùng vòng lặp while → Kết quả

20
Dùng vòng lặp do…while

21
Vòng lặp vô tận

• Trong vòng for các nhánh khởi tạo, điều kiện và cập nhật là
tùy chọn.
• Có thể tạo vòng lặp vô tận bằng lệnh while hoặc for
– Vòng lặp là vô tận khi điều kiện lặp là luôn luôn đúng

while (1)
{
/* lặp vô tận */
}

for (;;)
{
/* lặp vô tận */
}
Lệnh break

• Lệnh trực tiếp cho phép thoát ngay khỏi vòng lặp
mà không cần kiểm tra điều kiện lặp
• Thường được áp dụng trong các vòng lặp vô tận

for (;;)
{

if (<điều kiện thoát OK>) break;
}
Ví dụ

int n;
while (1) {
printf(“Input a positive number: ”);
fflush(stdin);

if (scanf(“%d”, &n) != 1)
printf(“Bad number. Try again\n”);
else if (n<0)
printf(“Must be positive. Try again\n”);
else
break;
}
printf(“The number is %d\n”, n);
Kết quả chạy

Input a positive number: abc


Bad number. Try again
Input a positive number: -2
Must be positive. Try again
Input a positive number: 5
The number is 5
Lệnh continue
• Thường được áp dụng để bỏ qua lần lặp hiện tại
khi một điều kiện nào đó xảy ra
• Bỏ qua việc thực hiện các câu lệnh nằm sau lệnh
continue trong thân vòng lặp.
• Chuyển sang thực hiện một vòng lặp mới

for (…)
{

if (<cần bỏ qua lần lặp này>) continue;

}
Ví dụ

In ra 100 số nguyên đầu tiên


ngoại trừ các số chia hết cho 5

for(i=1;i<=100;i++)
if (i % 5 != 0)
printf(‘‘%d’’,i);

28
Ví dụ

int n;

do {
printf(“Input a positive number: ”);
fflush(stdin);
if (scanf(“%d”, &n) != 1) {
printf(“Bad number. Try again\n”);
continue;
}
printf(“The number is %d\n”, n);
}while (n<0);
Kết quả chạy

Input a positive number: abc


Bad number. Try again
Input a positive number: -2
The number is -2
Input a positive number: 5
The number is 5
Xóa BỘ ĐỆM BÀN PHÍM
Ký tự enter (\n) của lần nhập trước có thể bị lưu
vào giá trị tiếp theo của lệnh scanf

• môi trường Windows:


Dùng lệnh fseek.
VD:
fseek(stdin,0,SEEK_END);
scanf(“%d”,&n);

• môi trường Linux:


Dùng lệnh
__fpurge(stdin);
scanf(“%d”,&n);
từ thư viện
#include <stdio_ext.h>
Bài tập

• Viết chương trình in ra tích của các thừa số


nguyên tô của một số.

• Ví dụ: 6 = 2x3,
• 24 = 2x2x2x3,
• 23 = 23
Bài tập 2

• Viết chương trình in ra số nguyên tố đầu tiên


lớn hơn một giá trị N.
Bài tập 3

Nhập một xâu và đếm số nguyên âm,


phụ âm, khoảng trắng

34
Bài tập

• Viết chương trình để tính số  theo công thức sau:

/4 = 1 – 1/3 + 1/5 – 1/7 + 1/9 – 1/11 + …


NGÔN NGỮ LẬP TRÌNH C

TS. Đỗ Quốc Huy


Bộ môn Khoa Học Máy Tính
huydq@soict.hust.edu.vn

1
Nội Dung Khóa Học
1st Tổng quan lập trình máy tính
2nd Ngôn ngữ lập trình C
3rd Kiểu dữ liệu
4th Vào ra dữ liệu
5th Biểu thức trong ngôn ngữ C
6th Lệnh rẽ nhánh
7th Lệnh lặp (1)
8th Lênh lặp (2)
9th Hàm
10th Mảng
11th Con trỏ
12th Chuỗi
13th Cấu trúc dữ liệu
14th Vào ra với file
15th Final Exam

2
Khái niệm chương trình con
• Chương trình con
– Là đoạn chương trình thực hiện 1 nhiệm vụ cụ thể và
được đóng gói theo cấu trúc xác định
– Được định nghĩa 1 lần và sử dụng lại nhiều lần.
• Vai trò
– Tổ chức 1 chương trình thành nhiều phần nhỏ để tăng
tính cấu trúc.
– Tiết kiệm thời gian bằng cách sử dụng chương trình con
đã có thay vì viết lại.
– Mở rộng tính năng cho chương trình bằng cách sử dụng
các thư viện, ví dụ printf(), scanf()…
– Gỡ lỗi, bảo trì chương trình dễ dàng hơn

3
Hàm trong ngôn ngữ C
• Các ngôn ngữ lập trình nói chung: 2 loại chương
trình con
– Hàm: thực hiện công việc và trả lại kết quả.
– Thủ tục: thực hiện công việc, không trả lại kết quả.
• Ngôn ngữ C:
– Chỉ có 1 loại chương trình con là hàm (function).
– Hàm trong C tương đương cả hàm và thủ tục trong các
ngôn ngữ khác.
– Sử dụng kiểu void (kiểu dữ liệu không định kiểu) khi hàm
không trả về dữ liệu.

4
Hàm
• Là một nhóm các khai báo và câu lệnh được đặt tên

• 1 hàm tương đương với 1 chương trình con

• Chương trình chúng ta viết là 1 hàm được đặt tên là main


và có thể gọi đến các chương trình con

• Các chương trình con này lại có thể sử dụng các hàm
khác, và cứ tiếp tục như vậy

• Tuy nhiên về mặt cú pháp, tất cả các hàm trong C đều có


cùng 1 cấp. Do vậy 1 hàm có thể gọi tới 1 hàm khác bất kì
có trong chương trình
Ví dụ
#include <stdio.h>

/* Ham in loi chao mung */


void sayHello ( void )
Định nghĩa hàm {
printf(“Hello World!\n”);
}

/*
* Goi ham in loi chao mung
*/
int main(void)
{
Gọi hàm sayHello();
return 0;
}
Tại sao sử dụng hàm?
• Chúng cho phép chia nhỏ vấn đề thành các công
việc con
– Giúp giải quyết dễ dàng hơn những vấn đề phức tạp
• Sử dụng hàm chương trình được viết sẽ sáng sủa hơn
nhờ khả năng “trừu tượng hoá”
– Chúng ta chỉ cần biết một hàm làm gì mà không quan tâm
nó làm thế nào
• Chúng cho phép tổng quát hoá một số nhóm lệnh lặp
nhiều lần
– Tránh viết đi viết lại nhiều lần một số nhóm lệnh
Xây dựng hàm
• Với mỗi hàm xây dựng cần phải đặc tả:
– Tên hàm
– Tham số truyền vào
– Loại giá trị mà hàm trả về nếu có
– Khối lệnh được thực hiện khi hàm được gọi đến

• Khối lệnh thực hiện còn được gọi là thân hàm


Xây dựng hàm (2)
• Cú pháp khai báo 1 hàm
[<kiểu giá trị trả về>] <tên hàm>([<danh sách tham số>,…])
• Thân hàm
{
<Các khai báo>

<Các câu lệnh>
}
• Trong thân hàm có ít nhất một lệnh return.
Ví dụ hàm giai thừa
#include <stdio.h>
Tên hàm
int giaithua (int a) {
int i, gt=1;
Thân hàm for(i=1; i<=a; i++)
gt = gt * i;
return gt;
}

int main( void ) {


int num;

printf(“Nhap so nguyen:");
scanf("%d",&num);

printf("%d!=%d\n",
num, giaithua(num));
Return 0;
}
Ví dụ hàm tính bình phương
#include<stdio.h>
int bp(int x){
Khai báo và
int y;
định nghĩa
y = x * x;
hàm
return y;
}
int main(){
int i;
for (i=1; i< 20; i+=2)
Gọi hàm ra printf("%4d\n", bp(i));
thực hiên printf("\n");
return 0;
}

11
Tham số hàm
• Là các biến địa phương của hàm mà giá trị được xác
định cho mỗi lần gọi hàm
– Vì vậy tham số có giá trị khác nhau cho mỗi lần gọi
– Tham số chỉ có thể truy nhập bên trong hàm mà thôi
– Khi gọi hàm giá trị cho tất cả các tham số phải được xác định

• Một hàm có thể có một, nhiều hoặc không có tham số nào cả


• Nếu có nhiều tham số, phải được phân cách với nhau bằng dấu
phẩy.
• không có tham số vẫn phải có cặp dấu ngoặc đơn sau tên hàm

• Một hàm không có tham số có thể được khai báo với từ khoá void
cho danh sách tham số
Tham số hàm
• Chú ý phân biệt

– Biến địa phương bên trong hàm dùng chứa tham số và biến mà giá trị
của nó được dùng để gọi hàm

– Vì thế nếu hàm thay đổi giá trị cho tham số, nó sẽ không làm thay
đổi giá trị của biến được sử dụng để gọi hàm
Ví dụ tham số
#include <stdio.h>
Khai báo tham số
int addOne ( int i ) là biến địa phương
{
i = i + 1; Thay đổi giá trị biến
return i; địa phương
}

int main(void)
{ Truyền giá trị của biến
int i = 3; i trong hàm main cho
lời gọi hàm
printf(“%d\n”, addOne(i) );
printf(“%d\n”, i);
Output:
return 0; 4
} 3
Ví dụ (tiếp)
void badSwap ( int a, int b )
{ int temp;
temp = a;
a = b;
b = temp;
printf("Called environment: %d %d\n",a,b);
}

int main(void)
{ int a = 3, b = 5;
printf("Calling environment: %d %d\n",a,b);
badSwap ( a, b );
printf("Calling environment: %d %d\n",a,b);
return 0;
}
Trả về giá trị
• Lệnh return dùng để trả về giá trị cho hàm

• Lệnh này phải chứa giá trị mà có thể chuyển về kiểu


giá trị trả về được đặc tả cho hàm
• Một hàm có thể có nhiều lệnh return
• nhưng lệnh nào được gặp đầu tiên sẽ làm kết thúc
hàm tại điểm đó với giá trị trả về tương ứng

• Một hàm không trả về giá trị nào phải được khai báo
với kiểu trả về void
– Khi đó có thể không cần sử dụng lệnh return trong hàm
Kiểu dữ liệu trả về
• Có thể là kiểu dữ liệu bất kì (kiểu dữ liệu có sẵn hoặc kiểu dữ liệu
do người dùng tự định nghĩa)

• Nhưng không được là kiểu dữ liệu mảng.

• Nếu kiểu dữ liệu trả về là kiểu void thì hàm không trả về giá trị
nào cả.

• Nếu không khai báo kiểu dữ liệu trả về → chương trình dịch của
C sẽ ngầm hiểu rằng kiểu dữ liệu trả về của hàm là kiểu int.

17
Sử dụng hàm
Tên_hàm (DS_tham_số_thực _sự);
Ví dụ:
N = bp(1);N= bp(3);,…
Lưu ý:
• Gọi hàm thông qua tên hàm và các tham số được cung cấp thực sự
cho hàm (tham số thực sự).
• Nếu hàm nhận nhiều tham số thì các tham số ngăn cách nhau bởi dấu
phẩy
• Các tham số hình thức của hàm sẽ nhận các giá trị từ tham số truyền
vào
• Sau khi thực hiện xong, trở về điểm mà hàm được gọi

18
Ví dụ: Cho biết kết quả thực hiện chương trình
#include <stdio.h> 3
int fun(int a){ fun(3)
a++;
4
return a;
} fun(4)
5
int main(){
fun(5)
printf("%d\n", fun(fun(fun(3))));
return 0;
}
6

19
Ví dụ: Cho biết kết quả thực hiện chương trình
fun(5)
#include<stdio.h>
5 * fun(4)
int fun(int n){
if(n==0) return 1; 4 * fun(3)
else return n*fun(n-1);
} 3 * fun(2)

int main(){ 2 * fun(1)


printf("%d\n", fun(5));
return 0;
120 1 * fun(0)
}
1
20
Khai báo và định nghĩa hàm
• Một định nghĩa hàm đặc tả đầy đủ tất cả các thành phần
của hàm bao gồm cả thân hàm

• Một khai báo hàm chỉ cần đặc tả:


– Tên hàm
– Kiểu của từng tham số
– Kiểu của giá trị trả về

• Tạo khai báo hàm bằng các nguyên mẫu (prototype).


Ví dụ:
int addOne (int);
void sayHello(void);
Vai trò của nguyên mẫu
• Một hàm có thể định nghĩa sau khi sử dụng nhưng
phải được khai báo trước khi dùng
• Nó cho phép bạn gọi 1 hàm mà không cần biết đến
định nghĩa
– Ví dụ khi sử dụng printf(), chúng ta không biết hàm làm việc
như thế nào nhưng chương trình dịch biết đây là 1 hàm với
kiểu tham số của nó
– Tệp stdio.h chứa khai báo (nguyên mẫu) của printf()

• Một định nghĩa hàm bao hàm luôn 1 khai báo


Nguyên mẫu hàm giai thừa
#include <stdio.h>

Nguyên mẫu int giaithua (int);

int main( void ) {


int num;

printf(“Nhap so nguyen:");
scanf("%d",&num);

printf("%d!=%d\n",
num, giaithua(num));
}

int giaithua (int a) {


Định nghĩa hàm int i, gt=1;
for(i=1; i<=a; i++)
gt = gt * i;
return gt;
}
Phạm vi
• Phạm vi: #include<stdio.h>
int i;
– Khối lệnh, chương trình con,
chương trình chính int binhphuong(int x){
int y;
• Biến chỉ có tác dụng trong phạm y = x * x;
vi được khai báo return y;
• Trong cùng 1 phạm vi các biến }
phải có tên khác nhau. int main(){
int y;
for (i = 0; i <= 10; i++){
Tình huống
y = binhphuong(i);
• Trong 2 phạm vi khác nhau có 2 biến
printf("%d ", y);
cùng tên. Trong đó 1 phạm vi này nằm
}
trong phạm vi kia?
return 0;
}

24
Phân loại biến
• Biến được khai báo trong hàm
• biến cục bộ.
• chỉ được truy cập trong hàm của nó mà thôi

• Biến tổng thể


• được khai báo bên ngoài phạm vi hàm
• có thể được truy cập bởi nhiều hàm khác nhau.
• Ví dụ:
int global;
void f(void) { global = 0; }
void g(void) { global = 1; }
Phân loại biến
Ghi chú

Hàm main() cũng là 1 chương trình con nhưng là nơi chương trình
được bắt đầu

Biến khai báo trong hàm main() cũng là biến cục bộ, chỉ có phạm
vi trong hàm main().
Biến trùng tên
• Khi có sự trùng tên giữa biến tổng thể và biến cục bộ
trong 1 hàm, thì biến cục bộ được ưu tiên dùng trong
hàm đó.
• Ví dụ:
int i;
void f() {
int i;
i++; // chỉ làm thay đổi giá trị biến i cục bộ
}
void g() {
i++; // làm thay đổi giá trị của biến i tổng thể
}
Biến static
• Biến cục bộ ra khỏi phạm vi thì bộ nhớ
dành cho biến được giải phóng
• Yêu cầu lưu trữ giá trị của biến cục bộ một
cách lâu dài => sử dụng từ khóa static
Cú pháp:
static <kiểu_dữ_liệu> tên_biến;

28
Ví dụ→Kết quả
#include <stdio.h>

void fct() {
static int count = 1;
printf("\n Day la lan goi ham fct lan thu %2d", count++);
}

int main() {
int i;
for (i = 0; i < 10; i++) fct();
return 0;
}

29
Ví dụ

30
Các hàm thư viện
• Ngôn ngữ C cung cấp một số hàm thư viện như vào
ra, toán học, quản lí bộ nhớ, xử lí xâu chuỗi, v.v.

• Để có thể sử dụng, ta cần khai báo nguyên mẫu


của chúng trong chương trình

• Các khai báo nguyên mẫu như thế này đã được viết
trong các tệp tiêu đề (.h), ta chỉ cần #include chúng
vào chương trình
Thư viện toán học math.h
• Bao gồm một tập các hàm toán học với khai báo nguyên mẫu
như sau:

double sin(double x);


double cos(double x);
double tan(double x);

double log(double x);
double sqrt(double x);
double pow(double x, double y);
int ceil(double x);
int floor(double x);

Bài tập
• Cho 2 hàm nhập giá trị một số nguyên và tìm
giá trị lớn nhất của 2 số có khai báo nguyên
mẫu như sau
int nhapso();
int max(int a, int b);
• Viết định nghĩa hàm và chương trình chính sử
dụng các hàm đã cho để tìm giá trị lớn nhất của
3 số bất kì được nhập vào
Bài tập1: Tính TBC f(a),f(b), f(c)
𝟓 𝟓
nếu f 𝐱 = 𝒙 + 𝒙

34
VD Đọc tọa độ 3 điểm A,B,C và
đưa ra d/tích ABC…

35
NGÔN NGỮ LẬP TRÌNH C

TS. Đỗ Quốc Huy


Bộ môn Khoa Học Máy Tính
huydq@soict.hust.edu.vn

1
Nội Dung Khóa Học
1st Tổng quan lập trình máy tính
2nd Ngôn ngữ lập trình C
3rd Kiểu dữ liệu
4th Vào ra dữ liệu
5th Biểu thức trong ngôn ngữ C
6th Lệnh rẽ nhánh
7th Lệnh lặp (1)
8th Lênh lặp (2)
9th Hàm
10th Mảng
11th Con trỏ
12th Chuỗi
13th Cấu trúc dữ liệu
14th Vào ra với file
15th Final Exam

2
Nội Dung Bài Học
➢ Khái niệm mảng
▪ Giới thiệu
▪ Khai báo
▪ Cấp phát bộ nhớ

➢ Các thao tác với mảng

➢ Mảng nhiều chiều


Giới thiệu
Bài toán:
• Nhập điểm thi (số nguyên) môn C - Intro cho lớp gồm 150 sinh viên rồi
đưa ra số lượng sinh viên phải học lại
Phương pháp: Điểm của mỗi sinh viên là 1 biến
• Tên biến là tên sinh viên
Ví dụ: int An, Anh, Binh1, Binh2, Cuong,….. Van, Viet;
• Tên biến dạng “dx” với x là chỉ số thứ tự của SV trong lớp
Ví dụ: int d1, d2, d3,……,d50;
Nhận xét 1: Không hợp lý
• Có quá nhiều biến (Điểm thi cho toàn trường.. !?)
• Khó khăn cho các thao tác duyệt toàn bộ danh sách
– Số SV học lại: if(d1 <5) d++; if(d2 <5) d++; ……if(d50 <5) d++;
Nhận xét 2: Các biến có chung ý nghĩa, tính chất

4
Giới thiệu
• Trong thực tế, thường gặp các đối tượng có tính chất chung
– Tháng trong năm
– Điểm trung bình của sinh viên trong lớp
• Các đối tượng được nhóm lại dưới 1 tên
• Đối tượng được đặc trưng bởi tên nhóm và thứ tự trong nhóm
– Tháng thứ 3 trong năm: Tháng 3
– Sinh viên thứ 17 trong lớp:…
• Số thứ tự của đối tương trong nhóm là chỉ số phần tử

5
Khái niệm mảng
• mảng là 1 kiểu dữ liệu gồm
– Một số hữu hạn thành phần.
– Các thành phần có cùng 1 kiểu: kiểu cơ sở hay là kiểu
thành phần.
• Mỗi phần tử của mảng được tham khảo thông qua
– Tên mảng và
– Chỉ số của phần tử trong mảng
Ví dụ:
<d7>: Điểm thi tin của sinh viên thứ tự 7 trong lớp

6
Khai báo mảng
Kiểu_dữ_liệu Tên_Mảng[Kích thước];

• Kiểu_dữ_liệu: kiểu của các phần tử trong mảng


(nguyên, thực, ký tự, chuỗi, mảng,…)
• Tên_mảng: tên của mảng
• Kích_thước_mảng: số phần tử trong mảng
Ví dụ
// khai báo mảng 50 phần tử có kiểu dữ liệu int
int DiemTin[50];
float A[10]; // Mảng 10 phần tử kiểu số thực

7
Cấp phát bộ nhớ cho mảng
• Các phần tử trong mảng được cấp phát các
ô nhớ kế tiếp nhau trong bộ nhớ
• Kích thước của mảng bằng kích thước 1
phần tử nhân với số phần tử
Ví dụ:
int A[10];//Mảng A gồm 10 phần tử nguyên
A[0] A[1] A[2] A[3] A[4] A[5] A[6] A[7] A[8] A[9]

Kích thước của mảng A: 10 x 4 = 40 bytes

8
Truy nhập phần tử của mảng
• Biến mảng lưu trữ địa chỉ ô nhớ đầu tiên
trong vùng nhớ được cấp phát
• Ngôn ngữ C đánh chỉ số các phần tử trong
mảng bắt đầu từ 0
• Các phần tử của mảng được truy nhập
thông qua
– Tên mảng và
– Chỉ số của phần tử của phần tử trong mảng

Tên_Mang[Chỉ_số_phần_tử];
9
Ví dụ
int A[10];//Mảng A gồm 10 phần tử nguyên
7
A[0] 5
A[1] A[2] A[3] 7
A[4] A[5] A[6] A[7] A[8] A[9]
A

A[0] A[4] A[9]

A[0] = 7;
A[1] = 5;
A[4] = 7;
int N = A[1] + A[4]; → N = 12

10
Ví dụ
int A[10];
for(int i = 0; i < 10; i++) A[i]= 2* i;

0? 2? 4? 6
? 8? 10
? 12
? 14
? 16
? 18
?

0
i : 10
7
1
4
2
6
5
3
8
9
Chú ý: C không kiểm tra vượt quá giới hạn
của mảng khi truy nhâp

11
Chỉ số mảng
• 1 mảng luôn có kích thước cố định
• Không có 1 phương thức cài đặt sẵn nào để
kiểm tra chỉ số trong phạm vi của mảng
• Người lập trình phải tự kiểm soát chỉ số trong phạm vi
của mảng

Kích thước mảng


Ví dụ: int i, n=5; phải là hằng số

int A[n];
Lỗi chỉ số
int B[5]; vượt phạm vi
for (i=1; i<=n; i++) của mảng
B[i] = 0;
Ví dụ
Chú ý: C không kiểm tra vượt quá giới hạn
của mảng khi truy nhập

int A[3], B[4], C[3];


A[0] A[1] A[2] B[0] B[1] B[2] B[3] C[0] C[1] C[2]

A[5]  B[2]  C[-2] nếu cấp phát liên tiếp

13
Nội Dung Bài Học

➢ Khái niệm mảng

➢ Các thao tác với mảng

➢ Mảng nhiều chiều


Các thao tác thường gặp
• Khởi tạo mảng
• Nhập/Xuất dữ liệu cho mảng
– Mảng 1 chiều, ma trận
• Bài toán đếm
– Đếm số phần tử
– Tính toán trên các phần tử..
• Tìm kiếm phần tử
– Lớn nhất/nhỏ nhất/bất kỳ
• Sắp xếp phần tử trong mảng
– Theo thứ tự, theo nguyên tắc
• Chèn thêm phần tử, xóa phần tử
15
Khởi tạo mảng
• Khi khai báo mảng các phần tử của mảng có thể được khởi
tạo giá trị như biến thông thường

• Số phần tử được khởi tạo không được nhiều hơn số phần tử


của mảng
– Nhưng nó có thể ít hơn
– Khi đó số phần tử còn lại được khởi tạo giá trị 0

• Kích thước của mảng có thể được suy diễn từ số phần tử


khởi tạo giá trị

int A[8] = {2, 4, 6, 8, 10, 12, 14, 16};


hoặc
int A[] = {2, 4, 6, 8, 10, 12, 14, 16};
Ví dụ
int month;
int table[12] = { 30, 40, 45, 95, 130, 220, 210,
185, 135, 80, 40, 45 };

printf("Enter a month: ");


scanf("%d", &month);

if (1 <= month && month <= 12)


printf("Average rainfall for month %d is %d
mm.\n", month, table[month-1]);
else
printf("Invalid month\n");
Nhập dữ liệu
Dùng hàm scanf()
Ví dụ: int Table[10];
• Nhập dữ liệu cho một phần tử
scanf(“%d”,&Table[2]);phần tử thứ 3 của mảng
• Nhập dữ liệu cho cả mảng
– Dùng vòng lặp for
for(i = 0; i < 10; i++)
scanf(“%d”,&Table[i]);
– Nên in ra chỉ số phần tử khi nhập
printf(“Table[%d] : ”,i); scanf(“%d”,&Table[i])

18
Nhập dữ liệu → Ví dụ 1
Nhập vào lượng mưa (mm) trong năm
#include <stdio.h>
#define MONTHS 12
int main(){
int rainfall[MONTHS], i;
for ( i=0; i < MONTHS; i++ ){
printf("Nhap luong mưa tháng %d: ", i+1);
scanf("%d", &rainfall[i] );
}
return 0;
}

19
Nhập dữ liệu → Lưu ý
• Nếu số phần tử của mảng chỉ được biết tại thời điểm
thực hiện chương trình (nhưng biết số phần tử tối
đa)
– Khai báo mảng với kích thước tối đa
– Sử dụng biến nguyên lưu số phần tử thực sự của mảng.
Ví dụ:
• Nhập vào mảng không quá 100 số thực
– Khai báo mảng thực Table có tối đa 100 phần tử.
– Nhập số phần tử thực sự của mảng
– Nhập giá trị cho từng phần phần tử (dùng for)

20
Nhập dữ liệu→Ví dụ 2
#include<stdio.h>
int main(){
float A[100];
int n, i;
do{
printf("\n Cho biet so phan tu cua mang: ");
scanf("%d",&n);
}while (n>100 || n<=0);
for(i = 0; i < n; i++){
printf("A[%d] = ", i); scanf("%f",&A[i]);
}
}

21
Chương trình dãy số
• Nhập 1 dãy số nguyên (nhiều nhất 10 phần tử) và in dãy số đó
theo thứ tự đảo ngược
#include <stdio.h>
int main(void) {
int i, n, A[10];
printf(“Nhap so phan tu trong day (n<=10):");
scanf("%d",&n);
printf(“Nhap cac phan tu trong day:\n");
for(i=0; i<n; i++) {
printf(“Phan tu thu %d:”, i+1);
scanf("%d",&A[i]);
}
printf(“Day so sau khi dao lai:\n");
for(i=n-1; i>=0; i--)
printf("%5d",A[i]);
return 0;
}
Truyền mảng cho hàm

• 1 mảng có thể được truyền cho hàm và


giá trị của nó có thể bị thay đổi

• Để truyền 1 mảng cho hàm ta dùng các


tham số như sau:
– f (int array[], int n)
Chương trình dãy số (hàm)
#include <stdio.h>

void nhapMang(int array[], int num)


{
int i;
for(i = 0; i < num; i++) {
printf(“Phan tu thu %d:”, i+1); Số phần tử
scanf("%d", &array[i]); chính xác của
} mảng phải được
} truyền qua 1
tham số khác
void inNguoc(int array[], int num)
{
int i;
for(i = num-1; i >= 0; i--)
printf("%5d", array[i]);
}
Chương trình dãy số (tiếp)
int main(void)
{
int n, A[10];
printf(“Nhap so phan tu trong day (n<=10):");
scanf("%d",&n);

printf(“Nhap cac phan tu trong day:\n");


nhapMang(A, n);

printf(“Day so sau khi dao lai:\n");


inNguoc(A, n);

return 0;
}
Đếm số phần tử thỏa mãn điều kiện
• Duyệt từng phần tử của dãy (dùng for )
• Nếu phần tử xét thỏa mãn điều kiện
– Ghi nhận
• Chuyển sang xem xét phần tử tiếp theo
Ví dụ: Đếm số tháng có lượng mưa lớn hơn 50mm
int dem = 0;
for(i = 0; i < MONTHS; i++)
if(rainfall[i] > 50)
dem++;
printf("\nThang mua nhieu hon 50mm: %d", dem);

26
Ví dụ: Nhập mảng, đưa ra TBC các số
chia hết cho 7
#include<stdio.h>
int main(){
int A[100];
int n, i, d = 0, S=0;
printf("\n So phan tu cua mang (<100) : "); scanf("%d",&n);
for(i = 0; i < n; i++){
printf("A[%d] = ", i); scanf("%d",&A[i]);
}
for(i = 0; i < n; i++)
if(A[i] %7==0){
d++;
S+= A[i];
}
if(d > 0) printf("TBC so chia het cho 7: %7.2f",(float)S/d);
else printf("Trong day khong co so chia het cho 7");
}

27
Tìm kiếm phần tử
Tìm phần tử lớn nhất (nhỏ nhất)
– Giả sử phần tử đó là phần tử đầu tiên
– Lần lượt so sánh với các phần tử còn lại
• Nếu phần tử mới của dãy lớn hơn coi đây là phần tử lớn nhất
và tiếp tục so sánh với phần tử kế
• Nếu không đúng, so sánh tiếp với phần tử kế
Ví dụ: Tìm tháng có lượng mưa nhiều nhất trong năm
max = rainfall[0];
for(i = 1; i < MONTHS; i++)
if(rainfall[i] > max)
max = rainfall[i];
printf("\n Luong mua nhieu nhat la: %d", max);

28
Tìm kiếm phần tử
• Tìm kiếm các phần tử thỏa mãn điều kiện (giống
bài toán đếm)
– Dùng for duyệt toàn bộ
– Nếu cần thiết, dùng thêm mảng ghi lại chỉ số

Ví dụ: Đưa ra danh sách các tháng có lượng mưa


nhiều hơn 50mm
printf(“Thang co luong mua lon hon 500mm”)
for(i = 0; i < MONTHS; i++)
if(rainfall[i] > 50)
printf("\nThang %d", i+1);

29
Tìm kiếm phần tử (tiếp)
• Tìm phần tử đầu tiên của danh sách
– Dùng vòng lặp for kết hợp với break;
– Dùng vòng lặp while

Ví dụ
Đưa ra phần tử đầu của mảng có giá trị
bằng k;

30
Tìm kiếm phần tử →Ví dụ
int Table[100]
int N, i, k, f;//N: số phần tử, k phần tử cần tìm
• Dùng for
for(i = 0; i < N; i++)
if(Table[i] == k) break;
if(i< N) printf("Tim thay tai vi tri %d",i);
• Dùng while
i=0; f =0; //f: found. f = 1  k is found
while(i < N && f==0){
if(Table[i] == k) f = 1;
else i++;}
if (f==1) printf("Tim thay tai vi tri %d",i);

31
Bài tập

• Viết chương trình tính tổng của dãy số theo


phương pháp nhập dãy số vào mảng sau đó
tính tổng của các số lưu trên mảng.

• Tính min, max của các số trong mảng.


Sắp xếp mảng

• Sắp xếp các phần tử trong mảng theo trật tự


tăng/giảm dần

• Duyệt các phần tử từ trái sang phải và đổi chỗ


nó với phần tử nhỏ/lớn hơn nằm bên phải.
– A[i] là phần tử được duyệt
– A[j] là phần tử bên phải A[i]
– A[i] được đổi chỗ cho A[j] nếu A[i] < A[j]
– Để đổi chỗ giá trị 2 phần tử, dùng thêm 1 biến phụ
Chương trình
#include <stdio.h>
int main(void) {
int A[10], n, i, j, tmp;
/* nhập dãy số vào mảng */ Sắp xếp lựa chọn

for(i = 0; i < n-1; i++)


for(j = i+1; j < n; j++)
if (a[i] < a [j]) {
tmp = A[i];
A[i] = A[j];
A[j] = tmp;
}
printf(“Dãy được sắp xếp:\n”);
for(i=0; i<n-1; i++)
printf(”%5d”, A[i]);
return 0;
}
Selection sort
Lượt 1

i=0
j=1
5 1 4 2 8

i=0 Không đổi


j=2 1 5 4 2 8 chỗ

i=0 1 5 4 2 8 Không đổi


j=3 chỗ

i=0 Không đổi


j=4 1 5 4 2 8 chỗ

i=0
j=5
Selection sort
Lượt 2

i=1
j=2
1 5 4 2 8

i=1
j=3 1 4 5 2 8

i=1 1 2 5 4 8 Không đổi


j=4 chỗ

i=1
j=5
Selection sort
Lượt 3

i=2
j=3
1 2 5 4 8

i=2 Không đổi


j=4 1 2 4 5 8 chỗ

i=2
j=5
Selection sort
Lượt 4

i=3 Không đổi


j=4
1 2 4 5 8 chỗ

i=3
j=5

Lượt 5

i=4
j=5 1 2 4 5 8
Ví dụ→Kết quả

39
Nội Dung Bài Học

➢ Khái niệm mảng

➢ Các thao tác với mảng

➢ Mảng nhiều chiều


Mảng nhiều chiều
• Mỗi phần tử của mảng có thể là 1 mảng
• Cú pháp
Kiểu Tên[Chiều_1] [Chiều_2]… [Chiều_N];
• Kiểu: Kiểu của mỗi phần tử trong mảng
• Chiều_1, Chiều_2,…Chiều_N: Các hằng số nguyên, cho
biết kích thước (số phần tử) của mỗi chiều
• Mảng gồm: Chiều_1 x Chiều_2 x...x Chiều_N phần tử
được lưu trữ trong vùng nhớ liên tục. Các phần tử thuộc
kiểu Kiểu

41
Ví dụ mảng hai chiều

• Mảng 2 chiều là mảng của mảng

Ví dụ: int A[5][3];


for (i=0; i<=5; i++)
for (j=0; j<=3; j++)
A[i][j] = 10;
Mảng nhiều chiều
int t[3][4] ;

t[0] t[0][0] t[0][1] t[0][2] t[0][3]

t[1] t[1][0] t[1][1] t[1][2] t[1][3]

t[2] t[2][0] t[2][1] t[2][2] t[2][3]

t[1][0] t[1][1] t[1][2] t[1][3]

43
Mảng nhiều chiều→Ví dụ
int b[3][4][5]; b[2][2][4]
b[2]
b[1]
b[0]

b[0][1][2]
• Mảng b gồm 3 phần tử b[0], b[1], b[2]
• Mỗi phần tử là mảng 2 chiều gồm 4 hàng (hàng 0, 1, 2, 3)
và 5 cột (0, 1, 2, 3, 4)
• Mỗi phần tử là 1 số nguyên có dấu 4 byte

44
Khởi tạo giá trị cho mảng
Các phần tử của mảng có thể được khởi tạo
giá trị ngay khi khai báo
Ví dụ
int a[4] = {1,4,6,2};
int b[2][3]={ {1,2,3}, {4,5,6} };
int t[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
};

45
Khởi tạo giá trị cho mảng → Chú ý
• Số lượng giá trị khởi tạo không được lớn hơn số
lượng phần tử trong mảng
– Nếu số lượng này nhỏ hơn, các phần tử còn lại được
khởi tạo giá trị 0
int A[3][4] = { {1}, {4,5} };
int A[3][4] = { };Tất cả đều mang giá trị 0

46
Ví dụ bảng cửu chương
#include <stdio.h>
void main()
{
/* bảng cửu chương cho cả số 0 */
int cuuchuong[10][10];
int i, j;

/* tạo giá trị cho bảng cửu chương */


for (i=0; i<=9; i++)
for (j=0; j<=9; j++)
cuuchuong[i][j] = i*j;

printf("Nhap hai so cua bang cuu chuong\n");


printf("So 1: "); scanf("%d", &i);
printf("So 2: "); scanf("%d", &j);

printf("Giá trị trong bang la %d", cuuchuong[i][j]);


}
Ví dụ (tiếp)
/*Biểu diễn lượng mưa theo tháng của 5 năm khác nhau*/

#define NYEARS 5
#define NMONTHS 12
int table[NYEARS][NMONTHS] ={
{30,40,75,95,130,220,210,185,135,80,40,45},
{25,25,80,75,115,270,200,165, 85, 5,10, 0},
{35,45,90,80,100,205,135,140,170,75,60,95},
{30,40,70,70, 90,180,180,210,145,35,85,80},
{30,35,30,90,150,230,305,295, 60,95,80,30}
};

• table[i][j] cho phép truy cập vào giá trị lượng mưa tháng
thứ (j+1) và năm (2000+i)
Ví dụ (tiếp)

printf("Enter a year: ");


scanf("%d", &year);
if (month < 2000 && month > 2004) {
printf("Year between 2000 and 2004\n");
return 1;
}
printf("Enter a month: ");
scanf("%d", &month);
if (month < 1 && month > 12) {
printf("Invalid month\n");
return 1;
}
printf("Average rainfall is mm.\n", table[year-2000][month-1]);
Bài tập
• Cho khai báo nguyên mẫu các hàm như sau:
int nhapdayso(int a[]);
int timmax(int a[], int n);
• Hàm nhapdayso() cho phép nhập số phần tử và các
con số trong dãy số để lưu vào mảng a.
• Hàm trả về số phần tử của dãy số đã nhập.

• Hàm timmax() tìm giá trị lớn nhất trong 1 dãy số


truyền vào.

• Viết định nghĩa các hàm và chương trình chính cho


phép nhập vào 2 dãy số khác nhau và tìm giá trị lớn
nhất của các số trong cả 2 dãy số đó.
NGÔN NGỮ LẬP TRÌNH C

TS. Đỗ Quốc Huy


Bộ môn Khoa Học Máy Tính
huydq@soict.hust.edu.vn

1
Nội Dung Khóa Học
1st Tổng quan lập trình máy tính
2nd Ngôn ngữ lập trình C
3rd Kiểu dữ liệu
4th Vào ra dữ liệu
5th Biểu thức trong ngôn ngữ C
6th Lệnh rẽ nhánh
7th Lệnh lặp (1)
8th Lênh lặp (2)
9th Hàm
10th Mảng
11th Con trỏ
12th Chuỗi
13th Cấu trúc dữ liệu
14th Vào ra với file
15th Final Exam

2
Nội Dung Bài Học

➢ Khái niệm địa chỉ bộ nhớ và con trỏ

➢ Sử dụng con trỏ

➢ Con trỏ và mảng, hàm


Giới thiệu
00000
• Là một khái niệm “mạnh“ 00001
trong C
– Cho phép tính toán trên con trỏ
– Sử dụng con trỏ hàm Biến
• Cho phép truy nhập gián tiếp
tới một đối tượng có địa chỉ
(biến, hàm) Con trỏ
– Truy nhập trực tiếp→ thông qua
tên FFFFF
Bộ nhớ

4
Địa chỉ bộ nhớ của biến
• Bộ nhớ gồm dãy các ô nhớ
• Mỗi ô nhớ là 1 byte
• Mỗi ô nhớ có 1 địa chỉ riêng
• Các biến trong chương trình được lưu tại vùng nhớ nào
đó trong bộ nhớ
char ch = ’A’;

ch:

0x2000 ’A’

Địa chỉ bộ nhớ Giá trị của biến


của biến ch ch
Địa chỉ
• Khi khai báo biến, tùy thuộc vào kiểu, biến sẽ được cấp một số ô
nhớ liên tục nhau

– VD: Biến int được cấp 4 bytes,


• float được cấp 4 bytes,..

– Địa chỉ của biến, là địa chỉ của byte đầu tiên trong số các byte
được cấp

– Khi gán giá trị cho biến, nội dung các byte cung cấp cho biến
sẽ thay đổi

6
Ví dụ
int N; Địa chỉ biến N ?
?
E8
float x; 03 N
char Arr[4]; ?
00
Địa chỉ biến X ?
00
?
00
N = 1000;//03E8 ?
00
?
1B X
X=9.6875;//411B0000 Địa chỉ biến Arr ?
41
for(i=0;i<4;i++) ?
01
?
05
Arr[i]= 4*i+1; ?
09 Arr
?
Địa chỉ của 1 biến là địa chỉ byte 0D

nhớ đầu tiên được cung cấp cho


biến để lưu trữ dữ liệu
Bộ nhớ
7
Con trỏ
• Con trỏ là 1 biến mà giá trị 0000
của nó là địa chỉ của 1 Biến nhớ
vùng nhớ EB03 ABCD
• Vùng nhớ này có thể dùng
để chứa các biến có kiểu cơ
bản (nguyên, thực, ký
tự,…) hay có cấu trúc
(mảng, bản ghi,..)
• Con trỏ dùng “trỏ tới” 1
biến nhớ ABCD EFCD
Biến con trỏ
• Có thể trỏ tới 1 hàm
• Có thể trỏ tới con trỏ khác FFFF
Bộ nhớ
8
Con trỏ

1 biến có thể lưu giá trị


là địa chỉ bộ nhớ của 1
biến khác
0x3A15

0x2000
chPtr

0x1FFE 0x1FFF 0x2000 0x2001 0x2002


etc
‘B’
ch
Nội Dung Bài Học

➢ Khái niệm địa chỉ bộ nhớ và con trỏ


➢ Sử dụng con trỏ
➢ Khai báo con trỏ
➢ Tham chiếu và khử tham chiếu
➢ Các phép toán với con trỏ
➢ Con trỏ và mảng, hàm
Khai báo con trỏ
• Khai báo con trỏ là 1 biến
• Chứa 1 địa chỉ bộ nhớ
• Thường trỏ tới 1 kiểu dữ liệu xác định

Kiểu * Tên;
Ví dụ:
char* cPtr;
cPtr:
0x2004

Có thể chứa địa chỉ của


biến kiểu char
Khai báo con trỏ

• Kiểu: Kiểu của biến mà con trỏ “Tên” trỏ tới


• Giá trị của con trỏ có thể thay đổi được
• Trỏ tới các biến khác nhau, có cùng kiểu
• Kiểu biến mà con trỏ trỏ tới không thay đổi được
• Muốn thay đổi phải thực hiện “ép kiểu”
• Ví dụ:
• int * pi; //Con trỏ, trỏ tới 1 biến kiểu nguyên
• char * pc; //Con trỏ, trỏ tới 1 biến kiểu ký tự

12
Khai báo con trỏ (tiếp)

•Có thể tạo con trỏ đến biến có kiểu dữ liệu bất kì

Ví dụ: int * numPtr;


float * xPtr;

•Một biến con trỏ luôn được khai báo đi kèm với
toán tử *
Ví dụ: int *numPtr1, *numPtr2;
float *xPtr, *yPtr;
Toán tử địa chỉ (&)
• Ký hiệu: &
• Là toán tử 1 ngôi, trả về địa chỉ của biến
Địa chỉ biến có thể được gán cho 1 con trỏ,
trỏ tới đối tượng cùng kiểu
• Ví dụ
char ch = ’A’;

0x2000 ’A’
&ch trả về giá trị 0x2000

14
Tham chiếu

•Dùng toán tử & để xác lập địa chỉ tham chiếu cho con trỏ

Ví dụ: char c = ’A’;


char *cPtr;
cPtr = &c;
Xác lập địa chỉ của c cho
con trỏ cPtr

c: cPtr:
… A … 0x2000
0x2000 0x2004
Toán tử địa chỉ (&) (tiếp)
• Ví dụ 0000
short int N; // &N→ ABCD Biến N
short int *pi; ABCD

pi = &N; // piABCD

ABCD EFCD
Biến pi
FFFF
Bộ nhớ
16
Chú ý về con trỏ

•Chỉ có thể xác lập tham chiếu cho con trỏ tới địa
chỉ của biến có kiểu tương thích với con trỏ

int aNumber;
Lỗi tương thích
char *ptr; về kiểu dữ liệu
Ví dụ:
ptr = &aNumber;

•Để in giá trị địa chỉ lưu bởi 1 con trỏ ta có thể
sử dụng định dạng in %p

Ví dụ: printf(“%p”, ptr);


Con trỏ NULL

Chú ý con trỏ không được khởi


int *numPtr; tạo giá trị tham chiếu!

???
numPtr

•Khởi tạo con trỏ với giá trị


NULL để chắc chắn
không sử dụng tham chiếu sai trong chương trình
int *numPtr = NULL;
NULL Con trỏ có tham chiếu NULL
(không chứa địa chỉ nào cả)
numPtr
Toán tử nội dung (*)
• Cho phép truy cập biến có địa chỉ bộ nhớ lưu bởi
1 con trỏ
• Được biết đến như toán tử dùng để “khử tham
chiếu” cho con trỏ
• Tránh nhầm lẫn với toán tử * dùng trong khai báo
con trỏ

Ví dụ: char c = ’A’;


char *cPtr = NULL;
cPtr = &c;
*cPtr = ’B’; Đổi giá trị của biến c
trỏ bởi cPtr
Toán tử nội dung (*)
0000
Biến N
20
10 ABCD

• Ví dụ
• short int N;
• short int * pi;
N= 10; *pi = 10;
• pi = &N;
• N = 10;//Vùng nhớ mà pi trỏ tới ABCD EFCD
mang giá trị 10; Vậy *pi=10 Con trỏ pi
• *pi = 20;// Vùng nhớ pi trỏ tới được
gán giá trị 20; Vậy N= 20 FFFF
Bộ nhớ
20
Các bước sử dụng con trỏ

• Bước 1: Khai báo biến được trỏ bởi con trỏ

int num;
char ch = ‘A’;
float x;

num:
ch: ‘A’
x:
Các bước sử dụng con trỏ (tiếp)

• Bước 2: Khai báo biến con trỏ


int num;
char ch = ‘A’; numPtr: NULL
float x; chPtr: NULL
int* numPtr = NULL; xPtr: NULL
char *chPtr = NULL;
float * xPtr = NULL;
num:
ch: ‘A’
x:
Các bước sử dụng con trỏ (tiếp)

• Bước 3: Xác lập tham chiếu cho con trỏ


int num; numPtr: &num
char ch = ‘A’;
float x; chPtr: &ch
int* numPtr = NULL;
char *chPtr = NULL; xPtr: &x
float * xPtr = NULL;
numPtr = &num;
chPtr = &ch; num:
xPtr = &x;
ch: ‘A’
x:
Các bước sử dụng con trỏ (tiếp)

• Bước 4: Khử tham chiếu con trỏ


int num;
char ch = ‘A’; numPtr: &num
float x;
chPtr: &ch
int* numPtr = NULL;
char *chPtr = NULL; xPtr: &x
float * xPtr = NULL;
numPtr = &num;
chPtr = &ch; num: 65
xPtr = &x;
ch: ‘A’
*xPtr = 0.25;
*numPtr = *chPtr; x: 0.25
Lỗi thường gặp
• Không thể tham chiếu1con trỏ đến 1 hằng số
hay 1 biểu thức
• Cũng không thể thay đổi địa chỉ của 1 biến
trong bộ nhớ (bởi vì nó không được quyết định
bởi người sử dụng!)

• Do vậy sau đây là một số lỗi:


– ptr = &3;
– ptr = &(x+5);
– &x = ptr;
– &x = 0x2000;
Gán giá trị cho con trỏ
• Con trỏ được gán địa chỉ của 1 biến
• Biến cùng kiểu với kiểu mà con trỏ trỏ tới
Nếu không, cần phải ép kiểu
• Con trỏ được gán giá trị của con trỏ khác
• 2 con trỏ sẽ trỏ tới cùng 1 biến(do cùng địa chỉ)
• 2 con trỏ nên cùng kiểu trỏ đến
Nếu không, phải ép kiểu
• Con trỏ được gán giá trị NULL
Ví dụ: int *p; p = 0;
• Gán nội dung vùng nhớ 2 con trỏ trỏ tới.
Ví dụ: int *p1, *p2; *p1 = *p2;

26
Ví dụ
#include <stdio.h> #include <stdio.h>
int main(){ int main(){
int N=5, M=10; int N=5, M=10;
int *p1 = &N; int *p1 = &N;
int *p2 = &M; int *p2 = &M;
*p1 = *p2; p1 = p2;
printf("%d %d",*p1,*p2); printf("%d %d",*p1,*p2);
} }

10 10 10 10

27
Ví dụ→Trường hợp 1 0000
#include <stdio.h>
int main(){ 10
5 ABCD
int N=5, M=10; 10 ABCF
int *p1 = &N;
int *p2 = &M;

p2 p1
ABCF
*p1 = *p2;
printf("%d %d",*p1,*p2); ABCD
}

FFFF
Bộ nhớ
28
Ví dụ→Trường hợp 2 0000
#include <stdio.h>
int main(){ 5 ABCD
int N=5, M=10; 10 ABCF
int *p1 = &N;
int *p2 = &M;

p2 p1
ABCF
p1 = p2;
ABCD
ABCF
printf("%d %d",*p1,*p2);
}

FFFF
Bộ nhớ
29
Các phép toán trên con trỏ

• Cộng con trỏ với một số nguyên


• Kết quả: Con trỏ cùng kiểu
• Trừ con trỏ với một số nguyên
• Kết quả: Con trỏ cùng kiểu
• Trừ 2 con trỏ cùng kiểu cho nhau
• Kết quả: Một số nguyên
• Khoảng cách giữa 2 con trỏ được đo bằng số phần tử thuộc kiểu
dữ liệu mà con trỏ trỏ tới

30
Ví dụ
0000
• int N=1000,
E8 ABC0
M=2000,P=3000; P 03 ABC1
• int * p1 = &P, *p2 = &N; M D0 ABC2
07 ABC3
p1 - p2 → -2 B8 ABC4
N 0B ABC5
* (p2-1) → 2000 ABC6

* ++ p1 → 2000 p2 ABC4
Ghi chú: p1 ABC0
ABC2
• Kiểu int, các phần tử cách nhau 4 bytes
• Kiểu float, các phần tử cách nhau 4 bytes FFFF
Bộ nhớ
31
Con trỏ void
void * Tên_con_trỏ

• Là 1 con trỏ đặc biệt: con trỏ tới dữ liệu không định
kiểu.
• Có thể nhận giá trị là địa chỉ của 1 biến có kiểu dữ
liệu bất kỳ
• Ví dụ:
void * p, *q;
int n; float x;
p = &n; q= &x; \\Các câu lệnh hợp lệ

32
Nội Dung Bài Học

➢ Khái niệm địa chỉ bộ nhớ và con trỏ


➢ Sử dụng con trỏ
➢ Con trỏ và mảng, hàm
Tham số hàm và con trỏ

• Ví dụ: Tạo hàm là thay đổi giá trị của


2 biến truyền vào

x: 1 x: 2
swap
y: 2 y: 1
Truyền theo tham trị
#include <stdio.h>
void swap1(int a, int b) tmp:
{
int tmp; a: 1
tmp = a;
a = b; b: 2
b = tmp;
return;
}
int main()
{
int x = 1, y = 2; x: 1

swap1(x, y); y: 2
printf(“%d %d\n”, x, y);
return 0;
}
TRUYỀN THEO THAM TRỊ (TIẾP)
#include <stdio.h>
void swap1(int a, int b) tmp: 1
{
int tmp; a: 2

tmp = a; b: 1
a = b;
b = tmp;
return;
}
int main()
{ x: 1
int x = 1, y = 2;
swap1(x, y); y: 2
printf(“%d %d\n”, x, y);
return 0;
}
TRUYỀN THEO THAM CHIẾU
#include <stdio.h>
void swap2(int* a, int* b) tmp:
{
int tmp; a:
&x
tmp = *a;
*a = *b; b:
&y
*b = tmp;
return;
}
int main()
{
int x = 1, y = 2; x: 1

swap2(&x, &y); y: 2
printf(“%d %d\n”, x, y);
return 0;
}
TRUYỀN THEO THAM CHIẾU
#include <stdio.h>
void swap2(int* a, int* b) tmp: 1
{
int tmp; a:
&x
tmp = *a;
*a = *b; b:
&y
*b = tmp;
return;
}
int main()
{
int x = 1, y = 2; x: 2

swap2(&x, &y);
printf(“%d %d\n”, x, y); y: 1
return 0;
}
Tham số hàm là con trỏ

• Cho phép thay đổi giá trị của 1 biến


qua lời gọi hàm
• Truyền tham chiếu trong hàm scanf
char ch;
int numx;
float numy;
scanf(“%c %d %f”, &ch, &numx, &numy);
Ưu điểm
• Truyền theo tham chiếu sẽ hiệu quả hơn truyền theo
tham trị bởi chúng ta không mất thời gian tạo bản sao
giá trị truyền vào cho hàm mỗi lần gọi

• Có thể sử dụng tham số dạng tham chiếu để


tạo 1 hàm trả về nhiều hơn 1 giá trị

void maxmin(int a, int b,


int *max, int *min)
{
*max = (a>b) ? a : b;
*min = (a<b) ? a : b;
}
Nhược điểm

• Rất khó kiểm soát 1 chương trình sử dụng nhiều hàm


với tham số con trỏ vì khi đó 1 biến có thể bị thay đổi
ở bất kì đâu trong chương trình.

• Chỉ dùng hàm với tham số dạng tham chiếu (con trỏ)
khi cần thiết thay đổi giá trị 1 biến được truyền vào
cho hàm.
Mảng và con trỏ

◼ Chú ý rằng 1 mảng tương ứng với địa chỉ phần tử đầu tiên
của nó
◼ Do vậy 1 mảng A là con trỏ đến phần tử A[0]
Ví dụ: int A[10];
int *ptr;
ptr = A; /* ptr = &A[0] */

◼ Có sự tương đương giữa ptr và A vì cùng mang 1 địa chỉ


giống nhau
◼ Không thể thay đổi địa chỉ 1 mảng nhưng lại có thể thay đổi
địa chỉ của con trỏ
Ví dụ: int B[10];
ptr = B; /* OK */
A = B; /* KO */
Mảng và con trỏ (tiếp)

• Có thể truy cập vào các phần tử của 1 mảng thông qua
con trỏ
Ví dụ: int A[10];
int *ptr=A;
ptr[2] = 5; /* A[2] = 5 */

• Con trỏ có thể được tăng hoặc giảm trỏ đến phần tử khác
trong mảng
• Nếu p là một con trỏ tới 1 kiểu xác định, p+1 đưa ra địa chỉ
chính xác của biến tiếp theo trong bộ nhớ có cùng kiểu
• p++, p--, hay p += i cũng đem lai ý nghĩa tương tự

Ví dụ: ptr += 2; /* ptr → A[2] */


ptr[1] = 3; /* A[3] = 3 */
A[0] = *(ptr+1); /* A[0] = A[3] */
Ví dụ
• int A[10];
• int * p = A; // int *p = &A[0]
for(i = 0; i < 10; i ++)
printf("%d ", *(p + i) );
for(i = 0; i < 10; i ++)
printf("%d ", p[i]);
for(i = 0; i < 10; i ++)
printf("%d ", *(p++) );

44
Câu hỏi 1
#include<stdio.h>
30
int main()
{
int a=3, *p;
p = &a;
printf("%d\n", a * *p * a + *p);
return 0;
}

45
Câu hỏi 2
#include<stdio.h>
int main(){
int arr[2][2][2] = {10, 2, 3, 4, 5,
6, 7, 8};
int *p, *q; 8, 5
p = &arr[1][1][1];
q = (int *) arr;
printf("%d, %d\n", *p, *(q+4) );
return 0;
}

46
Truyền mảng cho hàm

• Một mảng truyền cho hàm tương ứng với


địa chỉ gốc của nó
• Do đó có 2 cách tương đương để khai báo
hàm có thể truyền tham số là mảng
Như một mảng có kích
thước không
– f (int array[]) xác định
hoặc
– f (int *ptr) Như một con trỏ
Chương trình dãy số (v2)

#include <stdio.h>

void nhapMang(int *ptr, int num)


{
int i;
for(i=0; i<num; i++) {
printf(“Phan tu thu %d:”, i+1);
scanf("%d", ptr+i);
} Lấy địa chỉ phần tử
} chỉ số i qua con trỏ

void inNguoc(int *ptr, int num)


{
int i; Lấy giá trị phần tử
for(i=num-1; i>=0; i--) chỉ số i qua con trỏ
printf("%5d", ptr[i]);
}
Chương trình dãy số (v2)

int main(void)
{
int n, A[10];
printf(“Nhap so phan tu trong day (n<=10):");
scanf("%d",&n);

printf(“Nhap cac phan tu trong day:\n");


nhapMang(A, n);

printf(“Day so sau khi dao lai:\n");


inNguoc(A, n);

return 0;
Chương trình chính
}
vẫn không thay đổi khi
thay cách khai báo
hàm
Bài TẬP

Viết chương trình sắp xếp 1 mảng các dãy số


nhập vào từ bàn phím. (sử dụng con trỏ để
lấy địa chỉ của phần tử hoặc lấy giá trị
phần tử qua chỉ số).
NGÔN NGỮ LẬP TRÌNH C

TS. Đỗ Quốc Huy


Bộ môn Khoa Học Máy Tính
huydq@soict.hust.edu.vn

1
Nội Dung Khóa Học
1st Tổng quan lập trình máy tính
2nd Ngôn ngữ lập trình C
3rd Kiểu dữ liệu
4th Vào ra dữ liệu
5th Biểu thức trong ngôn ngữ C
6th Lệnh rẽ nhánh
7th Lệnh lặp (1)
8th Lênh lặp (2)
9th Hàm
10th Mảng
11th Con trỏ
12th Chuỗi
13th Cấu trúc dữ liệu
14th Vào ra với file
15th Final Exam

2
Khái niệm xâu ký tự

‘T’ ‘i’ ‘n‘ ‘ ‘ ‘h’ ‘o’ ‘c’ ‘\0’


• Xâu kí tự (string) là 1 dãy các kí tự viết liên
tiếp nhau
– Độ dài xâu là số kí tự có trong xâu
– Xâu không có kí tự nào: Xâu rỗng
• Ví dụ: "Tin hoc", "String"
• Lưu trữ: kết thúc xâu bằng kí tự ‘\0’ hay
NULL (mã ASCII là 0)

3
Khai báo chuỗi
char tên_xâu [số_kí_tự_tối_đa];

• Để lưu trữ 1 xâu có n kí tự, cần 1 mảng có


kích thước n+1
– Phần tử cuối cùng chứa ký tự NULL
Ví dụ
• Để lưu trữ xâu “Tin hoc” → phải khai báo
xâu có số phần tử tối đa ít nhất là 8
char str[8] = "Tin hoc";

•4
Biểu diễn trong bộ nhớ

• Nhắc lại:
– Các ô trong bộ nhớ được đánh địa chỉ
– Một khai báo biến kí tự cho phép dành 1 “ô”
để chứa giá trị

Ví dụ: char ch;

ch = ‘B’;

0 x1 FFE 0 x1 FFF 0 x2 0 0 0 0 x2 0 0 1 0 x2 0 0 2

‘B’ etc
ch
Biểu diễn chuỗi kí tự
• 1 chuỗi được biểu diễn bởi 1 mảng kí tự
• Mỗi phần tử của mảng chứa 1 char
• Tên chuỗi là địa chỉ trỏ đến phần tử đầu tiên của
mảng

Ví dụ: char name[5];

name
= 0x2000

0x2 000 0x2 004


Khai báo chuỗi

Khai báo 1:
char name[5] = “Ann”; Kí tự đánh dấu kết
thúc xâu (kí tự rỗng)

name A n n \0
= 0x2000

0x2 000 0x2 004

Khai báo tương đương:


char name[5] = {’A’,’n’,’n’,’\0’};
Khai báo chuỗi (tiếp)
Khai báo 2: Lấy thêm 1 ô kí tự
char name[] = “Ann”; cho ‘\0’

name A n n \0
= 0x2000
0x2 000 0x2003
Chú ý:
char name[] = ‘Ann’; Không có kí tự kết
thúc xâu (‘\0’)

name A n n
= 0x2000
0x2 000 0x2 002
Khai báo chuỗi (tiếp)

Khai báo 3:
char *name = “Ann”;

0x3000 A n n \0
name

0x3000 0x3 003


Có thể thay đổi
tham chiếu cho
con trỏ name
Xuất nhập chuỗi
#include <stdio.h> Đặt tên cho
hằng số
#define MAXLENGTH 15
Số kí tự tối đa có
int main() trong xâu là
{ MAXLENGTH-1
char str1[MAXLENGTH];
char str2[MAXLENGTH]; Không có toán
tử & ở đây

scanf("%s", str1); gets() cho phép


gets(str2); nhập xâu với kí tự
printf("%s\n%s\n", str1, str2); trắng

return 0;
}
Mảng kí tự và Chuỗi kí tự
• Tập hợp các kí tự viết liên tiếp nhau
• Truy nhập 1 phần tử của xâu ký tự (là 1 ký tự)
giống như truy nhập vào 1 phần tử của mảng:
Tên[Chỉ_số]

• 1 chuỗi kí tự là 1 mảng kí tự nhưng không phải là


điều ngược lại
• 1 chuỗi kí tự phải có kí tự kết thúc hay còn gọi là kí
tự rỗng (’\0’)
Mảng kí tự và Chuỗi kí tự
• Kí tự kết thúc
• dùng để báo hiệu điểm dừng của xâu.
• tiện cho việc xử lí xâu trong các hàm, ví dụ:
hàm printf(), scanf(), v.v.

• Xâu kí tự độ dài 1 ≠ kí tự ("A" =’A’ ?)


– ‘A’ là 1 kí tự, được lưu trữ trong 1 byte
– “A” là 1 xâu kí tự, ngoài kí tự ‘A’ còn có kí tự ‘\0’ => được
lưu trữ trong 2 byte

12
Truy nhập phần tử của xâu
Giống như truy nhập tới 1 phần tử của mảng
ký tự
tên_xâu [chỉ_số_của_kí_tự]
Ví dụ: char Str[10] = "Tin hoc";

T i n - h o 1 \0
c \0 ? ?
Str[0] → ‘T’ Str[3] = ‘-’;
Str[3] → ‘ ’ Str[7] = ‘ ’;
Str[7] → ‘\0 ‘ Str[8] = ‘1 ‘ ;
Str: Tin-hoc 1
Str[8] → ? Str[9] = ‘\0’;
13
Kí tự trong xâu
0 x3 9 9 5 0x399C

name J o h n \0
= 0x3995

index 0 index 2

char name[8] = “John”;


int i = 2;

printf(“Char at index %d is %c.\n”, i, name[i]);

o u t p u t : Char a t i n d e x 2 i s h .
Chương trình đếm kí tự
• Đếm số kí tự không phải là kí tự trắng trong 1 chuỗi được nhập vào
#include <stdio.h>

int main()
{
char str[80]; int dem, i;

printf("Nhap xau bat ki: ");


gets(str);

dem = 0; i = 0;
while ( str[i] != '\0' ) {
if ( str[i] != ' ' ) dem++; i++;
}
printf(“So ki tu khac trang trong xau la %d", dem);

return 0;
}
Ví dụ: Nhập xâu và đếm số ký tự ‘*’
#include <stdio.h> Tính chiều dài của xâu
void main(){ d=0;
char Str[100];
while(Str[d] != '\0') d++;
int d=0, i=0;
printf("Nhap xau ky tu: "); gets(Str);
while(Str[i] != '\0'){
if(Str[i]=='*')
d++;
i++;
}
printf("Ket qua : %d",d);
}

16
Nhập câu và đưa ra dưới dạng cột
1. #include <stdio.h>
2. void main(){
3. char S[100];
4. int i=0;
5. printf("Nhap xau: "); gets(S);
6. while(S[i] != '\0'){ //32 là mã ASCII của phím space
7. if(S[i] != 32 && S[i+1]==32) printf("%c\n",S[i]);
8. else if(S[i] != 32) printf("%c",S[i]);
9. i++;
Đếm số từ, nếu các từ
10. }
11. printf("\n\n");
được cách nhau bởi dấu
12.} phân cách

17
Các hàm xử lý xâu ký tự
Vào/ra xâu ký tự
• Tệp tiêu đề: stdio.h
• Nhập xâu kí tự
– gets(tên_xâu);
– scanf("%s",&tên_xâu);
• Hiển thị xâu kí tự
– puts(tên_xâu);
– printf("%s",tên_xâu);

Sự khác nhau giữa gets và scanf?

18
Lưu ý:
• Hàm gets: các trình biên dịch mới hiện tại không còn hỗ trợ
nữa
• Lý do: gets không kiểm tra kích thước xâu được nhập

• Giải pháp: dùng fgets(str, so_ky_tu_toi_da, stdin);

19
Các hàm xử lý xâu ký tự
Tệp tiêu đề: string.h
• #include <string.h>
• Phải sử dụng các hàm thư viện cho các thao tác
xử lí chuỗi:
– Gán: strcpy()
– Nối chuỗi: strcat()
– So sánh: strcmp()
– Lấy độ dài xâu: strlen()
– v.v.

20
Các hàm xử lý xâu ký tự

Chú ý:
char str[100] = "Hello world";
char * p = str;
• p là con trỏ, trỏ tới mảng các ký tự/xâu ký tự
p+6 : (Phép tính toán trên con trỏ), cũng là xâu ký tự. p+6
trỏ tới xâu "world"
• Xâu ký tự, có thể được khai báo char *

21
Các hàm xử lý xâu ký tự
size_t strlen(const char * xâu)
– Trả về độ dài xâu
printf("%d ",strlen("Hello world"));  11
char * strcpy(char * đích, const char * nguồn)
– sao chép nội dung xâu nguồn vào xâu đích, trả về giá trị
xâu nguồn
char Str[20];
printf("%s ",strcpy(Str,"Hello"));  Hello
printf("%s", Str);  Hello
Chú ý: Phép gán Str = "Hello" là không hợp lệ

22
Gán chuỗi
#include <stdio.h>
#include <string.h>

#define MAXLENGTH 100

int main()
{
char string1[MAXLENGTH];
char string2[MAXLENGTH];

strcpy(string1, “Hello World!”);


strcpy(string2, string1);

return 0;
}

st r i ng1: “ Hello World! ”


st r i ng2: “ Hello World! ”
Gán dạng con trỏ

char *name1 = “Ann”;


char *name2 = “Dave”;

0x2000 A n n \0
name1

0x2 000 0x2003

0x3990 D a v e \0
name2

0x3 990 0x3 994


Gán dạng con trỏ (tiếp)

name2 = name1;

0x2000 A n n \0
name1

0x2 000 0x2003

0x2000 D a v e \0
name2

0x3 990 0x3 994


Lỗi thường gặp
• Dùng toán tử gán không tương thích kiểu
char name1[5] = “Ann”;
char name2[5] = “Dave”;
name2 = name1;

• Không đủ bộ nhớ
char name[] = “Ann”;
strcpy(name, “David”);
name A n n \0

name D a v i d \0
Các hàm xử lý xâu ký tự
int strcmp(const char * xâu_1, const char * xâu_2)
– So sánh hai xâu.
– Trả về giá trị 0 nếu hai xâu giống nhau;
– Giá trị < 0: xâu_1 < xâu_2
– Giá trị >0: xâu_1 > xâu_2
Ví dụ
char Str[20];
strcpy(Str, "hello");
printf("%d", strcmp(Str, "hello"));→0
printf("%d", strcmp(Str, "hello! "); →-1 (!?)
printf("%d", strcmp(Str, "Hello"); → 1 (!?)

27
So sánh chuỗi

Trả về
0 : nếu string1 giống string2
strcpy(string1, “Apple”); < 0: nếu string1 < string2
strcpy(string2, “Wax”); > 0: nếu string1 > string2
if (strcmp(string1, string2) < 0)
{
printf(“%s %s\n”, string1, string2);
}
else
{
printf(“%s %s\n”, string2, string1);
}

o u t p u t : Apple Wax
Lỗi về so sánh

strcpy(string1, “Apple”); Chỉ là phép so


strcpy(string2, “Wax”); sánh địa chỉ bộ
nhớ
if (string1 < string2)
{
printf(“%s %s\n”, string1, string2);
}
else
{
printf(“%s %s\n”, string2, string1);
}
Các hàm xử lý xâu ký tự
char * strcat(char * xđích, const char * nguồn)
– Ghép nối xâu nguồn vào ngay sau xâu đích, trả
lại xâu kết quả
Ví dụ
char Str[20];
strcpy(Str,"Hello ");
printf("%s ",strcat(Str,"world"));  Hello world
printf("\n%s",Str);  Hello world

30
Nối chuỗi
char string1[80];
char string2[80];

strcpy(string1, “Goodbye”);
strcpy(string2, “, Cruel ”);

strcat(string1, string2);
strcat(string1, string2);
strcat(string1, “World!”);

st r i ng1: “ Goodbye, Cruel , Cruel World! ”


string2: “ , Cruel ”
Lỗi bộ nhớ

char name[5];

strcpy(name, “Ann”);
strcat(name, “ Smith”);

name A n n \0
= 0x2000

0x2 000 0x2 004

A n n S m i t h \0
Các hàm xử lý xâu ký tự
char * strchr (const char * s, int c)
– Trả về con trỏ trỏ tới vị trí xuất hiện đầu tiên của ký tự
c trong s. Nếu không có trả về con trỏ null
strcpy(Str,"Hello world");
printf("%s ",strchr(Str,‘o'));  o world
char* strstr(const char * s1, const char * s2)
– Trả về con trỏ trỏ tới vị trí xuất hiện đầu tiên của chuỗi
s2 trong s1. Nếu không tồn tại, trả về con trỏ null
printf("%s ",strstr(Str, "llo"));  llo world

33
Lưu ý:

• Tương tự như hàm gets, một số hàm xử lý xâu:


strcpy, strcat cũng đã không dùng được đối với các
trình biên dịch mới.
• Tuy nhiên trong phạm vi môn C Intro, chúng ta vẫn
chấp nhận dùng các hàm trên với trình biên dịch phù
hợp.

34
Các hàm xử lý xâu ký tự (tiếp)
Tệp tiêu đề: stdlib.h
• int atoi(const char * str):
– Chuyển một xâu kí tự thành một số nguyên tương ứng
– Ví dụ: atoi("1234") →1234
• int atol(const char * str):
– Chuyển xâu kí tự thành số long int
• float atof(const char * str):
– Chuyển xâu kí tự thành số thực
– Ví dụ: atof("123.456E-2") →1.23456
• Thất bại cả 3 hàm: trả về 0

35
Tham số hàm là chuỗi
• Giống như mảng ta có thể khai báo chuỗi như là
tham số với char* hoặc char[]
void greeting (char* name)
void greeting (char name[])
• Chúng trỏ đến kí tự đầu tiên của chuỗi (mảng kí tự)
• Thay đổi nội dung chuỗi truyền vào sẽ làm thay
đổi chuỗi kí tự gốc
• Không cần truyền số kí tự trong chuỗi cho hàm
Ví dụ

Viết hàm chuyển đổi chữ thường thành chữ hoa

char *capitalize(char * str)


{
for (i=0; i<strlen(str); i++)
if ( str[i]>=’a’&& str[i]<=’z’)&&
(i==0||str[i-1]==’ ’) )
str[i] = ’A’ + (str[i]-’a’);
return str;
}
Chương trình tách tên
• Viết 1 hàm trả về con trỏ tới phần tên của 1 xâu họ và tên là
tham số của hàm

char * timten(const char hoten[]) Giả thiết họ tên là xâu


không rỗng và không có
{
kí tự trắng thừa
int i;
i = strlen(hoten)-1;
/* tìm kí tự trắng cuối cùng trong xâu */
while (i >= 0 && hoten[i] != ' ') i--;
return hoten + i + 1;
}
hoten+i+1

hoten T r a n T i e n \0

Vị trí kí tự trắng
được tìm thấy (i=4)
Chương trình tách tên (tiếp)
Khai báo const thể hiện
#include <stdio.h> giá trị của tham số không
bị thay đổi trong hàm
#include <string.h>
char * timten(const char hoten[]);

int main()
{
char hoten[80];

printf("Nhap mot xau ho va ten: ");


gets(hoten);

printf("Ten sau khi tach duoc: %s",

timten(hoten)); return 0;
}
Bài tập

• Viết chương trình với các hàm xử lí xâu


được tạo ra để làm các việc sau:
– Cắt bỏ các dấu cách thừa trong 1 xâu
– Đếm số từ hiện có trong 1 xâu
– Đảo ngược thứ tự các kí tự hiện có của một xâu
– Tách 1 xâu họ và tên thành 2 xâu mới cho phần
họ và phần tên
NGÔN NGỮ LẬP TRÌNH C

TS. Đỗ Quốc Huy


Bộ môn Khoa Học Máy Tính
huydq@soict.hust.edu.vn

1
Nội Dung Khóa Học
1st Tổng quan lập trình máy tính
2nd Ngôn ngữ lập trình C
3rd Kiểu dữ liệu
4th Vào ra dữ liệu
5th Biểu thức trong ngôn ngữ C
6th Lệnh rẽ nhánh
7th Lệnh lặp (1)
8th Lênh lặp (2)
9th Hàm
10th Mảng
11th Con trỏ
12th Chuỗi
13th Cấu trúc dữ liệu
14th Vào ra với file
15th Final Exam

2
Nội Dung Bài Học

➢ Khái niệm cấu trúc


➢ Khai báo cấu trúc
➢ Xử lý dữ liệu cấu trúc
➢ Một số ví dụ

3
Ví dụ → Bài toán quản lý thí sinh thi đại học

Để quản lý cần lưu trữ các thông tin


• Số báo danh: Số nguyên không dấu
• Họ tên sinh viên: Chuỗi ký tự không quá 30
• Khối thi: Ký tự (A,B,C..)
• Tổng điểm 3 môn thi: kiểu thực
Do vậy với mỗi sinh viên cần các biến
unsigned SBD;
char Ten[30];
char KhoiThi;
float KetQua;

4
Ví dụ → Bài toán quản lý thí sinh thi đại học
(tiếp)
Để quản lý danh sách (dưới 1000) thí sinh dự
thi, cần nhiều mảng rời rạc

#define MAX 1000


unsigned DS_SBD[MAX];
char DS_Ten[MAX][30];
char DS_KhoiThi[MAX];
float DS_KetQua[MAX];

5
Ví dụ → Bài toán quản lý thí sinh thi đại học (tiếp)
DS_SBD
123 456 789

DS_Ten Mảng ký tự
Nguyễn Nguyễn Trần An
Văn An Hoàng Nghĩa

DS_KhoiThi
A D A
DS_KetQua
24.5 28.5 22.0

Biến điều khiển


TS: TS: TS: i+2
i i+1 dùng duyệt mảng
6
Vấn đề & giải pháp
Dùng nhiều mảng
- Khó quản lý, dễ nhầm lẫn
- Không thể hiện cấu trúc thông tin dành cho từng
thí sinh
Cấu trúc thông tin
Mảng các cấu
dành cho một thí sinh
trúc thông tin
dành cho thí sinh THÔNG TIN THÍ SINH
THÔNG TIN THÍ SINH
Số báo
Số báo danh:…....123………
THÔNG TINdanh:…………………
THÍ SINH
THÔNG Họ
TINvà Tên:..Nguyễn
THÍ SINH
Họ và Tên:…………………… Văn An..
SốSốbáo
báodanh:…………………
danh:…………………
Khối thi:…………A………….
Khối thi:……………………….
Họ và Tên:……………………
Kết quả:………24.5……….
Họ và Tên:……………………
Kết quả:……………………….
Khối thi:……………………….
Khối thi:……………………….
Kếtquả:……………………….
quả:………………………. Mỗi phần tử của mảng
Kết
•7
là một cấu trúc thông tin
Khái niệm cấu trúc
• Cấu trúc là kiểu dữ liệu phức hợp, do người dùng
tự định nghĩa
– Kiểu cấu trúc bao gồm nhiều thành phần có thể thuộc
các kiểu dữ liệu khác nhau

– Các thành phần:


– gọi là trường dữ liệu (field)
– không được truy nhập theo chỉ số (như mảng) mà theo tên
của trường.

8
Khái niệm → Ví dụ
• Kết quả học tập của sinh viên
– TenSV: Chuỗi ký tự
– MaSV: Chuỗi số/ số nguyên
– Điem: Số thực

• Điểm trong mặt phẳng


– Tên điểm: Ký tự (A, B, C..)
– Hoành độ: Số thực
– Tung độ: Số thực

9
Nội Dung Bài Học

➢ Khái niệm cấu trúc


➢ Khai báo cấu trúc
• Khái báo kiểu cấu trúc
• Khai báo biến cấu trúc
• Định nghĩa kiểu dữ liệu với typedef
➢ Xử lý dữ liệu cấu trúc
➢ Một số ví dụ

10
Khai báo kiểu cấu trúc
struct Tên_kiểu_cấu_ trúc {
<Khai báo các trường dữ liệu>
};

• struct: từ khóa, cho phép người dùng khai báo kiểu dữ liệu
mới: kiểu cấu trúc
• Tên_kiểu_cấu_truc: Tên của kiểu cấu trúc do người dùng
tự định nghĩa
– Tuân theo nguyên tắc đặt tên đối tượng trong C
• Khai báo các trường dữ liệu: Danh sách các khai báo
thành phần (trường:field) của cấu trúc
– Giống khai báo biến
– Các trường có thể có kiểu bất kỳ

11
Khai báo cấu trúc

struct complex
{ Khai báo tên của
int real; cấu trúc
int img;
}; Các trường của
cấu trúc

struct studentRec
{
Đừng quên dấu ; sau
char name[80]; khai báo cấu trúc
int mark;
};

Khai báo này mới chỉ tạo kiểu của cấu trúc chứ chưa có biến
Khai báo kiểu cấu trúc → Ví dụ
Thẻ sinh viên
Số hiệu:…(Chuỗi ký tự).. struct SinhVien{
char SHSV[10];
Tên sinh viên: (Chuỗi ký tự)
char Ten[30];
Năm sinh:…(Số nguyên)… int NS;
Khóa:……(Số nguyên)……. int Khoa;
Lớp:…… :.(Chuỗi ký tự). … char Lop [10];
};
Point2D
struct Point{
Hoành độ (x)…(Số thực)..
float x, y;
Tung độ (y)…..(Số thực).. };
13
Khai báo biến cấu trúc
• Khai báo kiểu cấu trúc nhằm tạo định nghĩa toàn thể cho các
cấu trúc sẽ được dùng sau này
– Không cung cấp không gian nhớ cho kiểu

• Khai báo biến cấu trúc nhằm yêu cầu chương trình tạo vùng
nhớ để lưu trữ các dữ liệu cho biến cấu trúc
– Chứa dữ liệu của các trường của cấu trúc

14
Khai báo biến cấu trúc → Cú pháp
Tồn tại định nghĩa kiểu cấu trúc
struct Kiểu_cấu_ trúc Tên_biến;
Khai báo trực tiếp
struct {
<Khai báo các trường dữ liệu>
}Tên_biến;
Kết hợp với khai báo kiểu
struct Kiểu_cấu_ trúc {
<Khai báo các trường dữ liệu>
}Tên_biến;

15
Khai báo biến cấu trúc → Ví dụ
Tồn tại định nghĩa kiểu cấu trúc
struct SinhVien SV1, SV2, Thu_khoa;
Khai báo trực tiếp
struct {
float x, y; //Tọa độ trên mặt phẳng
}A, B; //Khai báo 2 điểm A, B
Kết hợp với khai báo kiểu
struct Point_3D{
float x, y, z;// Tọa độ không gian
}A, B;

16
Khai báo biến cấu trúc → Ví dụ 2
Tồn tại định nghĩa kiểu cấu trúc
struct complex num;
Khai báo trực tiếp
struct {
int real, img;
}num; //Khai báo biến số phức
Kết hợp với khai báo kiểu
struct complex{
int real, img;
}num;

17
Khai báo biến cấu trúc → Ví dụ 3
Tồn tại định nghĩa kiểu cấu trúc
struct studentRec john;
Khai báo trực tiếp
struct {
char name[80];
int mark;
} john;

Kết hợp với khai báo kiểu


struct studentRec {
char name[80];
int mark;
} john;

18
Khai báo biến cấu trúc →Chú ý
Các cấu trúc có thể được khai báo lồng nhau
struct diem_thi {
float dToan, dLy, dHoa;
}
struct thi_sinh{
char SBD[10];
char ho_va_ten[30];
struct diem_thi ket_qua;
} thi_sinh_1, thi_sinh_2;

19
Khai báo biến cấu trúc →Chú ý
Có thể khai báo trực tiếp các trường dữ liệu của 1
cấu trúc bên trong cấu trúc khác
struct thi_sinh{
char SBD[10];
char ho_va_ten[30];
struct{
float dToan, dLy, dHoa;
} ket_qua;
} thi_sinh_1, thi_sinh_2;

20
Khai báo biến cấu trúc →Chú ý
Có thể gán giá trị khởi đầu cho một biến cấu
trúc, theo nguyên tắc như kiểu mảng
Ví dụ: struct SinhVien{
struct Date { char Ten[20];
int day; struct Date {
int month; int day;
int year; int month;
}; int year;
struct SinhVien{ } NS;
char Ten[30]; } SV = {"Tran Anh",20,12,1990};
struct Date NS;
} SV = {"Tran Anh",20,12,1990};

21
Định nghĩa kiểu dữ liệu với typedef
typedef <tên_cũ> <tên_mới>;
Mục đích
• Đặt tên mới đồng nghĩa với tên của 1 kiểu dữ liệu
đã được định nghĩa
– Thường được sử dụng cho kiểu cấu trúc
• Giúp cho khai báo trở nên quen thuộc và ít bị sai hơn
Ví dụ
typedef char Str80[80];
typedef long mask;
Str80 str="Bonjour tout le monde !";
mask a, b;

22
Định nghĩa kiểu (typedef)
• Ví dụ:
struct studentRec
{
char name[80];
int mark;
}; Cấu trúc dữ liệu Tên kiểu
hiện có mới

typedef struct studentRec Student;


Khai báo biến, con
Student studA, studB, *ptr;
trỏ hay mảng với
Student stud_list[100]; kiểu dữ liệu mới
Định nghĩa kiểu (tiếp)
• Có thể gộp định nghĩa kiểu với khai báo cấu trúc

typedef struct studentRec


{
char name[80]; int
mark;
} Student;

Student studA, studB, *ptr;


Student stud_list[100];
Chú ý
Cho phép đặt tên mới trùng với tên cũ
Ví dụ
struct point_3D {
float x, y, z;
}
struct point_3D M;
typedef struct point_3D point_3D;
point_3D N;

25
Chú ý
typedef struct point_2D {
float x, y;
} point_2D, diem_2_chieu, ten_bat_ki;
point_2D X;
diem_2_chieu Y;
ten_bat_ki Z;
Chú ý:
point_2D, diem_2_chieu, ten_bat_ki là
các tên cấu trúc, không phải tên biến

26
Nội Dung Bài Học

➢ Khái niệm cấu trúc


➢ Khai báo cấu trúc
➢ Xử lý dữ liệu cấu trúc
• Truy nhập các trường dữ liệu
• Phép gán giữa các biến cấu trúc
➢ Một số ví dụ

27
Truy cập các trường dữ liệu

• Cú pháp
tên_biến_cấu_trúc.tên_trường
• Lưu ý
– Dấu “.” là toán tử truy cập vào trường dữ
liệu trong cấu trúc
– Nếu trường dữ liệu là 1 cấu trúc → sử dụng
tiếp dấu “.” để truy cập vào thành phần mức
sâu hơn

28
Truy nhập cấu trúc
• Ví dụ:
struct studentRec {
char name[80];
int mark;
};

struct studentRec john;


Khởi tạo các trường
cấu trúc giống như
strcpy(john.name, “John”); đối với các biến
john.mark = 7; thông thường

printf(“%s co diem la %d”, john.name, john.mark);


Truy nhập cấu trúc (tiếp)
• Trường hợp 1 biến cấu trúc được trỏ bởi 1 con
trỏ, truy cập vào các trường cấu trúc thông qua
con trỏ bằng cách dùng toán tử '->'.

struct studentRec john;


struct studentRec *ptr = &john;
Khai báo một
con trỏ có kiểu
strcpy(ptr->name, “John”); cấu trúc
ptr->mark = 7;
printf(“%s co diem la %d”, ptr->name, ptr->mark);
Ví dụ
#include <stdio.h>
int main() {
struct {
char Ten[20];
struct Date {
int day;
int month;
int year;
} NS;
} SV = {"Tran Anh", 20, 12, 1990};
printf("Sinh vien %s (%d/%d/%d)", SV.Ten, SV.NS.day,
SV.NS.month, SV.NS.year);
return 0;
}

31
Ví dụ
Bài toán: Xây dựng một cấu trúc biểu diễn
điểm trong không gian 2 chiều.
– Nhập giá trị cho một biến kiểu cấu trúc này
– Hiển thị giá trị các trường dữ liệu của biến này
ra màn hình.
Thực hiện:
– Cấu trúc gồm: tên điểm, tọa độ x, tọa độ y
– Nhập, hiển thị từng trường của biến cấu trúc
như các biến dữ liệu khác

32
Ví dụ
#include<stdio.h>
#include<string.h>
typedef struct {
char ten[5];
int x, y;
} toado;
int main() {
toado t;
printf("Nhap thong tin toa do\n");
printf("Ten diem: ");
fgets(t.ten, sizeof(t.ten), stdin);
t.ten[strlen(t.ten) - 1] = '\0'; // Bỏ ký tự xuống dòng
printf("Toa do x: "); scanf("%d", &t.x);
printf("Toa do y: "); scanf("%d", &t.y);
printf("Gia tri cac truong\n");
printf("%-5s%3d%3d\n", t.ten, t.x, t.y);
return 0;
}

33
Phép gán giữa các biến cấu trúc
• C cho phép gán hai biến cấu trúc cùng kiểu:
Biến_cấu_trúc_1 = biến_cấu_trúc_2
• Ví dụ
– Xây dựng cấu trúc gồm họ tên và điểm thi học kỳ của
sinh viên
– Khai báo 3 biến cấu trúc: a, b, c
– Nhập giá trị cho biến a.
– Gán biến a cho biến b
– gán từng trường của a cho c.
– So sánh a, b và c ?

34
Ví dụ
#include<stdio.h>
#include<string.h>
typedef struct {
char hoten[20];
int diem;
} sinhvien;
int main() {
sinhvien a, b, c;
printf("Nhap thong tin sinh vien\n");
printf("Ho ten: ");
fgets(a.hoten, sizeof(a.hoten), stdin);
a.hoten[strlen(a.hoten) - 1] = '\0'; // Bỏ ký tự
xuống dòng
printf("Diem:"); scanf("%d", &a.diem);
35
Ví dụ
b = a; //Gan bien cau truc
strcpy(c.hoten, a.hoten); //Gan tung truong
c.diem = a.diem;
printf("Bien a: ");
printf("%-20s%3d\n", a.hoten, a.diem);
printf("Bien b: ");
printf("%-20s%3d\n", b.hoten, b.diem);
printf("Bien c: ");
printf("%-20s%3d\n", c.hoten, c.diem);
return 0;
}

36
Ví dụ →Kết quả

37
So sánh cấu trúc

• Không thể so sánh 2 cấu trúc bằng toán tử ==


• Chỉ có thể so sánh từng trường của cấu trúc

if (studA == studB)
{
printf(“Du lieu trung nhau.\n”);
}

if (strcmp(studA.name, studB.name) == 0
&& (studA.mark == studB.mark) )
{
printf(“Du lieu trung nhau.\n”);
}
Mảng của cấu trúc

stud_list
name: "fred"
0 stud_list[0]
mark:5

name: "ralph"
1
mark:7

name: "fong"
2
mark:4

name: "rachel"
3
mark:9
Chương trình ví dụ (1)

#include <stdio.h>
#define MAXLEN 80
#define MAXN 40

typedef struct studentRec


{
char lastname[MAXLEN];
int mark;
} Student;

int main()
{
int total, i;
Student stud_list[MAXN];

printf(“Co bao nhieu sinh vien? ");


scanf("%d", &total);
Chương trình ví dụ (2)
if (count > MAXN) {
printf(“So qua lon! Khong du bo nho.\n");
exit(1);
}

printf("\nNhap danh sach ten va diem:\n");


for (i=0; i < total; i++) {
printf(“Sinh vien %d: ", i+1);
scanf("%s %d", stud_list[i].name, &(stud_list[i].mark) );
}

printf("\nDanh sach nhung nguoi thi lai:\n\n");


for (i=0; i < total; i++)
if (stud_list[i].mark < 5) {
printf(“Ten : %s\n", stud_list[i].name);
printf(“Diem: %d\n\n", stud_list[i].mark);
}

return 0;
}
Truyền cấu trúc làm tham số
• Giống như mọi biến khác, cấu trúc có thể
được dùng làm tham số của hàm
• Cũng có 2 cách truyền tham số cho 1 cấu trúc
– Truyền cấu trúc theo dạng sử dụng giá trị của các
trường sẽ không làm thay đổi nội dung của biến
cấu trúc gốc
– Truyền địa chỉ của cấu trúc để có thể làm thay
đổi nội dung của cấu trúc gốc
Hàm trả về cấu trúc
• Trả về 1 “gói” chứa nhiều giá trị
Student readRecord ( void )
{
Student newStud;
printf(“Nhap ten va diem: ");
scanf("%s %f",newStud.name,&(newStud.mark));
return newStud;
}

main()
{
Student studA;
studA = readRecord();
}
Truyền cấu trúc qua con trỏ

void readStudent ( Student* item )


{
printf(“Please enter name and ID\n”);
scanf(“%s”, s->name);
scanf(“%f”, &(s->mark) );
}

int main()
{
Student studentA;
readStudent(&studentA);
}
Nội Dung Bài Học

➢ Khái niệm cấu trúc


➢ Khai báo cấu trúc
➢ Xử lý dữ liệu cấu trúc
➢ Một số ví dụ

45
Một số ví dụ
1. Nhập vào 2 số phức và đưa ra tổng và tích của chúng

46
Ví dụ 1→Kết quả thực hiện

47
Một số ví dụ
2. Nhập vào một danh sách (<100) sinh viên gồm họ tên, năm sinh. Kết
thúc nhập khi gặp SV có tên là rỗng
• Đưa danh sách vừa nhập ra màn hình.
• Đưa ra màn hình sinh viên lớn tuổi nhất

48
Ví dụ 2→Kết quả thực hiện

49
Một số ví dụ
3. Nhập danh sách có N (N < 100, nhập từ bàn phím) thí sinh gồm họ tên,
số báo danh, khoa dự thi và điểm thi
• Đưa ra DSSV đã sắp xếp theo kết quả thi
• Đưa ra danh sách sinh viên dự thi khoa CNTT có điểm thi từ 22.5
trở lên
• Nhập vào một số báo danh và in ra họ tên, điểm thi và khoa đăng
ký của thí sinh nếu tìm thấy. Nếu không tìm thấy thí sinh thì đưa ra
thông báo « không tìm thấy »

50
Ví dụ 3→Kết quả thực hiện

51
Ví dụ 3→Kết quả thực hiện

52
Ví dụ 3→Kết quả thực hiện

53
NGÔN NGỮ LẬP TRÌNH C

TS. Đỗ Quốc Huy


Bộ môn Khoa Học Máy Tính
huydq@soict.hust.edu.vn

1
Nội Dung Bài Học

VÀO RA TỆP
➢ Khái niệm tệp
➢ Khái niệm kênh nhập xuất
➢ Nhập xuất với tệp văn bản
➢ Nhập xuất với tệp nhị phân

2
Khái niệm
• Tệp (Tập tin/File):
– Tập hợp các dữ liệu cùng kiểu
– Có liên quan tới nhau
• Lưu trữ tệp
– Lưu trữ trên thiết bị lưu trữ ngoài
– Có tên riêng để phân biệt
• Phân thành 2 loại
– Tệp văn bản (text file)
– Tệp nhị phân (binary file)

3
Phân loại
• Tệp văn bản
– Được tổ chức theo từng dòng
• Trên mỗi dòng là các ký tự ASCII hiện thị được như chữ cái,
chữ số, dấu câu,…
– Cuối mỗi dòng là các ký tự điều khiển
• CR: Carriage Return - mã ASCII 13
• LF:Line Feed- Mã ASCII 10
• Tệp nhị phân
– Các phần tử của tệp là các số nhị phân dung mã hóa
thông tin
– Thông tin được mã hóa: số, cấu trúc dữ liệu,..

4
Tệp dữ liệu và mảng
• Mảng
– Được lưu trong bộ nhớ →dữ liệu bị mất đi khi tắt máy
– Truy nhập trực tiếp tới một phần tử qua số hiệu
– Kích thước mảng xác định trước
• Tệp
– Lưu trữ trên thiết bị lưu trữ ngoài→dữ liệu được lưu trữ
lâu dài, không bị mất đi khi tắt máy
– Không truy nhập trực tiếp qua số hiệu phần tử
– Kích thước có thể rất lớn và không cần xác định trước

5
Tổ chức tệp
Phần tử dữ liệu ngay sau khi mở file Ký hiệu đánh dấu kết thúc file

EOF

Phần tử dữ liệu Phần tử dữ liệu Phần tử dữ liệu


đầu tiên đang truy cập cuối cùng
• Tệp là dãy các phần tử kế tiếp nhau
– Sử dụng phần tử đặc biệt (EOF) để đánh dấu kết thúc tệp
• Con trỏ tệp:
– Xác định vị trí phần tử hiện có thể truy cập
– Khi mở file, con trỏ tệp luôn ở vị trí phần tử đầu
– Sau các thao tác đọc/ghi tệp, con trỏ file dịch chuyển về cuối tệp một
khỏng bằng số byte đã đọc/ ghi

6
Nội Dung Bài Học

VÀO RA TỆP
➢ Khái niệm tệp
➢ Khái niệm kênh nhập xuất
➢ Nhập xuất với tệp văn bản
➢ Nhập xuất với tệp nhị phân

7
Kênh xuất nhập
• Là 1 vùng đệm dùng cho việc
nhập xuất dữ liệu ở mức cao

• Chương trình chỉ đọc và ghi


dữ liệu trên vùng đệm, do đó
các hàm vào ra độc lập với
thiết bị đầu cuối

• HĐH đảm nhiệm việc đồng bộ


hoá dữ liệu trên vùng đệm với
thiết bị vào ra

• Có thể chuyển hướng vào ra


của 1 kênh xuất nhập cho
nhiều thiết bị khác nhau
Kênh xuất nhập chuẩn
• Luôn tồn tại 3 kênh xuất nhập chuẩn trong 1 chương
trình:

– stdin : kênh nhập chuẩn


– stdout: kênh xuất chuẩn
– stderr: kênh báo lỗi chuẩn

• scanf() và printf() là các hàm đọc và ghi trên các kênh


stdin và stdout tương ứng

• perror() là hàm in thông báo lỗi ra kênh stderr


Kênh xuất nhập chuẩn
• Việc định hướng các kênh xuất nhập chuẩn này
cho thiết bị nào phụ thuộc vào lúc chạy chương trình
• ngầm định: bàn phím cho stdin,
• màn hình cho stdout và stderr
Ví dụ xuất nhập

Input.c
$input 
#include <stdio.h> 10 
void main() Nhap so 10
{ $input 
abc 
int a; Khong phai so nguyen
$input >out.txt 
if ( scanf("%d", &a) != 1 ) 10 
$input >out.txt 
perror(“Khong phai so nguyen\n”); abc 
else Khong phai so nguyen
printf(“Nhap so %d", a);
} Chuyển hướng stdout
ra tệp out.txt
Vào ra tệp

• Tệp cần được mở trước khi sử dụng


• Mỗi tệp được gắn với một thẻ tệp khi mở
• Thao tác với tệp chỉ thông qua thẻ tệp mà
không sử dụng tên tệp
• Thẻ tệp được dùng như là kênh xuất nhập
cho các hàm vào ra tệp
• Cần phải đóng tệp khi kết thúc
• Các hàm thao tệp cơ bản: fopen(),
fclose(), fscanf(), fprintf().
Nội Dung Bài Học

VÀO RA TỆP
➢ Khái niệm tệp
➢ Khái niệm kênh nhập xuất
➢ Nhập xuất với tệp văn bản
➢ Nhập xuất với tệp nhị phân

13
Quy trình
• Khai báo biến tệp

• Mở tệp để làm việc


– Phân biệt các loại tệp và các mục đích mở tệp

• Truy nhập tệp


– Truy nhập để đọc/ ghi/thêm mới

– Phân biệt giữa các loại tệp

• Đóng tệp

14
Khai báo biến tệp

FILE * Con_Trỏ_Tệp

• Tệp được truy nhập qua con_trỏ_tệp

• Ví dụ
– FILE * f1, *f2;

– FILE *finput,*foutput;

15
Mở tệp
Con_Trỏ_Tệp = fopen(Tên_Tệp, Chế_độ mở)
• Hàm fopen() khi báo trong thư viện stdio.h
• Tên_Tệp: Kiểu chuỗi, xác định tên tệp trên đĩa
– Tên đầy đủ của tệp hoặc tệp trên thư mực hiện thời
• Chế độ mở: Hằng xâu, gồm các ký tự r/w/a/+/t/b
– Tùy thuộc kiểu tệp và mục đích sử dụng
– Kiểu tệp: t: text file; b: binary file
• Trả về NULL nếu có lỗi mở tệp

16
Chế độ mở tệp
“r” Đọc (tệp đã tồn tại) Báo lỗi nếu tệp
chưa tồn tại
“w” Mở mới để ghi, ghi đè (tệp đã tồn tại)

“a” Mở để ghi vào cuối, ghi thêm (tệp đã tồn


tại)
“r+” Đọc và ghi (tạo mới nếu không có tệp)

“w+” Ghi đè (tạo mới nếu không có tệp)

“a+” Ghi thêm (tạo mới nếu không có tệp)


Đóng tệp
int fclose(FILE * Con_Trỏ_Tệp)

• Hàm fclose() khi báo trong thư viện stdio.h


• Con_trỏ_tệp: Tên biến tệp

• Kết quả trả về


– 0: Nếu đóng tệp thành công

– EOF: Nếu có lỗi

18
Ví dụ

#include <stdio.h>
Mở tệp để ghi
int main()
{
FILE *out = fopen(“hello.txt”, “w”);

if (out == NULL)
{
perror(“Khong the mo tep de ghi.\n”);
return 1;
} Ghi dữ liệu ra
tệp
fprintf(out, “Hello world”);
fclose(out);
ðóng tệp khi
return 0;
kết thúc
}
Truy nhập tệp văn bản
• Tương tự như với bàn phím/ màn hình
• Yêu cầu chỉ rõ nguồn/đích thông tin
• Các thao tác
– Đọc dữ liệu từ tệp : fscanf() / fgets() /fgetc()
– Ghi dữ liệu ra tệp : fprintf() / fputs() /fputc()
– Dịch chuyển con trỏ tệp : fseek() / rewind()
– Kiểm tra kết thúc tệp : feof()
• Ví dụ:
– fprintf(FILE *fptr, Xâu_định dạng [,DS giá trị])
– fgets(char * Xâu ký tự, int n, FILE *fptr)

20
fprintf() và printf()
• fprintf(FILE * stream, …) hoạt động giống như
printf(…) nhưng được áp dụng trên 1 kênh xuất dữ
liệu bất kì (thường là tương ứng với 1 tệp)
• printf(…) là một trường hợp cụ thể tương đương với
fprintf(stdout, …)

• Tương tự ta có các cặp xuất dữ liệu:


– fputs(char*, FILE*) và puts(char*)
– fputc(char, FILE*) và putchar(char)
Nhập dữ liệu

• Quá trình nhập liệu tương ứng với việc quét dữ liệu
trên vùng đệm theo 1 đặc tả dữ liệu được chỉ ra
• Sau mỗi lần quét thành công con trỏ vùng đệm di
chuyển để có thể quét dữ liệu kế tiếp cho những
lần đọc sau
• Khi dữ liệu trong vùng đệm không còn, con trỏ
vùng đệm trỏ đến vị trí EOF.
– Để kiểm tra con trỏ có ở vị trí kết thúc hay không
sử dụng hàm: int feof(FILE*)
fscanf() và scanf()
• fscanf(<stream>, …) dùng để đọc dữ liệu hoạt
động trên một kênh nhập bất kì
• Hoạt động giống như scanf() dùng để đọc dữ liệu
trên kênh stdin
• Kết quả của fscanf() và scanf() là số phần tử dữ liệu
đọc được

• Tương tự ta có các cặp nhập dữ liệu:


– char* fgets(char*, int maxlen, FILE*) và
char*gets(char*)
– int fgetc(FILE*) và int getchar(void)
Khuôn dạng nhập
• Nhập số theo các khuôn dạng như %d, %l, %x,…, sẽ
được bỏ qua các kí tự trắng và  nếu gặp tại vị trí quét
• %s cho phép quét 1 xâu kí tự không bao gồm kí tự
trắng và . Nó cũng bỏ qua kí tự trắng và  nếu gặp
phải ở đầu xâu khi quét
• %c cho phép nhập một kí tự bất kì tại vị trí con trỏ (kể cả
kí tự trắng và )
• Ví dụ nếu “12 ab ” là dữ liệu trên kênh nhập
– "%d%s" quét được số 12 và xâu “ab”
– "%d%c%s" quét được số 12, kí tự trắng và xâu “ab”
– "%d %c%s" quét được số 12, kí tự ‘a’ và xâu “b”
– "%s%s" quét được xâu “12” và xâu “ab”
– "%d%s%c" quét được số 12, xâu “ab” và kí tự 
fflush()
• fflush(<stream>) : hàm dùng để làm sạch 1 vùng đệm
xuất hoặc nhập
• Khi 1 tệp được đóng, vùng đệm của nó sẽ được tự
động làm sạch

• Nên sử dụng hàm fflush() trước khi nhập 1 kí tự hoặc 1


xâu với gets() hay fgets()
• Giống như nhập kí tự, gets() không bỏ qua bất cứ 1 kí
tự nào khi quét. Hàm này quét tất cả kí tự trắng và
dừng ở sau kí tự  gặp đầu tiên.
• Tuy nhiên kí tự  sẽ không nằm ở trong xâu đích.
Ví dụ

Input.c
#include <stdio.h> C:\>input 
Nhap so: 12 
void main() Nhap xau: ab 
{ So 12, xau ab
int a;
char s[20];
printf(“Nhap so: ”);
scanf(“%d”, &a);
%d chỉ lấy hai kí tự
fflush(stdin); ’12’ để chuyển thành
printf(“Nhap xau: “); số, còn dư kí tự 
gets(s); được làm sạch bằng
fflush() trước khi nhập
printf(“So %d, xau %s”, a, s); xâu bằng gets()
}
Đếm số từ của một tệp

#include <stdio.h>

int main()
{
Mở tệp để đọc
int dem = 0;
char s[80];
FILE * f = fopen(“vanban.txt”, “r”);
if (f == NULL)
{
perror(“Loi mo tep vanban.txt\n”);
return 1;
} Mỗi lần chỉ
while (!feof(f)) đọc 1 từ
dem += fscanf(f, “%s”, s);
fclose(f);
printf(“Tong so tu: %d”, dem);
return 0;
}
fgetc() và fputc()
VD:Chương trình chép nội dung 2 file

FILE *input, *output;


input = fopen( "tmp.c", "r" );
output = fopen( "tmpCopy.c", "w+" );

ch = fgetc( input );
while( ch != EOF ) {
fputc( ch, output );
ch = fgetc( input );
}

fclose(input);
fclose(output);
fgets()

#include <stdio.h>
#define LINE_LENGTH 80

main()
{
FILE* fp;
char line[LINE_LENGTH];
int count=0;
fp=fopen("input.txt","r");
while ( fgets(line, LINE_LENGTH, fp) != NULL)
count++;
printf("File contains %d lines.\n", count);
fclose(fp);
}
Nội Dung Bài Học

VÀO RA TỆP
➢ Khái niệm tệp
➢ Khái niệm kênh nhập xuất
➢ Nhập xuất với tệp văn bản
➢ Nhập xuất với tệp nhị phân

30
Tệp văn bản vs. tệp nhị phân
Tệp văn bản Tệp nhị phân

Nội dung file là các ký tự điều khiển và Dãy các bit 0 và 1 -> con người
chữ cái (văn bản) con người có thể đọc không đọc được
được

Có thể được đánh dấu kết thúc bằng 1 Không có ký tự đặc biệt đánh dấu
kí tự điều khiển (kí tự mã 26 ) kết thúc file
Các ký tự được lưu trữ 1 ký tự trong 1 Số nguyên 1245 lưu trữ trong 2 byte
byte.
Ví dụ: số nguyên 1245 chiếm 2 bytes
trong bộ nhớ nhưng sẽ chiếm 5 bytes
trong file văn bản.
Muốn mở ở chế độ văn bản ta chỉ cần kí tự ‘b’ nếu mở ở dạng nhị phân
thêm kí tự ‘t’ vào cho chế độ mở ("r+b", "wb", “a+b",...)
("r+t", "wt",...)
Truy nhập tệp nhị phân
Đọc dữ liệu
int fread(void * Địa_Chỉ_Đích, int Kích_thước,
int số_phần_tử, FILE *fptr)
– Đọc từ file xác đinh bởi biến fptr một khối dữ liệu kích thước
Số_Phần_Tử x Kích_Thước vào vùng nhớ xác định bởi Địa_Chỉ_Đích
– Nếu đọc thành công: Trả về số phần tử đọc được
– Nếu không thành công: Trả về giá trị 0
• Ví dụ:
int Buf[100];
FILE * fptr = fopen(“so.dat”,”rb”);
fread( Buf, sizeof(int), 100, fptr);
Ví dụ 2:
int a[10]; f=fopen("songuyen.dat", "r+b");
fread(a, 10, sizeof(int), f);
32
Truy nhập tệp nhị phân
Ghi dữ liệu
int fwrite(void * Đ_Chỉ_Nguồn, int Kích_thước,
int số_phần_tử, FILE *fptr)
– Ghi từ vùng nhớ xác định bởi Địa_Chỉ_nguồn một khối dữ liệu có kích
thước Số_Phần_Tử x Kích_Thước ra file được xác đinh bởi biến fptr
– Nếu ghi thành công: Trả về số phần tử đã ghi
– Nếu không thành công: Trả về giá trị 0
• Ví dụ:
int Buf[100];
FILE * fptr = fopen(“so.dat”,”wb”)
fwrite(Buf, sizeof(int), 100, fptr);

33
Truy nhập tệp nhị phân
Dịch chuyển con trỏ file
int fseek(FILE *fptr, long int N, int Vị_Trí_Đầu)
– Dịch chuyển con trỏ file của file fptr đi một khoảng N so
với Vị_Trí_Đầu
• SEEK_SET / 0: Vị trí đầu là đầu tệp
• SEEK_CUR / 1: Vị trí đầu là vị trí con trỏ file hiện thời
• SEEK_END / 2: Vị trí đầu là cuối tệp
– void rewind(FILE *fptr): Đưa con trỏ về đầu tệp
Kiểm tra kết thúc file
int feof(FILE *fptr) Chú ý khi dùng, có thể lỗi
– Trả về 0 nếu con trỏ file vẫn còn trỏ tới một phần tử dữ
liệu, 1 nếu con trỏ file đang trỏ tới EOF

34
Ví dụ 1
Tạo file Songuyen.dat ghi 100 số lẻ đầu tiên.
#include <stdio.h>
int main() {
FILE * f = fopen("SoNguyen.Dat", "wb");
int i, n;
for(i = 0; i <100; i++) {
n = 2*i+1;
fwrite(&n, sizeof(int), 1, f);
}
fclose(f);
return 0;
}

35
Ví dụ 2
Đọc file Songuyen.dat, đưa ra màn hình các số
lẻ từ vị trí số thứ 50 của file
#include <stdio.h>
int main() {
FILE * f = fopen("SoNguyen.Dat", "rb");
int n;
fseek(f, 50*sizeof(int), SEEK_SET);
while(!feof(f)){
fread(&n, sizeof(int), 1, f);
printf("%4d", n);
}
fclose(f);
return 0;
}

36
Ví dụ 3
• Nhập danh sách từ bàn phím các thí sinh dự thi, mỗi thí sinh gồm họ
tên, số báo danh, khoa dự thi và điểm thi. Dữ liệu nhập được ghi vào file
ThiSinh.dat. Kết thúc nhập khi gặp một thí sinh có tên là « *** »
• Đọc từ file ThiSinh.Dat, đưa ra màn hình danh sách các thí sinh thi vào
ngành CNTT có điểm thi lớn hớn 21 theo quy cách
STT Số Báo Danh Ho Tên Điểm Thi
• Từ file ThiSinh.Dat, tạo file CNTT.Dat chỉ chứa danh sách các thí inh thi
vào khoa CNTT
• Nhập vào một số báo danh, tìm trong file ThiSinh.Dat và in ra họ tên,
điểm thi và khoa đăng ký của thí sinh nếu tìm thấy. Nếu không tìm thấy
thí sinh thì đưa ra thông báo « không tìm thấy »

37
Ví dụ 3
#include <stdio.h>
#include <string.h>
typedef struct {
char Ten[30];
long SBD;
char Khoa[10];
float Diem;
} SinhVien;
int main() {
FILE *f1,*f2;
SinhVien SV;
int i, SBD;
//Nhap thong tin cho file ThiSinh.Dat
//Tao file CNTT.Dat
return 0;
}

38
Ví dụ 3
//Nhap thong tin cho file ThiSinh.Dat
f1 = fopen("ThiSinh.Dat", "wb");
i = 1;
do {
printf("Thi sinh %d :\n", i);
printf(" Ho Ten : "); fflush(stdin); fgets(SV.Ten, sizeof(SV.Ten), stdin);
SV.Ten[strlen(SV.Ten) – 1] = '\0'; // Bỏ ký tự xuống dòng
if(strcmp(SV.Ten,"***") == 0) break;
printf(" So Bao Danh: "); scanf("%d", &SV.SBD);
printf(" Khoa : "); fflush(stdin); fgets(SV.Khoa, sizeof(SV.Khoa), stdin);
SV.Khoa[strlen(SV.Khoa) – 1] = '\0'; // Bỏ ký tự xuống dòng
printf(" Diem : "); scanf("%f", &SV.Diem);
fwrite(&SV, sizeof(SinhVien), 1, f1);
i++;
} while(1);
fclose(f1);

39
Ví dụ 3
printf("\n\n DANH SACH BAN DAU \n");
f1 = fopen("ThiSinh.Dat", "rb");
i = 0;
while (fread(&SV, sizeof(SinhVien), 1, f1) > 0)
printf("%-3d %-5d %-20s %-20s %-5.1f\n", ++i, SV.SBD,
SV.Ten, SV.Khoa, SV.Diem);
printf("\n\n Thi Sinh thi CNTT tren 21.0\n");
i = 0;
rewind(f1);

while (fread(&SV, sizeof(SinhVien), 1, f1) > 0)


if (strcmp(SV.Khoa, "CNTT") == 0 && SV.Diem > 21.0)
printf("%-3d %-5d %-20s %-5.1f\n", ++i, SV.SBD,
SV.Ten, SV.Diem);

40
Ví dụ 3
printf("\n\n Tao file CNTT.Dat\n");
i = 0;
rewind(f1);
f2 = fopen("CNTT.Dat", "wb");
while (fread(&SV, sizeof(SinhVien), 1, f1) > 0)
if (strcmp(SV.Khoa, "CNTT") == 0 )
fwrite(&SV, sizeof(SinhVien), 1, f2);
fclose(f2);
f2 = fopen("CNTT.Dat", "rb"); //doc lai file
while (fread(&SV, sizeof(SinhVien), 1, f2) > 0)
printf("%-3d %-5d %-20s %-5.1f\n", ++i, SV.SBD, SV.Ten,
SV.Diem);
fclose(f2);

41
Ví dụ 3
printf("\n\nTim Sinh Vien\n");
printf(" So Bao Danh "); scanf("%d", &SBD);
rewind(f1);
while (fread(&SV, sizeof(SinhVien), 1, f1))
if (SV.SBD == SBD){
printf("Tim thay sinh vien %s", SV.Ten);
break;
}

if (feof(f1)) printf("Khong thay");


fclose(f1);

42
Bài tập
1.Viết chương trình tạo ra tệp văn bản F3 từ việc ghép nội dung hai
tệp văn bản F1 và F2.

2.Viết một chương trình cho phép cắt hết chú thích của một chương
trình C được lưu trữ trong một tệp. Tên tệp chương trình được
nhập vào từ bàn phím. Giả thiết rằng chương trình không có lỗi cú
pháp.

3.Giả thiết một tệp dữ liệu thu thập về thời tiết trong một năm có
định dạng theo mỗi dòng là:
<ngày>/<tháng> <nhiệt độ thấp nhất>-<nhiệt độ cao nhất> <độ ẩm>
1/1 11-17 70
2/1 12-17 75

Hãy viết chương trình đọc dữ liệu của tệp này và in ra nhiệt độ trung
bình của các tháng trong năm, tháng khô hanh nhất và tháng ẩm
ướt nhất.

You might also like