4. #include <stdio.h>
main() {
int a;
printf("Input a numbern");
scanf("%d", &a);
printf("%dn", a);
char c;
printf("Input a charactern");
scanf(" %c", &c);
printf("%cn", c);
return 0;
}
Nhập vào một số
và một ký tự từ
dòng lệnh sau đó
in ra
5. Sửa lỗi scanf nhận newline trước đó
scanf("%c", &c);//Nếu phía trước
người dùng gõ enter, lệnh scanf
đọc vào ký tự newline rồi không
đọc ký tự cần đọc nữa
scanf(" %c", &c);//tạo khoảng
trống tạm để bỏ qua ký tự new line
trước đó
6. scanf chỉ đọc vào đến ký tự space
char str[128];
scanf("%s", str);
printf("%sn", str);
Input: hello world
Output: hello
Input: hello world
Output: hello world
Hy vọng Thực tế
8. Dùng fgets đọc vào một dòng
#define BUFF_SIZE 128
char str[BUFF_SIZE];
printf("Please input string:n");
fgets(str, BUFF_SIZE, stdin);
printf("%sn", str);
9. Vòng lặp nhập lựa chọn người dùng
int main() {
printf("Enter your choicen");
printf("N. Create new student recordn");
printf("S. Search student by namen");
printf("E. Edit student recordn");
printf("Q. Quitn");
// Xem trang tiếp theo
}
10. char c;
while (1) {
scanf(" %c",&c);
c = toupper(c);
switch (c) {
case 'N':
printf("You select Nn");
break;
case 'S':
printf("You select Sn");
break;
case 'E':
printf("You select En");
break;
case 'Q':
printf("You select Q to quitn");
return 1;
}
11. Enter your choice
N. Create new student record
S. Search student by name
E. Edit student record
Q. Quit
N
You select N
S
You select S
E
You select E
13. Cần ôn lại đại số bool
https://www.tutorialspoint.com/cprogramming/c_operators.htm
14. Các toán tử
• && logical AND
• || logical OR
• ! logical NOT
• !(A && B) = !A || !B
• !(A || B) = !A && !B
15. Nếu ưu tiên tốc độ
if (conditionA) {
} else if (conditionB) {
} else if (conditionC) {
} else {
}
Điều kiện có xác suất xảy
ra lớn nhất lên đầu,
Điều kiện có xác suất
thấp nhất cuối cùng
Nếu không đánh giá được
xác suất xảy ra thì ưu
tiên condition đơn giản
lên đầu
16. Bài tập
• Từ dãy số nguyên dương 0 đến 20,
hay dựng 3 mảng:
– Mảng A chứa các số chia hết cho 5
– Mảng B chứa các số chia hết cho 3
– Mảng C chứa các số chia hết cho 2
17. Nếu ưu tiên bảo trì, dễ hiểu
if (age < 5) {
} else if (age < 12) {
} else if (age < 20) {
} else if (age < 30) {
}
if (age < 20 && age >= 12) {
} else if (age < 12 && age >=5) {
} else if (age < 30 && age >=20)
{
} else if (age < 5) {
}
Nên Không nên
18. Giảm thiểu if lồng nhau quá 2 lớp
if lồng nhau 3 lớp trở lên cực khó
debug, thường xảy thiếu trường
hợp.
19. if (A) {
if (B) {
if (C) {
} else {
//A đúng, B đúng, C sai
}
} else {
//A đúng, B sai
if (C) {
//A đúng, B sai, C đúng
}
}
} else {
//A sai
}
Phát rồ luôn
20. Lý thuyết tổ hợp
• 3 điều kiện {A,B,C} thực ra có
{A,!A,B,!B,C,!C}
• Tìm ra tất cả tổ hợp có thể
{A,B,C}, {!A,B,C}, {A,!B,C},
{!A,!B,C}, {A,!B,!C},
{!A,!B,!C}...
21. Nếu không liệt kê tất cả các khả năng
Chi phí để gỡ rối, sửa bug sẽ rất lớn
22. if (a) {
doA();
if (b) {
doB();
if (c) {
doC();
} else {
return "No C for you!";
}
} else {
return "No B for you!";
}
} else {
return "No A for you!";
}
Code khó hiểu,
khó bảo trì
23. if (!a) {
return "No A for you!";
}
doA();
if (!b) {
return "No B for you!";
}
doB();
if (!c) {
return "No C for you!";
}
doC();
Các lệnh if phẳng
hơn, ít lồng nhau
dễ bảo trì
24. Code nhanh nhất là không làm gì cả
• Hãy để tất cả logic kiểm tra lên
đầu ~ sanity check
• Thoát khỏi hàm hoặc throw
exception càng nhanh càng tốt
26. switch vs if then else
• switch dùng khi điều kiện chỉ ứng
với các khả năng rời rạc, đếm
được ~ discret values
• if then else điều kiện có thể rời
rạc hoặc liên tục
27. typedef enum {
RED,
GREEN,
BLUE
} colors;
colors c;
switch (c) {
case RED:
//...
break;
case GREEN:
//...
break;
default:
//Luôn phải có nhánh mặc định default
break;
}
Điều kiện trong switch
phải có giá trị rời rạc,
nguyên kiểu integer hoặc
enum
28. Tối ưu lệnh switch
• Cần bảo trì: xếp các case theo
thứ tự tự nhiên dễ nhớ
• Cần tốc độ: xếp các case xác suất
xảy ra cao hơn lên đầu
29. switch và if then else cái nào nhanh hơn?
• Số khả năng < 5 dùng if then else
• 5 <= số khả năng <= 10 dùng switch
• Trên 10 nên lookup table hoặc hash
list để đảm bảo thời gian truy cập
phần tử bất kỳ là như nhau
https://stackoverflow.com/questions/767821/is-else-if-faster-than-switch-case
http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx
30. Mã hoá chuỗi
Ký tự vào Ký tự ra
a m
b q
c r
d s
e x
0 A
1 Q
2 T
other *
Hãy viết hàm mã hoá ứng với mỗi ký tự vào
sẽ có 1 ký tự ra. Nếu không tìm được ký tự
phù hợp thì xuất ra *
Ví dụ ‘abc23’ sẽ thành ‘mqrT*’
32. for () { }
• 1 biến đếm rời rạc
• Lệnh khởi tạo ban đầu
• Điều kiện dừng vòng lặp
• Lệnh tác động lên biến đếm: tăng
– giảm
• Chú ý không nhất thiết tăng hay
giảm 1 đơn vị
33. break - continue
• break //thoát khỏi vòng lặp ngay
• continue //bỏ qua phần còn lại,
chạy luôn lần lặp tiếp theo
34. Vòng lặp for lồng nhau
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
for (int k = 0; k < P; k++) {
//Do some thing M * N * P times
}
}
}
Chuyển đoạn lệnh tính toán không
phụ thuộc biến đếm vòng lặp ra
ngoài
35. Chỉ để trong vòng lặp for những đoạn code sử dụng biến
đếm, bị ảnh hưởng bởi biến đếm
for (int i = 0; i < 10000; i++)
{
float pi = computePi();
printf("%dn", i);
}
float pi = computePi();
for (int i = 0; i < 10000; i++)
{
printf("%dn", i);
}
Gây chậm
Tốt hơn
36. Không thay đổi biến đếm trong thân vòng lặp
for (int i = 0; i < 10; i++) {
i = i * 2; //Dangerous code, i is mutable
printf("%dn", i);
}
for (int i = 0; i < 10; i++) {
int j = i * 2; //i is immutable inside loop
printf("%dn", j);
}
Nguy hiểm
An toàn
37. while vs do while
do {
//lặp trước, kiểm tra sau
} while(condition);
while(condition) {
//Kiểm tra trước, lặp sau
};
38. int condition = 0;
do {
printf("Hello"); //In 1 lần Hello
} while (condition);
Khuyến cáo không nên dùng do while, về sau
gỡ rối rất mệt
39. for
• Dùng khi có biến
đếm và nhìn ra số
lần lặp
• Không được sửa biến
đếm trong thân vòng
lặp
while
• Dùng khi nhìn ra
điều kiện để lặp
• Nếu có biến đếm thì
có thể sửa biến đếm
trong thân vòng lặp
41. Mục đích của hàm
• Chia nhỏ độ phức tạp ~ Divide conquer
• Mỗi hàm một chức năng mà tên hàm thể
hiện rõ ~ Single responsibility
• Tăng khả năng tái sử dụng ~
Reusability
• Kiểm thử được ~ Testable
• Ít phụ thuộc đến thành phần khác ~
loosely coupling
43. Sử dụng hàm main
• main() là hàm chính quản lý toàn bộ
vòng đời của ứng dụng
• Chỉ viết logic điều phối chung
trong main
• Không viết logic tác vụ cụ thể
trong main
• Tuyệt đối không gọi đệ quy hàm main
44. int main(int N) {
int result = 1;
if (N > 1) {
return N * main(N-1);
}
return result;
}
Viết đệ quy hàm main. Một cách viết cẩu thả. C cho
phép nhưng C++ tuyệt đối cấm
45. #include <stdio.h>
int main() {
int result = 1;
int N;
printf("Nhap vao so N tinh giai thuan");
scanf(" %d", &N);
for (int i = 1; i < N; i++) {
result *= result * i;
}
printf("%d", result);
return 0;
}
Cách viết này phải
đọc toàn bộ code
mới hiểu
Đây là code điển hình của lập trình viên mới vào nghề
46. long long giaiThua(int N) {
if (N < 2) return 1;
long long result = 1;
for (int i = 2; i <= N; i++) {
result = result * i;
}
return result;
}
int main() {
int N;
printf("Nhap vao so N tinh giai thuan");
scanf(" %ld", &N);
printf("%lld", giaiThua(N));
return 0;
}
Tên hàm mô tả
rõ luôn mục
đích
47. long long giaiThua(int N) {
if (N < 2) return 1;
long long result = 1;
for (int i = 2; i <= N; i++) {
result = result * i;
}
return result;
}
Trong hàm không có lệnh nhập xuất cụ thể bởi,
việc tính giai thừa chỉ quan tâm nhập vào số
nguyên rồi trả về kết quả
Cohesion – Loose coupling
48. void giaiThua() {
int N;
long long result = 1;
printf("Nhap vao so N tinh giai thuan");
scanf(" %ld", &N);
if (N > 1) {
for (int i = 2; i <= N; i++) {
result = result * i;
}
}
printf("%lld", result);
}
Cách viết hàm rất tệ vì việc tính giai thừa lại phụ thuộc
hàm nhập scanf và xuất printf
49. Đặt tên hàm kiểu mệnh lệnh
• Lập trình mệnh lệnh: imperative programming
• Tên hàm bắt đầu bằng động từ, camel case,
chữ cái đầu tiên viết chữ thường
– lowerCase(const char* string)
– reverseString(const char* string)
– validateEmail(const char* email)
– sendPackage(const byte* buff)
50. Đặt tên hàm để kiểm tra đúng sai
• Nên bằng đầu bằng từ is và trả về kiểu boolean
• C không hỗ trợ boolean thì dùng stdbool.h hoặc tự
định nghĩa
• Ví dụ:
– bool isJobDone(int jobID)
– bool isTodayWeekEnd(date today)
51. Đặt tên hàm xử lý sự kiện
• Lập trình hướng sự kiện: event driven
programming
• Luôn bắt đầu bằng on theo sau tên sự kiện
• Ví dụ:
– void onReceiveEmail(netEvent* e)
– void onWindowClose()
– void onCacheIsFull()
52. đặt tên tham số
• Sử dụng danh từ tiếng Anh
• Nếu tên hàm đã rõ ràng, dùng ký tự
i,j,k,N … để đặt tên tham số
• Không cần phải bổ xung tên kiểu
• Mảng, tập nên dùng danh từ số nhiều
53. Chống thay đổi tham số
void change(const char* string)
{
string[1] = 'A';
}
Không sửa
đổi nội
dung
chuỗi
error: read-only variable
is not assignable
54. Chống thay đổi địa chỉ con trỏ
void change(char* const string)
{
string = "New value";
}
Không
thay đổi
địa chỉ
con trỏ
error: cannot assign to variable
'string' with const-qualified
type 'char *const'