You are on page 1of 86

Ngôn Ngữ Lập Trình C

Tống Ngọc Quyền


10/2023

Tống Ngọc Quyền 1


Ngôn ngữ lập trình phân làm 2 loại

Scripting language: Compiling language:

• Ví dụ: họ hàng shell script, Tcl, Python,… • Ví dụ: C, C++, C#, Java,…
• Có thể chạy trực tiếp mà không cần biên • Cần phải biên dịch để có thể thực thi chương
dịch. trình.
• Cần có môi trường để chạy (ví dụ Python • Quá trình biên dịch sẽ dịch mã nguồn gốc về
shell, Tcl shell,… hợp ngữ (Assembly) để có thể chạy được
trên máy.

Tống Ngọc Quyền 2


Agenda

• Phần 1: Làm quen với ngôn ngữ lập trình C.

• Phần 2: Cấu trúc rẽ nhánh – Câu lệnh điều kiện if..else ; switch..case.

• Phần 3: Vòng lặp (for, while, do..while).

• Phần 4: Mảng và chuỗi.

• Phần 5: Hàm.

Lưu ý: Bỏ qua nội dung “Con trỏ” (Pointer), “Cấu trúc” (Struct), thao tác tệp (File), hướng đối tượng (C++).
Tống Ngọc Quyền 3
Phần 1: Làm quen với ngôn ngữ lập trình C
• Cấu trúc chương trình C cơ bản
• Kiểu dữ liệu
• Toán tử
• Các hàm nhập xuất dữ liệu

Tống Ngọc Quyền 4


1/ Cấu trúc chương trình C cơ bản
Chương trình chính – main() #include <stdio.h>
int main() {
printf("Hello lớp học thầy Quyền!!!");
• Hàm đặc biệt, bắt buộc, là điểm khởi đầu khi thực thi
return 0;
chương trình. Thường trả về kiểu dữ liệu là int. }

#include <stdio.h>
• Giá trị trả về (trong ví dụ là 0) được xem là mã thoát
void main() {
(EXIT CODE) của chương trình. HĐH sử dụng để xác printf("Hello lớp học thầy Quyền!!!");
}
định chương trình kết thúc thành công hoặc có lỗi.

• Trường hợp không có kiểu dữ liệu trả về (void), mặc


định sẽ trả về 0.

Tống Ngọc Quyền 5


1/ Cấu trúc chương trình C cơ bản
Tiền xử lý (preprocessing): #include #define
Preprocessing được xử lý trước tạo thành code hoàn chỉnh trước khi biên dịch.
→ Không tốn bộ nhớ trên RAM như biến hoặc hằng (kiểu const) để lưu trữ giá trị, còn hàm giả thì
tùy.

• #include: Được dùng để chèn thêm code từ một file vào source code hiện tại.
→Thường được dùng để khai báo thư viện, hoặc nạp thêm các code thành phần vào code chính.
#include <stdio.h>
#include "controlMotors.h"

• #define: Được dùng để định nghĩa macro, biểu thị một hằng số hoặc một hàm giả.
→ Giúp code trở nên tường minh hơn mà không sử dụng các biến, hàm gây tốn bộ nhớ trên RAM.
– Định nghĩa hằng số: #define PI 3.1415926
– Định nghĩa hàm giả: #define SQUARE(x) ((x) * (x))

Tống Ngọc Quyền 6


1/ Cấu trúc chương trình C cơ bản
Biến (Variable) và Hằng (Constant)
• Biến
– Mỗi biến là một vùng nhớ lưu trữ dữ liệu trên RAM → Tùy kiểu dữ liệu mà vùng nhớ rộng/hẹp khác.
– Giá trị có thể thay đổi trong chương trình.
– Khai báo trước khi sử dụng.
– Lưu trữ loại dữ liệu động, cần thay đổi trong suốt quá trình chạy chương trình.
int born_year = 2005;
• Hằng: khai báo bởi #define hoặc const
– Sử dụng #define → Giá trị của hằng thay thế trực tiếp vào source code trước khi biên dịch → Không
tốn bộ nhớ RAM để lưu trữ.
– Hằng khai báo bởi const được lưu trữ trên RAM.
– Giá trị của hằng không thể thay đổi trong chương trình.
– Việc sử dụng hằng giúp source code dễ đọc, giảm nguy cơ thay đổi không mong muốn.
const int maximum_PWM_value = 255;

Tống Ngọc Quyền 7


1/ Cấu trúc chương trình C cơ bản
Lệnh (Statement) và khối lệnh (Block of statements)
• Lệnh:
– Đơn vị cơ bản, thực hiện một hành động cụ thể.
– Kết thúc bằng dấu chấm phẩy ‘;’
int a = 5; // lệnh khai báo biến
printf("Hello, World!"); // lệnh in ra màn hình
• Khối lệnh:
– Nhóm các lệnh lại với nhau để có thể thực thi như một đơn vị.
– Bao quanh bởi dấu ngoặc nhọn ’{’ và ‘}’.
– Khối lệnh có thể rỗng (không chứa lệnh).
if (a > 10) {
printf("a is greater than 10");
a = a - 5;
} // Hai lệnh printf và gán a nằm trong khối lệnh của câu lệnh if

Tống Ngọc Quyền 8


1/ Cấu trúc chương trình C cơ bản
Chú thích (comment) → QUAN TRỌNG
//+===================+//
• Giúp người khác dễ đọc dễ hiểu, và cho
// Ở đây tôi chú thích 1 hàng
chính bản thân tác giả khi đọc lại code của /* Ở đây tôi chú thích thoải mái, kể cả
mình. xuống hàng cho đến khi gặp 2 ký tự này
--> */

• Chú thích không phải một phần của chương #include <stdio.h>
trình, và không được biên dịch. void main() {
// In ra màn hình
// Biên dịch là quá trình dịch từ ngôn ngữ
printf("Hello lớp học thầy Quyền!!!");
bậc cao sang ngôn ngữ máy để máy tính hiểu
}
được và thực thi.

Tống Ngọc Quyền 9


2/ Kiểu dữ liệu
Các kiểu dữ liệu
Phạm vi không dấu Phạm vi có dấu
STT Kiểu Byte
(unsigned) (signed)

1 Boolean bool 1 false (0), true (số khác)


2 char (Ký tự) 1 0 … 255 -128 … 127
3 Nguyên short 2 0 … 65535 -32768 … 32768
4 (Z) int 4 0 … 4294967295 -2147483648 … 2147483647
5 long 8 0 … 18.446 x 1018 -9.223 x 1018 … 9.223 x 1018
6 float (-) 4 ∄ -3.402 x 1038 … -1.175 x 10-38
7 Thực float (+) 4 ∄ 1.175 x 10-38 … 3.402 x 1038
8 (R) double (-) 8 ∄ -1.797 x 10308 … -2.225 x 10-308
9 double (+) 8 ∄ 2.225 x 10-308 … 1.797 x 10308

Tống Ngọc Quyền 10


2/ Kiểu dữ liệu
Ép kiểu dữ liệu (Type casting)
• Dùng để chuyển đổi kiểu dữ liệu, áp dụng cho cả biến và giá trị.
• Ví dụ:
int i = (int) 4.6; // i = 4
float j = (float) i; // j = 4.0
long k = (long) i * 100000000000; // k = 400000000000

• Một số mục đích chính:


– Tương thích kiểu dữ liệu: Ví dụ hàm max(int a, int b) đầu vào kiểu int → Cần đưa vào hàm giá trị int.

– Tránh tràn số (overflow): Tránh việc giá trị của phép tính vượt quá khả năng biểu diễn của biến.

– Tránh thiếu số (underflow): Một giá trị rất gần, tiệm cận 0, vượt quá giới hạn khả năng của float sẽ bị
làm tròn thành 0 → Cần ép về double để lưu giá trị này.

– Tối ưu bộ nhớ/tốc độ tính toán: RAM có hạn, tính số nhỏ thì dễ hơn số lớn.

Tống Ngọc Quyền 11


3/ Toán tử
Toán tử số học (Arithmetic), Toán tử gán và Toán tử tiếp tục (Increment/Decrement)

• Kết quả trả về giá trị số học như tính toán số STT Toán tử Tác dụng
học thông thường.
1 + Cộng
int a = 3 + 2; // a = 5
float b = 1.0 / 2; // b = 0.5 2 - Trừ
int c = 2; // c = 2 Số
3 * Nhân
học
int d = a % c; // d = 1
4 / Chia
int e = a / c; // e = 2
float f = b / a; // f = 0.1 5 % Chia lấy dư

6 Gán +=, -=,… Tác động trực tiếp biến


f *= 2; // f = 0.1 * 2 = 0.2
a++; // a = 5 + 1 = 6 7 Tăng ++ Tăng biến lên 1 đơn vị
a--; // a = 6 - 1 = 5 8 Giảm -- Giảm biến đi 1 đơn vị

Tống Ngọc Quyền 12


3/ Toán tử
Toán tử so sánh (Relational operators) và Toán tử luận lý (Logical operators)

• Kết quả trả về giá trị Boolean là true/false. STT Toán tử Tác dụng

• Thường dùng làm điều kiện cho vòng lặp 1 == Bằng


hoặc câu lệnh rẽ nhánh. 2 != Không bằng
int a = 5;
3 So < Nhỏ hơn
a > 4 // true
4 sánh > Lớn hơn
a != 5 // false
a != 5 == 0 // true 5 <= Nhỏ hơn hoặc bằng
true && false // = false 6 >= Lớn hơn hoặc bằng
true || false // = true
7 && (AND) Tất cả đều đúng → đúng
a > 4 && true // = true
Luận
!true // = false 8 || (OR) Ít nhất 1 ý đúng → đúng

!yêu // = không yêu 9 ! (NOT) Phủ định/Đảo ngược

Tống Ngọc Quyền 13


3/ Toán tử
Toán tử ba ngôi/toán tử điều kiện (Ternary operator)

• Cú pháp: (Điều kiện) ? <Giá trị nếu đúng> : <Giá trị nếu sai>

– Nếu đúng: Toán tử trả giá trị đầu.

– Nếu sai: Toán tử trả giá trị sau.

• Thường dùng để đơn giản hóa một phép gán giá trị thay cho câu lệnh if..else.
int max = (a > b) ? a : b; // Nếu a lớn hơn thì max được gán bằng a, ngược lại thì b

Tống Ngọc Quyền 14


3/ Toán tử
Toán tử bit (Bitwise operators)

• Dùng để thao tác trực tiếp với các bit của giá
STT Biểu diễn Tác dụng
trị số. → Tăng tốc độ tính toán và xử lý với
các thanh ghi, ngoại vi nhiều lần. 1 & (AND)

2 | (OR) Kết quả trả về chuỗi kết quả


0001 | 1100; // = 1101 của toán tử bit giữa các bit
1001 | 1100; // = 1101 3 ^ (XOR) tương ứng của 2 số đầu vào.
0001 & 1100; // = 0000
1001 & 1100; // = 1000
4 ~ (NOT)
PORTD = 00001111;
// Đưa 2 bit đầu của PORTD lên 1 và đưa 2 bit
cuối về 0, các giá trị khác giữ nguyên.
5 << (Dịch trái)
Dịch tất cả các bit một lượng
PORTD = PORTD | 11000000 & 11111100 ; // nhất định.
PORTD = 11001100 6 >> (Dịch phải)

Tống Ngọc Quyền 15


3/ Toán tử
Toán tử con trỏ (Pointer operators)

• Lấy địa chỉ của biến ở trên bộ nhớ, bằng toán tử &
int b;

scanf ("%d", &b); // Đưa số vừa nhập vào đến địa chỉ ô nhớ của b

• Lấy/Tác động đến giá trị tại ô nhớ mà “con trỏ” trỏ đến, bằng toán tử *
int *a ; // Khai báo con trỏ a (địa chỉ ô nhớ, không phải biến)

*a = 1; // ---> Ô nhớ tại địa chỉ a được gán giá trị 1

Tống Ngọc Quyền 16


3/ Toán tử
Thứ tự thực hiện (ưu tiên)

• Tương tự toán học thông thường, phép tính có độ ưu tiên trước sau:
– Trong dấu ngoặc tròn ()

– Toán tử số học nhân chia * /

– Toán tử số học + -

• Đặc biệt, với toán tử tiếp tục, thứ tự phép tính phụ thuộc vị trí của toán tử, ví dụ:
int a = 10;
// b được gán bằng a trước rồi tăng giá trị biến a sau
int b = a++; // b = a = 10; a = a + 1 = 11;
// a được tăng giá trị trước rồi c mới được gán bằng a
int c = ++a; // a = a + 1 = 12; c = a = 12;

Tống Ngọc Quyền 17


4/ Nhập và xuất dữ liệu
Xuất dữ liệu (printf)
printf("C Programming Language");
• Cú pháp:
→ C Programming Language
printf("<Định dạng chuỗi>", <các giá trị
truyền vào>); printf("Ten toi la: %s", "QuyenTN");
→ Ten toi la: QuyenTN

• Hàm được khai báo trong thư viện stdio.h, printf("%s sinh nam %d", "QuyenTN", 2004);
do đó cần khai báo thư viện mới có thể sử → QuyenTN sinh nam 2004

dụng được.
#include <stdio.h>

Tống Ngọc Quyền 18


4/ Nhập và xuất dữ liệu
Nhập dữ liệu (scanf)
int a;
• Cú pháp:
scanf("%d", &a); // Nhập giá trị số nguyên cho a
scanf("<Định dạng chuỗi>", <địa chỉ các
char b;
biến>);
scanf("%c", &b); // Nhập ký tự cho b

• Dùng để nhập giá trị cho biến.

• Hàm được khai báo trong thư viện stdio.h,


do đó cần khai báo thư viện mới có thể sử
dụng được.
#include <stdio.h>

Tống Ngọc Quyền 19


4/ Nhập và xuất dữ liệu
Ký tự đặc biệt trong chuỗi (Escape sequence)
• Cần biểu thị chúng để tránh trùng lẫn với syntax của ngôn ngữ lập trình.
• Bắt đầu bằng ký tự ‘\’, ví dụ:
DEC Tên ký tự Ý nghĩa
\0 0 NULL Không có gì cả
\n 10 Line Feed (LF) Xuống dòng
\t 9 Horizontal Tab (HT) Tab
\r 13 Carrier Return (CR) Đưa con trỏ về đầu dòng
\b 8 Backspace Xóa
\’ 39 Single quote Dấu nháy đơn
\” 34 Double quotes Dấu nháy kép
\\ 92 Backslash Ký tự ‘\’ (ký tự ‘\’ đầu tiên đánh dấu ký tự đặc biệt → cần 2 ký tự)
\xHH (Mã HEX XX) Biểu diễn ký tự đặc biệt thông qua mã HEX của ký tự đó trong bảng mã ASCII

• Chú thích: Trong nhiều loại giao tiếp, thông điệp có dạng
“DATA\r\n”, trong đó \r\n chỉ dẫn kết thúc dòng/ thông điệp.
(Khi nhấn Enter thì máy tính sẽ tự chèn thêm ký tự đặc biệt phía cuối chuỗi)

Tống Ngọc Quyền 20


4/ Nhập và xuất dữ liệu
Định dạng chuỗi (Cơ bản)
Format Mô tả Ví dụ
%d Số nguyên (DEC) -279, -4, 1, 28, 48, 219,…

Số %u Số nguyên không dấu (DEC) 128, 92901,…


nguyên %x Số nguyên dưới dạng hệ thập lục phân (HEX) 2 (2), 5 (5), A (10), 3FF (1023),…
%o Số nguyên hệ bát phân (OCT) 0 (0), 27 (23), 77 (63),…
%f Số thực dạng float -123.82389, 192091.219
Số
%lf Số thực dạng double (chỉ riêng hàm scanf) -278136218182389
thực
%e Hiển thị dưới dạng số mũ 1.23456e+05 (123456.0)
%c 1 ký tự ~,!,@,#,$,%,^, a , 1, 9, 0,…
Ký tự
hoặc %s 1 chuỗi ~!@#$%^, 123456, qwerty,…
chuỗi
%% Ký tự ‘%’ %
Tống Ngọc Quyền 21
4/ Nhập và xuất dữ liệu
Định dạng chuỗi (Nâng cao)
Format Mô tả Ví dụ
Đặt nội dung trong 1 khung gồm ‘X’
%<X>s printf("%5s" , "TNQ"); ---> " TNQ"
ký tự và căn lề phải
String
frame %-<X>s Giống trên nhưng căn lề trái printf("%-5s" , "TNQ"); ---> "TNQ "

%s, %d, %f Không tạo frame printf("%s" , "TNQ"); ---> "TNQ"

%<X>d printf("%5d" , 1 ); ---> " 1"


Giống string frame
Int %-<X>d printf("%-5d" , 1 ); ---> "1 "
frame
%0<X>d Fill số 0 vào frame printf("%05d“ , 1 ); ---> "00001"

%.<X>f Lấy ‘X’ ký tự sau dấu chấm printf("%.3f" , 1.0 ); ---> "1.000"

Float Số thực có ‘X’ ký tự sau dấu chấm,


%<Y>.<X>f printf("%10.3f" , 1.0 ); ---> " 1.000"
frame đặt trong frame “Y” ký tự

%0<Y>.<X>f Giống trên nhưng fill số 0 printf("%010.3f", 1.0 ); ---> "000001.000"

Tống Ngọc Quyền 22


5/ Ví dụ
Tiền xử lý
#include <stdio.h>

#define PI 3.14159265

int main() {
printf("Gia tri cua PI la: %f\n", PI);
return 0;
}

➢Giá trị của PI là: 3.141593

? Vì sao giá trị của PI được in ra không phải là 3.14159265?

Tống Ngọc Quyền 23


5/ Ví dụ
Ép kiểu dữ liệu
#include <stdio.h>
int main() {
float a = 5.7;
int b;
b = (int) a;
printf("Gia tri sau khi ep kieu: %d\n", b);
return 0;
}

➢Gia tri sau khi ep kieu: 5

? Giá trị của biến a sau khi thực hiện chương trình trên là gì?

Tống Ngọc Quyền 24


5/ Ví dụ
Toán tử ba ngôi
#include <stdio.h>
int main() {
int a = 5, b = 7;
int max;
max = (a > b) ? a : b;
printf("Gia tri lon nhat la: %d\n", max);
return 0;
}

➢Gia tri lon nhat la: 7

? Giá trị của biến a và b sau khi thực hiện chương trình trên là gì?

Tống Ngọc Quyền 25


5/ Ví dụ
Nhập dữ liệu với scanf
#include <stdio.h>
int main() {
int age;
printf("Vui long nhap tuoi cua ban: ");
scanf("%d", &age);
printf("Ban %d tuoi.\n", age);
return 0;
}

➢Vui long nhap tuoi cua ban: 10


➢Ban 10 tuoi.

Tống Ngọc Quyền 26


5/ Ví dụ
Định dạng chuỗi
#include <stdio.h>
int main() {
char stringFormat[] = "%-5s%-25s%-10s\n";
printf(stringFormat, "STT", "Ho va ten", "MSSV");
printf(stringFormat, "1", "Nguyen Van A", "23110001");
printf(stringFormat, "2", "Vo Van Ngan", "23110002");
printf(stringFormat, "3", "Xa Lo Ha Noi", "None");
return 0;
}
➢ STT Ho va ten MSSV
➢1 Nguyen Van A 23110001
➢2 Vo Van Ngan 23110002
➢3 Xa Lo Ha Noi None

? Ý nghĩa của biến stringFormat bên trên?

Tống Ngọc Quyền 27


6/ Bài tập

1. Giải phương trình bậc nhất ax + b = 0. Với a và b được nhập từ bàn phím, in ra màn hình
nghiệm của phương trình.

2. Tìm số lớn nhất trong 5 số được nhập từ bàn phím.

3. In bảng cửu chương từ 1 đến 10, sử dụng format string ngay ngắn thẳng hàng.

4. Tính trung bình cộng của 5 số được nhập từ bàn phím.

Notes: AI có thể hoàn thành bài tập trên dễ dàng, nhưng bạn muốn là kẻ theo sau AI hay là người
không thể bị AI thay thế?
Tống Ngọc Quyền 28
7/ Giải bài tập
Giải phương trình bậc nhất ax + b = 0
#include <stdio.h>
int main() {
int a, b;
scanf("%d%d",&a,&b);
printf("x = %.2f", (float)(-b)/a);
return 0;
}

Tống Ngọc Quyền 29


7/ Giải bài tập
Tìm số lớn nhất trong 5 số được nhập từ bàn phím
#include <stdio.h>
int main() {
int a, b, c, d, e, max;
scanf("%d%d%d%d%d",&a,&b,&c,&d,&e);
max = a ;
max = (max > b) ? max : b ;
max = (max > c) ? max : c ;
max = (max > d) ? max : d ;
max = (max > e) ? max : e ;
printf("max = %d", max);
return 0;
}

Tống Ngọc Quyền 30


7/ Giải bài tập
In bảng cửu chương sử dụng String Format
char stringFormat[] = "%-15s%-15s%-15s%-15s%-15s\n";

printf(stringFormat, "1 x 1 = 1" , "2 x 1 = 2" , "3 x 1 = 3" , "4 x 1 = 4" , " 5 x 1 = 5" );
printf(stringFormat, "1 x 2 = 2" , "2 x 2 = 4" , "3 x 2 = 6" , "4 x 2 = 8" , " 5 x 2 = 10" );
printf(stringFormat, "1 x 3 = 3" , "2 x 3 = 6" , "3 x 3 = 9" , "4 x 3 = 12", " 5 x 3 = 15" );
printf(stringFormat, "1 x 4 = 4" , "2 x 4 = 8" , "3 x 4 = 12", "4 x 4 = 16", " 5 x 4 = 20" );
printf(stringFormat, "1 x 5 = 5" , "2 x 5 = 10", "3 x 5 = 15", "4 x 5 = 20", " 5 x 5 = 25" );
printf(stringFormat, "1 x 6 = 6" , "2 x 6 = 12", "3 x 6 = 18", "4 x 6 = 24", " 5 x 6 = 20" );
printf(stringFormat, "1 x 7 = 7" , "2 x 7 = 14", "3 x 7 = 21", "4 x 7 = 28", " 5 x 7 = 35" );
printf(stringFormat, "1 x 8 = 8" , "2 x 8 = 16", "3 x 8 = 24", "4 x 8 = 32", " 5 x 8 = 40" );
printf(stringFormat, "1 x 9 = 9" , "2 x 9 = 18", "3 x 9 = 27", "4 x 9 = 36", " 5 x 9 = 45" );
printf(stringFormat, "1 x 10 = 10", "2 x 10 = 20", "3 x 10 = 30", "4 x 10 = 40", " 5 x 10 = 50" );

printf("\n");

printf(stringFormat, "6 x 1 = 6 ", "7 x 1 = 7" , "8 x 1 = 8" , "9 x 1 = 9" , "10 x 1 = 10" );
printf(stringFormat, "6 x 2 = 12", "7 x 2 = 14", "8 x 2 = 16", "9 x 2 = 18", "10 x 2 = 20" );
printf(stringFormat, "6 x 3 = 18", "7 x 3 = 21", "8 x 3 = 24", "9 x 3 = 27", "10 x 3 = 30" );
printf(stringFormat, "6 x 4 = 24", "7 x 4 = 28", "8 x 4 = 32", "9 x 4 = 36", "10 x 4 = 40" );
printf(stringFormat, "6 x 5 = 30", "7 x 5 = 35", "8 x 5 = 40", "9 x 5 = 46", "10 x 5 = 50" );
printf(stringFormat, "6 x 6 = 36", "7 x 6 = 42", "8 x 6 = 48", "9 x 6 = 54", "10 x 6 = 60" );
printf(stringFormat, "6 x 7 = 42", "7 x 7 = 49", "8 x 7 = 56", "9 x 7 = 64", "10 x 7 = 70" );
printf(stringFormat, "6 x 8 = 48", "7 x 8 = 56", "8 x 8 = 64", "9 x 8 = 72", "10 x 8 = 80" );
printf(stringFormat, "6 x 9 = 54", "7 x 9 = 63", "8 x 9 = 72", "9 x 9 = 81", "10 x 9 = 90" );
printf(stringFormat, "6 x 10 = 60", "7 x 10 = 70", "8 x 10 = 80", "9 x 10 = 90", "10 x 10 = 100");

Tống Ngọc Quyền 31


7/ Giải bài tập
Tính trung bình cộng của 5 số được nhập từ bàn phím
#include <stdio.h>
int main() {
int a, b, c, d, e;
scanf("%d%d%d%d%d",&a,&b,&c,&d,&e);
printf("mean = %.2f", (float)(a+b+c+d+e)/5);
return 0;
}

Tống Ngọc Quyền 32


Phần 2: Cấu trúc rẽ nhánh
• Câu lệnh điều kiện if
• Câu lệnh điều kiện switch..case
• So sánh 2 cấu trúc rẽ nhánh trong cùng nhiệm vụ

Tống Ngọc Quyền 33


1/ Câu lệnh if
Dạng thiếu (simple if)

• Chỉ thực hiện khối lệnh nếu điều kiện đó đúng.


if (<điều kiện>) { Điều kiện?

// Khối lệnh thực thi khi điều kiện đúng


Đúng
}

• Ví dụ:
Công việc 1
int number = 5;

if (number > 0) {
Sai
printf("Number is positive.\n");

Tống Ngọc Quyền 34


1/ Câu lệnh if
Dạng đủ (if..else)

• Điều kiện đúng thực hiện khối lệnh 1, sai thực hiện khối
lệnh 2. Điều kiện?

if (<điều kiện>) {
// Khối lệnh thực thi khi điều kiện đúng Đúng
Sai
} else {
// Khối lệnh thực thi khi điều kiện sai
}
Công việc 1 Công việc 2
• Ví dụ:
int number = 5;
if (number > 0) {
printf("Number is positive.\n");
} else {
printf("Number is not positive.\n");
}
Tống Ngọc Quyền 35
2/ Câu lệnh switch..case
Cấu trúc

• So sánh bằng của biểu thức thay vì kiểm tra tính đúng biểu thức == giá trị 1? Đúng Khối lệnh 1

sai của điều kiện.


switch(biểu thức) { Sai break? Đúng

case value1:
Sai
// Khối lệnh cho giá trị value1
break; biểu thức == giá trị 2? Đúng Khối lệnh 2

case value2:
// Khối lệnh cho giá trị value2
Sai break? Đúng
break;
...
default: Khối lệnh 3 Sai

// Khối lệnh khi không giá trị nào khớp


}

Tống Ngọc Quyền 36


2/ Câu lệnh switch..case
Đặc điểm
• “biểu thức” có thể là bất kỳ giá trị nào switch(biểu thức) {
(bool/int/char/float): biến, hằng, chuỗi, hàm có case value1:
trả về, toán tử (ngoại trừ toán tử điều kiện),.. // Khối lệnh cho giá trị value1
break;
• Khi khớp “case”, các khối lệnh sẽ được thực case value2:
hiện, cấu trúc switch..case chỉ kết thúc khi
gặp break. // Khối lệnh cho giá trị value2
break;

• Phần “default” có thể có hoặc không, được ...


thực thi khi không có “case” nào khớp (giống default:
else) bên câu lệnh if. // Khối lệnh khi không giá trị
nào khớp
}

Tống Ngọc Quyền 37


2/ Câu lệnh switch..case
Ví dụ
char grade = 'B';
• Trong trường hợp này, biểu thức là biến “grade”. switch(grade) {
case 'A':
• Biểu thức này khớp với case 'B', tuy nhiên khối lệnh này printf("Excellent!");
break;
rỗng và không có break. Do vậy, khối lệnh ở case 'C' case 'B':
case 'C':
được thực hiện. → In ra dòng chữ "Well done" và kết
printf("Well done");
thúc. break;
case 'D':
printf("Passed");
break;
→ Ý nghĩa: Nếu nhiều case cùng thực hiện 1 công việc có case 'F':
printf("Better try again");
thể không dùng break. Thường dùng trong ứng dụng xe dò break;
default:
line, giúp tinh giản lượng code. printf("Invalid grade");
}

Tống Ngọc Quyền 38


3/ if..else và switch..case trong giải quyết vấn đề
So sánh bằng – “if thiếu” vs “switch..case không có default”
Ví dụ: Kiểm tra ngày trong tuần
#include <stdio.h> #include <stdio.h>
int main() { int main() {
int day = 5; int day = 5;
if (day == 1) switch(day) {
printf("Monday\n"); case 1: printf("Monday\n"); break;
if (day == 2) case 2: printf("Tuesday\n"); break;
printf("Tuesday\n"); case 3: printf("Wednesday\n"); break;
if (day == 3) }
printf("Wednesday\n"); return 0;
return 0; }
}

Tống Ngọc Quyền 39


3/ if..else và switch..case trong giải quyết vấn đề
So sánh bằng – “if..else” vs “switch..case có default”
Ví dụ: Kiểm tra loại màu
#include <stdio.h> #include <stdio.h>
#include <stdbool.h>
int main() { int main() {
int color = 3; int color = 3;
if (color == 1) switch(color) {
printf("Red\n"); case 1: printf("Red\n"); break;
else if (color == 2) case 2: printf("Green\n"); break;
printf("Green\n"); case 3: printf("Blue\n"); break;
else if (color == 3) default: printf("Unknown\n"); break;
printf("Blue\n"); }
else return 0;
printf("Unknown\n"); }
return 0;
}

Tống Ngọc Quyền 40


3/ if..else và switch..case trong giải quyết vấn đề
So sánh không bằng – “if thiếu” vs “switch..case không có default”
Ví dụ: Kiểm tra lớp có nhiều bạn chăm học không
#include <stdio.h> #include <stdio.h>
#include <stdbool.h>
int main() { int main() {
int cham_hoc = 11; int cham_hoc = 11;
if (cham_hoc < 10) { switch ( (bool) (cham_hoc < 10) ) {
printf("Bad"); // Toán tử điều kiện cần ép kiểu về boolean
/* Dù chỉ có 1 lệnh, nhưng nên đặt trong case true:
khối lệnh để tránh lỗi và dễ sửa sau này */ printf("Bad");
} break;
return 0; }
} //printf ("\n%d\n", (bool) cham_hoc < 10);
return 0;
}

Tống Ngọc Quyền 41


3/ if..else và switch..case trong giải quyết vấn đề
So sánh không bằng – “if..else” vs “switch..case có default”
Ví dụ: Kiểm tra lớp có nhiều bạn chăm học không
#include <stdio.h> #include <stdio.h>
#include <stdbool.h>
int main() { int main() {
int cham_hoc = 11; int cham_hoc = 11;
if (cham_hoc < 10) { switch ((bool) (cham_hoc < 10)) {
printf("Bad"); case true:
/* Dù chỉ có 1 lệnh, nhưng nên đặt trong printf("Bad");
khối lệnh để tránh lỗi và dễ sửa sau này */ break;
} else { default:
printf("Good"); printf("Good");
} break;
return 0; }
} //printf ("\n%d\n", (bool) cham_hoc < 10);
return 0;
}

Tống Ngọc Quyền 42


4/ Ví dụ
Kiểm tra số chẵn / lẻ
#include <stdio.h>
int main() {
int number;
scanf("%d", &number); //Nhap mot so nguyen
if (number % 2 == 0) { printf("%d la so chan.\n", number); }
else { printf("%d la so le.\n", number); }
return 0;
}

➢2005
➢2005 la so le.

? Viết lại chương trình trên sử dụng switch..case?

Tống Ngọc Quyền 43


4/ Ví dụ
Xác định mùa dựa theo tháng
#include <stdio.h> case 7: case 8: case 9:
int main() { printf("Mua thu.\n") ; break;
int month; case 10: case 11: case 12:
scanf("%d", &month); // Nhap thang 1-12 printf("Mua dong.\n"); break;
switch(month) { default:
case 1: case 2: case 3: printf("Khong hop le!\n");
printf("Mua xuan.\n"); break; }
case 4: case 5: case 6: return 0;
printf("Mua he.\n") ; break; }

➢2
➢Mua xuan.

? Ưu điểm của switch..case?

Tống Ngọc Quyền 44


5/ Bài tập

1. Nhập lương gộp (lương trên hợp đồng – gloss salary) (đơn vị: đồng, KHÔNG phải “triệu
đồng”). Tính bảo hiểm, thu nhập tính thuế, thuế thu nhập cá nhân, lương ròng (thực nhận –
net salary), biết:
– Lương ròng = Lương gộp – Bảo hiểm – Thuế TNCN.
– Bảo hiểm bắt buộc (BHXH 8% + BHYT 1.5% + BHTN 1%) = 10.5% lương gộp.
– Các khoản giảm trừ miễn thuế:
– Miễn trừ gia cảnh bản thân: 11 triệu/tháng (tương đương 132 triệu/năm) (Lương này thuế TNCN = 0 đó!).
– Mỗi người phụ thuộc là 4.4 triệu/tháng (Trong bài 1 thì cho là không có người phụ thuộc).
– Các khoản đóng bảo hiểm, đóng góp từ thiện (cho là không làm). TNPCT (triệu VND) Thuế suất (%)

– Thu nhập phải chịu thuế = Lương gộp – Các khoản miễn thuế. 0+ đến 5 5
5+ đến 10 10
– Thuế thu nhập cá nhân (đơn giản) = TNPCT x Thuế suất.
10+ đến 18 15
2. Giống bài 1, nhưng xét 10 năm tới, lương tăng 20%/năm. 18+ đến 32 20
Biết cả cha và mẹ sau 3 năm trở thành người phụ thuộc. 32+ đến 52 25
52+ đến 80 30
80+ 35

Tống Ngọc Quyền 45


6/ Giải bài tập

#include <stdio.h>
int main(){
float lr, lg, bh, t, bhbb, tncn, tnpct;
printf("Luong Gop (VND): ");
scanf("%f", &lg); if (lg < 0) return 0;
bhbb = lg * 0.105;
tnpct = lg - bhbb - 11000000;
if (tnpct < 5000000) {tncn = tnpct * 0.05;}
else if (tnpct < 10000000) {tncn = tnpct * 0.10;}
else if (tnpct < 18000000) {tncn = tnpct * 0.15;}
else if (tnpct < 32000000) {tncn = tnpct * 0.20;}
else if (tnpct < 52000000) {tncn = tnpct * 0.25;}
else if (tnpct < 80000000) {tncn = tnpct * 0.30;}
else {tncn = tnpct * 0.35;}
lr = lg - bhbb - tncn;
printf("Thu nhap chiu thue: %d\n", (int)tnpct );
printf("Bao hiem: %d\n", (int)bhbb );
printf("Thue TNCN: %d\n", (int)tncn );
printf("Luong rong: %d\n", (int)lr );
return 0;
}

Tống Ngọc Quyền 46


Phần 3: Vòng lặp
• Vòng lặp for
• Vòng lặp while / do..while
• Vòng lặp vô hạn
• break và continue trong vòng lặp

Tống Ngọc Quyền 47


1/ Vòng lặp?

• Chức năng: Lặp lại công việc nhiều lần.

• Kết thúc khi không thỏa mãn điều kiện, hoặc gặp lệnh break.
Điều kiện Sai
• Phân loại:
– Vòng lặp for. Đúng
– Vòng lặp while / do..while.
– Vòng lặp vô hạn.
Khối lệnh

Tống Ngọc Quyền 48


2/ Vòng lặp for

• Thường sử dụng khi biết trước số lần lặp. Khởi tạo

• Có 3 thành phần (như lưu đồ):


for (<khởi tạo>; <điều kiện>; <cập nhật>) {
Điều kiện Sai
// Khối lệnh
} Đúng

• Ví dụ lặp lại công việc in ra màn hình 5 lần:


Khối lệnh
int i;
for (i = 0; i < 5 ; i++) {
printf("Ends when i reach 5, now i = %d", i); Cập nhật
}
→ Các giá trị của i để vòng lặp xảy ra: 0, 1, 2, 3, 4.

Tống Ngọc Quyền 49


3/ Vòng lặp while

• Vòng lặp thực hiện kiểm tra điều kiện trước, mới thực hiện khối
lệnh nếu đúng.
Điều kiện Sai
• Cú pháp:
while (<điều kiện>) {
Đúng
// Khối lệnh
}
• Ví dụ: Tìm số lớn nhất chia hết cho 7 trong khoảng 0..100. Khối lệnh

int i = 0, k = 0;;
while (i <= 100) { // i là biến đếm
// k là số chia hết cho 7 lớn nhất
if (i % 7 == 0) {k = i;} i++;
}

Tống Ngọc Quyền 50


4/ Vòng lặp do..while

• Tương tự vòng lặp while, nhưng làm trước rồi hỏi sau.
do {
// Khối lệnh
} while (<điều kiện>); Khối lệnh
Đúng
• Thường dùng trong trường hợp cần có input trước.

• Ví dụ: Nhập 1 số nguyên dương, nếu sai nhập lại.


do {
scanf("Nhap i: %d", &i); Điều kiện
// Nếu i là 0 hoặc nguyên âm thì lặp lại
} while {i <= 0};
Sai

Tống Ngọc Quyền 51


5/ Vòng lặp vô hạn (Inifinite loop)
Đặc điểm

• Điều kiện vòng lặp luôn đúng.

• Chỉ dừng lại khi có một sự kiện ngoại lệ (lệnh break, hoặc tác động
Khối lệnh
khác (thường là ngắt – interrupt) ).
Sai
while (true) {
// Khối lệnh
}
break?
• “true” có thể thay thế bởi các toán tử điều kiện, hoặc các giá trị số Tác động khác?
tương đương với true (như 1, 2, -2,…).

• Thông thường, vòng lặp vô hạn là trường hợp không mong muốn xảy
Đúng
ra, do lỗi lập trình, cần tránh.

Tống Ngọc Quyền 52


5/ Vòng lặp vô hạn (Inifinite loop)
Vòng lặp vô hạn có chủ đích

• Các máy tính đa dụng (General-purpose computer) luôn


được lập trình thực hiện một vòng lặp vô hạn
(Vi điểu khiển là máy tính đa dụng)

• Các sự kiện có thể diễn ra trong vòng lặp vô hạn:


– Chờ sự kiện ngoại vi: nút nhấn, dữ liệu cảm biến,…
Hàm loop trong Arduino IDE là một vòng lặp
– Tiêu thụ tài nguyên: tràn bộ nhớ, CPU,… vô hạn

– Giải quyết các công việc trong vòng lặp.

– Khóa/treo chương trình: thường khi gặp lỗi không mong muốn.

– Duy trì trạng thái hoạt động của thiết bị.

Tống Ngọc Quyền 53


5/ Vòng lặp vô hạn (Inifinite loop)
Vị trí hàm loop() trong Arduino
#include <Arduino.h>
• Trong Arduino IDE, ta không thấy hàm main(void) mà chỉ
int main(void)
thấy hàm setup() và loop(). {
init();
• Để hàm loop() được thực hiện lặp lại vô hạn, chúng được
#if defined(USBCON)
đặt trong vòng lặp for (;;), có nghĩa không có điều kiện USBDevice.attach();
#endif
dừng.
setup();
• Các sự kiện diễn ra (ngắt, ngoại vi,…) xảy ra trong hàm for (;;) {
loop();
serialEventRun().
if (serialEventRun)
serialEventRun();
}
return 0;
}

Tống Ngọc Quyền 54


6/ break và continue trong vòng lặp

• break
– Kết thúc vòng lặp ngay lập tức.

– Thường dùng khi đạt được mục đích của lệnh Các câu lệnh Các câu lệnh

vòng lặp.
break? Đúng continue? Sai

• continue
Sai Đúng

– Bỏ qua phần còn lại của vòng lặp và chuyển Các câu lệnh Các câu lệnh

đến lượt lặp tiếp theo ngay lập tức.

– Thường dùng khi thực hiện các vòng lặp phía


sau là vô nghĩa trong vòng lặp hiện tại. Lưu đồ của break (trái) và continue (phải)

Tống Ngọc Quyền 55


7/ Ví dụ
Nhập số dương
#include <stdio.h>
int main() {
int number;
do {
printf("Nhap mot so duong: ");
scanf("%d", &number);
} while (number < 0);
printf("Ban da nhap so: %d\n", number);
return 0;
}

➢2005
➢Ban da nhap so: 2005

? Viết lại ví dụ 1 sử dụng vòng lặp while thay vì do..while?

Tống Ngọc Quyền 56


7/ Ví dụ
Xe điều khiển bằng Bluetooth (Lập trình Arduino Nano)
#include <SoftwareSerial.h>
SoftwareSerial BTSerial(2, 3); // RX, TX pins
BTSerial.begin(9600); // Bắt đầu giao tiếp Serial
void loop() {
if (BTSerial.available()) {
char command = BTSerial.read();
switch (command) {
case 'F': forward() ; break; // Điều khiển xe tiến
case 'B': backward(); break; // Điều khiển xe lùi
case 'L': t_left() ; break; // Điều khiển xe rẽ trái
case 'R': t_right() ; break; // Điều khiển xe rẽ phải
}
}
}

Tống Ngọc Quyền 57


7/ Ví dụ (nâng cao)
Xe điều khiển bằng Bluetooth (Lập trình Arduino Nano)
#include <avr/interrupt.h>
void setup() {}
ISR(USART_RX_vect){ // Ngắt UART RX
switch(UDR0){ // Thanh ghi chứa byte received UART
case 0: PORTD = PORTD & B00001111 ; break ;
case 1: PORTD = PORTD & B01011111 | B01010000 ; break ;
case 2: PORTD = PORTD & B10011111 | B10010000 ; break ;
case 3: PORTD = PORTD & B10101111 | B10100000 ; break ;
case 4: PORTD = PORTD & B01101111 | B01100000 ; break ;
}
}
void loop() {}

? Hoạt động của đoạn code trên diễn ra như nào?

Tống Ngọc Quyền 58


8/ Bài tập (cơ bản)
Thông hiểu

1. In bảng cửu chương từ 1 đến X, với X nhập từ bàn phím. Sử dụng format string ngay ngắn
thẳng hàng.

2. Nhập vào số tự nhiên X, điều kiện 1 < X < 9999999, nếu sai nhập lại (gợi ý: dùng do..while).
Tìm các ước của X. Nếu X chỉ có 2 ước, in ra dòng chữ: “X la so nguyen to!”

3. In ra dãy Fibonaci gồm X con số, với X nhập từ bàn phím.

4. Nhập số tự nhiên X. Phân tích X thành thừa số nguyên tố.

5. Nhập 2 số tự nhiên X và Y. Tìm ước chung lớn nhất (ƯCLN) theo phương pháp bài 4.

6. Nhập 3 số tự nhiên X, Y và Z. Tìm bội chung nhỏ nhất (BCNN) theo phương pháp bài 4.

7. Nhập 2 số tự nhiên X và Y. Kiểm tra chúng có phải 2 số nguyên tố cùng nhau không.

Tống Ngọc Quyền 59


8/ Bài tập (nâng cao)
Vẽ 2 hình sau, với số N nhập từ bàn phím
Nhap N = 10 (số chẵn) Nhap N = 11 (số lẻ)

* *
* * * *
* * * *
* * * *
* * * *
* * * * * * * *
* * * * * *
* * * * * *
* * * * * *
* * * * * * * * * * * * * *
* * * * * * * * * * *

Tống Ngọc Quyền 60


9/ Giải bài tập (Cơ bản)
In bảng cửu chương
#include <stdio.h>
int main(){
char stringFormat[] = "%2d x %2d = %-3d ";
int i, j;
for (i = 1 ; i <= 10; i++) {
for (j = 1; j <= 5; j++) printf(stringFormat, j, i, j*i);
printf("\n");
}
printf("\n");
for (i = 1 ; i <= 10; i++) {
for (j = 6; j <= 10; j++) printf(stringFormat, j, i, j*i);
printf("\n");
}
}

Tống Ngọc Quyền 61


9/ Giải bài tập (Cơ bản)
In ra dãy Fibonaci gồm X con số
#include <stdio.h>
int main(){
int x; do {scanf("%d", &x);} while (x <= 0);
int num1 = 0, num2 = 1, num3, i;
printf("%d\t", num2);
for (i = 2; i <= x; i++) {
num3 = num2 + num1; num1 = num2; num2 = num3;
printf("%d\t", num3);
}
}

Tống Ngọc Quyền 62


9/ Giải bài tập (Cơ bản)
Tìm các ước của số tự nhiên X
#include <stdio.h>
int main(){
int x; do {scanf("%d", &x);} while (x <= 0 || x > 9999999);
int i, num = 0;
printf("Cac uoc cua %d la:\n", x);
for (i = 1; i <= x; i++) {
if (x % i == 0) {
printf ("%d,\t", i);
num++;
}
}
if (num == 2) {printf("\n%d la so nguyen to!", x);}
}

Tống Ngọc Quyền 63


9/ Giải bài tập (Nâng cao)
Vẽ hình với N chẵn
0 x
Ta có ba đỉnh A, B, C có tọa độ: * A(9; 0)
A (9 ; 0) = (N-1 ; 0) * *
B (0 ; 9) = (0 ; N-1) * *
C (18; 9) = (2*N - 2 ; N-1)
* *
* *
𝐴𝐵 = 9 ∗ (−1 ; 1) −1 ; 1 𝑙à 𝑣𝑒𝑐𝑡𝑜𝑟 𝑐ℎỉ 𝑝ℎươ𝑛𝑔 𝑐ủ𝑎 𝑑1
Ta có các vector sau: ൝ ⇒ ቊ * *
𝐴𝐶 = 9 ∗ ( 1 ; 1) 1 ; 1 𝑙à 𝑣𝑒𝑐𝑡𝑜𝑟 𝑐ℎỉ 𝑝ℎươ𝑛𝑔 𝑐ủ𝑎 𝑑2
* *
Phương trình tham số của d1 (đi qua điểm A):
* *
𝑥−(𝑁−1) 𝑦 −0
= ⇒ 𝑑1: 𝑦 = −𝑥 + 𝑁 − 1 * * C(18; 9)
−1 1
d3
Phương trình tham số của d2 (đi qua điểm A): * * * * * * * * * *
B(0; 9)
𝑥−(𝑁−1) 𝑦 −0
= ⇒ 𝑑2: 𝑦 = 𝑥 + 𝑁 − 1
1 1

Phương trình đường thẳng d3:


y
d3: 𝑦 = 𝑁 − 1

Tống Ngọc Quyền 64


9/ Giải bài tập (Nâng cao)
Vẽ hình với N chẵn
#include <stdio.h> 0 j
* A(9; 0)
int main() {
* *
while (1) {
* *
int n; do {scanf("%d", &n);} while (n%2 != 0 || n <= 0);
* *
int i, j; // bien dem
* *
for (i = 0; i < n; i++) {
* *
for (j = 0; j < 2*n-1; j++) {
if (j==i+n-1 || j==-i+n-1 || (i==n-1 && j%2==0) ) {
* *
printf("*");
* *
} else {printf(" ");} d3
* * C(18; 9)
} * * * * * * * * * *
B(0; 9)
printf("\n"); // Xuong dong
} i
} d3 chỉ đánh dấu * ở cột chẵn

Tống Ngọc Quyền 65


Không vẽ
vào đây

9/ Giải bài tập (Nâng cao) x=(N-1)/2 x=3*(N-1)/2

Vẽ hình với N lẻ
0 A(N-1; 0) j
*
−1 ; 1 𝑙à 𝑣𝑒𝑐𝑡𝑜𝑟 𝑐ℎỉ 𝑝ℎươ𝑛𝑔 𝑐ủ𝑎 𝑑1 𝑣à 𝑑4 * *
Ta có ቊ vì d1//d4 và d2//d5
1 ; 1 𝑙à 𝑣𝑒𝑐𝑡𝑜𝑟 𝑐ℎỉ 𝑝ℎươ𝑛𝑔 𝑐ủ𝑎 𝑑2 𝑣à 𝑑5 * *
* *
Tương tự với bài trước, ta có các phương trình đường thẳng:
𝑑1: 𝑦 = −𝑥+𝑁−1 d6
* *
𝑑2: 𝑦 = 𝑥+𝑁−1
y=(N-1)/2
* * * * * *
d3: 𝑦 = 𝑁−1
d4: 𝑦 = −𝑥+2𝑁−2
* * * *
d5: 𝑦 = 𝑥 * * * *
𝑁−1
d6: 𝑦 = 2 * * * *
Các giới hạn không được vẽ cho đường d4 và d5: d3
* * * *
𝑁−1 3 𝑁−1 * * * * * * * * *C(2*N-2;
B(0; N-1)
* * N-1)
𝑥< ℎ𝑜ặ𝑐 𝑥 > D(N-1; N-1)
2 2 i
ቐ 𝑁−1
𝑦< 2
Đường d6 đánh dấu * có thể đánh ở cả cột chẵn và lẻ, phụ thuộc giá trị của (N-1)/2 là chẵn hay lẻ

Tống Ngọc Quyền 66


Không vẽ
vào đây

9/ Giải bài tập (Nâng cao) x=(N-1)/2 x=3*(N-1)/2

Vẽ hình
#include <stdio.h> 0 A(N-1; 0) j
*
int main() {
while (1) {
* *
int n; do {scanf("%d", &n);} while (n%2 != 1 || n <= 0); * *
int in = 0; int half_odd = ((n-1)/2)%2 ; * *
d6 chỉ đánh dấu * ở cột chẵn hay
int i, j; // bien dem
lẻ phụ thuộc vào giá trị (N-1)/2 d6
* *
for (i = 0; i < n; i++) {
y=(N-1)/2
* * * * * *
for (j = 0; j < 2*n-1; j++) {
* * * *
if (j == i+n-1 || j == -i+n-1 || (i == n-1 && j%2 == 0) ) {in = 1;}
if (i == j || i == -j +2*n - 2 || (i == (n-1)/2 && j%2 == half_odd) ) {in = 1;}
* * * *
if ( (j < (n-1)/2 || j > (3*n-3)/2) && i <= (n-1)/2 ) {in = 0;} * * * *
if (in) {printf("*");} else {printf(" ");}
d3
* * * *
in = 0; * * * * * * * * *C(2*N-2;
B(0; N-1)
* * N-1)
} D(N-1; N-1)
printf("\n"); // Xuong dong
i
}
}
}

Tống Ngọc Quyền 67


Phần 4: Mảng và chuỗi
• Khái niệm và tính chất của mảng
• Truy cập và Gán giá trị cho mảng
• Các hàm xử lý chuỗi

Tống Ngọc Quyền 68


1/ Mảng (Array)
Khái niệm – Tính chất – Khai báo

• Tập hợp các biến có cùng kiểu dữ liệu.

• Xếp ở các ô nhớ kề nhau trên RAM.

• Cú pháp:
<kiểu dữ liệu> <tên mảng>[số phần tử];

int Arr[5];

• Nếu không khởi tạo, ban đầu giá trị ngẫu nhiên. int Arr[5]; // {12632347; 128739247;...} (Random)

int Arr[5] = {}; // {0, 0, 0, 0, 0}


Nếu khởi tạo, các phần tử chưa đặt mặc định là 0.
int Arr[5] = {1, 3, 5}; // {1, 3, 5, 0, 0}
• Tên mảng có ý nghĩa như địa chỉ của phần tử đầu.

• Phần tử đầu tiên của mảng là phần tử 0.

Tống Ngọc Quyền 69


1/ Mảng (Array)
Truy cập và Gán giá trị

• Truy cập phần tử trong mảng: <tên mảng>[chỉ số]

• Gán giá trị cho phần tử: <tên mảng>[chỉ số] = <giá trị>;

• Nhập và xuất các giá trị trong mảng:


int i, a[5];

for (i=0; i<5; i++) {scanf("%d", &a[i]);}

// for (i=0; i<5; i++) {scanf("%d", (a+i));}

printf("Các phần tử trong mảng:\n");

for (i=0; i<5; i++) {printf("%d\t", a[i]);}

Tống Ngọc Quyền 70


1/ Mảng (Array)
Mảng 2 chiều
• Cấu trúc dữ liệu gồm hàng và cột, xếp thành lưới.
• Cú pháp:
<kiểu dữ liệu> <tên mảng>[số hàng] [số cột];
int x[3][4];
• Truy cập và gán giá trị: Tương tự mảng 1 chiều.
for (int i = 0; i < row_size; i++)
for (int j = 0; j < column_size; j++)
scanf("%d", &a[i][j]);
for (int i = 0; i < row_size; i++) {
Các phần tử trong mảng 2 chiều
for (int j = 0; j < column_size; j++)
printf("%d\t", a[i][j]);
printf("\n");
}

Tống Ngọc Quyền 71


2/ Chuỗi (String)
Khái niệm – Nhập và xuất giá trị

• Chuỗi là mảng 1 chiều kiểu char, kết thúc bằng giá trị NULL (ký tự \0).

• Khai báo (giống mảng):

char <tên chuỗi>[số ký tự];

• Hàm nhập dữ liệu (dành riêng):

gets(<tên chuỗi>);

• Hàm xuất dữ liệu (dành riêng):

puts(<tên chuỗi>);

Tống Ngọc Quyền 72


2/ Chuỗi (String)
Các hàm xử lý chuỗi
• Cần khai báo thư viện string.h
#include <string.h>
• Lấy độ dài chuỗi (không bao gồm NULL):
strlen(<tên chuỗi>);
• Hàm sao chép chuỗi
char nguon[] = "Hello", dich[20];
strcpy(dich, nguon); // dich sẽ giữ giá trị "Hello"
• Hàm nối chuỗi
char dich[20] = "Hello", them[] = ", World!";
strcat(dich, them); // dich sẽ giữ giá trị "Hello, World!"
• Hàm so sánh chuỗi: Trả về 0 nếu bằng, âm nếu nhỏ hơn, dương nếu lớn hơn.
strcmp(<chuỗi 1>, <chuỗi 2>);

Tống Ngọc Quyền 73


3/ Ví dụ

There is no example here!

? There is no question here!

Tống Ngọc Quyền 74


4/ Bài tập

1. Tìm số lớn nhất trong mảng, với N phần tử nhập từ bàn phím.

2. Sắp xếp thứ tự tăng dần mảng N phần tử.

3. Sắp xếp chẵn qua trái mảng, lẻ qua phải mảng.

4. Kiểm tra chuỗi chuẩn. Điều kiện:


▪ Chuỗi chuẩn là chuỗi không có khoảng trắng thừa ở đầu chuỗi, cuối chuỗi.

▪ Giữa các từ chỉ có đúng 1 khoảng trắng.


5. Nhập họ và tên (chấp nhận cả các chuỗi không chuẩn), in ra họ, tên và đệm.

Tống Ngọc Quyền 75


Phần 5: Hàm
• Định nghĩa và khai báo hàm
• Phạm vi của biến
• Cấu trúc của bộ nhớ chương trình
• Đệ quy

Tống Ngọc Quyền 76


1/ Định nghĩa và khai báo hàm
Định nghĩa

• Một khối mã thực hiện một công việc cụ thể. • Hàm 𝑦 = 𝑓 𝑥 = 𝑥 2 + 2𝑥 + 3


float y (float x) {
• Chia chương trình thành các phần nhỏ. return (x*x + 2*x +3);
}
• Tại sao sử dụng hàm?
– Tăng sự tổ chức. • Hàm in dãy Fibonaci (bài tập phần 3):
void inFibonaci(int x){
– Tái sử dụng. int num1 = 0, num2 = 1, num3, i;
printf("%d\t", num2);
– Dễ sửa lỗi. for (i = 2; i <= x; i++) {
num3 = num2 + num1; num1 = num2; num2 = num3;
• Thông thường, nếu tên hàm viết như này để printf("%d\t", num3);
}
dễ đọc: }

inFibonaci(), emAnhQuyen(),…

Tống Ngọc Quyền 77


1/ Định nghĩa và khai báo hàm
Khai báo

• Cú pháp:
returnType functionName(parameter1Type parameter1, parameter2Type parameter2, ...) {

// Các dòng code thực hiện công việc của hàm

• Trong đó:
– returnType : kiểu dữ liệu trả về cho hàm.
Trong trường hợp không có dữ liệu trả về, ở đây đặt là void

– parameter1Type : kiểu dữ liệu cho tham số đầu vào. Các tham số cách nhau bởi dấu phẩy.
Trong trường hợp không có tham số đầu vào, ở đây bỏ trống, hoặc đặt là void

Tống Ngọc Quyền 78


2/ Phạm vi biến
Biến cục bộ (Local variable) và biến toàn cục (Global variable)

• Biến cục bộ: Chỉ có hiệu lực trong phạm vi của hàm.

• Biến toàn cục: Có hiệu lực trong toàn bộ chương trình.


#include <stdio.h>
int globalVariable = 10; // Biến toàn cục
int main() {
int localVariable = 5; // Biến cục bộ
printf("Biến toàn cục: %d\n", globalVariable);
printf("Biến cục bộ: %d\n", localVariable);
globalVariable = 20; // Thay đổi giá trị của biến toàn cục
printf("Biến toàn cục sau khi thay đổi: %d\n", globalVariable);
return 0;
}

Tống Ngọc Quyền 79


2/ Phạm vi biến
Truyền tham chiếu và truyền tham trị
#include <stdio.h>
• Truyền tham chiếu (Pass by reference)
// Truyền tham trị
– Địa chỉ của biến được truyền vào hàm. void passByValue(int a) {a = 10;}
// Truyền tham chiếu
→ Cho phép hàm thay đổi giá trị của biến đó
void passByReference(int *b) {*b = 10;}
(kể cả các biến cục bộ của hàm khác) trực tiếp
int main() {
trên bộ nhớ RAM.
int x = 5;
– Thường thực hiện bằng cách sử dụng con trỏ. int y = 5;
passByValue(x);
• Truyền tham trị (Pass by value) printf("Sau truyền tham trị: %d\n", x);
passByReference(&y);
– Truyền bản sao của giá trị biến.
printf("Sau khi truyền tham chiếu: %d\n", y);
→ Bất kỳ thay đổi nào đối với tham số trong return 0;
hàm không ảnh hưởng đến giá trị của biến gốc }

bên ngoài hàm.

Tống Ngọc Quyền 80


3/ Cấu trúc bộ nhớ chương trình

• Program instructions: Lưu mã máy chương trình.


• Data Segment: Lưu các biến toàn cục, dữ liệu tĩnh
được khởi tạo.
• Heap:
– Vùng nhớ động được cấp phát khi ứng dụng chạy.
– Lưu các đối tượng, mảng, hay dữ liệu cấp phát động.
– Thường thực hiện bằng các hàm malloc, free,…
• Stack:
– Lưu các biến cục bộ và thông tin về hàm gọi.
– Khi gọi hàm, các biến cục bộ và địa chỉ trả về được
lưu trong stack. Cấu trúc dữ liệu này hoạt động theo
nguyên tắc "last in, first out" (LIFO).

Tống Ngọc Quyền 81


4/ Đệ quy

• Là một hàm gọi lại chính nó. // Hàm tính giai thừa đệ quy
int giaiThua(int n) {
• Thường giải quyết các bài toán có cấu trúc đệ quy. // Điều kiện dừng
• Lưu ý khi sử dụng đệ quy: if (n == 0 || n == 1) {
return 1;
– Dùng bộ nhớ: Nếu đệ quy quá sâu → tràn bộ nhớ → Crash!
(hiện tượng này gọi là Stack Overflow). } else {
// Gọi đệ quy
– Điều kiện dừng hợp lý để tránh đệ quy vô hạn
return n * giaiThua(n-1);
– Hiệu suất: }
▪ Độ phức tạp thuật toán cao. }

▪ Máy tính cần sử dụng nhiều thao tác cho mỗi vòng đệ quy.
▪ Thời gian chạy lâu.

→ Hạn chế sử dụng nhất có thể.

Tống Ngọc Quyền 82


5/ Ví dụ

#include <stdio.h>

// Hàm tính giai thừa đệ quy


int giaiThua(int n) {
if (n == 0 || n == 1) {return 1;} // Điều kiện dừng
else {return n * giaiThua(n-1);} // Gọi đệ quy
}
// Chương trình chính
int main(){
int n; do {scanf("%d", &n);} while (n < 0);
printf("%d! = %d", n, giaiThua(n) );
}

? Theo lời giảng, khi kết thúc hàm, tại vị trí đó, xuất hiện giá trị trả về (cũng như biến).
? Như vậy %d thứ hai trong hàm printf() để biểu thị giá trị trả về của hàm giaiThua?

Tống Ngọc Quyền 83


5/ Ví dụ
Tự tạo thư viện để vẽ hình tam giác.
// draw.c đặt cùng thư mục main.c // main.c
void draw_even (int n) { #include <stdio.h>
int i, j; #include "draw.c"
for (i = 0; i < n; i++) { /* Khi khai báo thư viện thì:
for (j = 0; j < 2*n-1; j++) {
- Dấu <> chỉ thư viện được cài đặt sẵn.
if (j==i+n-1 || j==-i+n-1 || (i==n-1 && j%2==0) ) {printf("*");
- Dấu "" chỉ đường dẫn thư viện mình muốn.
} else {printf(" ");}
*/
} printf("\n");
int main() {
} }
void draw_odd (int n) { while (1) {

int i, j, in = 0, int half_odd = ((n-1)/2)%2 ; int n;


for (i = 0; i < n; i++) { do {scanf("%d", &n);} while (n <= 0);
for (j = 0; j < 2*n-1; j++) { // n chẵn gọi hàm draw_even()
if (j == i+n-1 || j == -i+n-1 || (i == n-1 && j%2 == 0) ) {in = 1;} if (n%2 == 0) {draw_even(n);}
if (i == j || i == -j +2*n - 2 || (i == (n-1)/2 && j%2 == half_odd) ) // n lẻ gọi hàm draw_odd()
{in = 1;}
if (n%2 == 1) {draw_odd(n) ;}
if ( (j < (n-1)/2 || j > (3*n-3)/2) && i <= (n-1)/2 ) {in = 0;}
}
if (in) {printf("*");} else {printf(" ");} in = 0;
} printf("\n"); return 0;

} } }

? Liên kết với nội dung phần 1 để hiểu #include sẽ gom code thư viện vào code chính.

Tống Ngọc Quyền 84


6/ Bài tập

1. Viết khai báo hàm strlen() để tính độ dài của chuỗi. Chấp nhận ký tự đặc biệt ngoài NULL.

2. Lưu code khai báo hàm strlen() thành file “string.c.<your name>”. Lưu ý file này chỉ chứa khai
báo hàm trên, không include và không chứa hàm main().

3. Tạo 1 code mới trong cùng thư mục với file code trên, thêm dòng #include “<tên file trên>”.
Sau đó gọi hàm strlen() để sử dụng.

4. Khai báo hàm “control” không có kiểu dữ liệu trả về, có 1 tham số đầu vào “signal”. Hàm thực
hiện yêu cầu sau:
▪ Nếu biến signal là 0, in ra màn hình “Stop”.
▪ Nếu biến signal là 1, in ra màn hình “Forward”.
▪ Nếu biến signal là 2, in ra màn hình “Turn right”.
▪ Nếu biến signal là 3, in ra màn hình “Backward”.
▪ Nếu biến signal là 4, in ra màn hình “Turn left”.

Tống Ngọc Quyền 85


Thank You

Tống Ngọc Quyền 86

You might also like