You are on page 1of 53

C rất dễ

Version 0.3

HĐP
UET HSGS
Chương I. CHỈ LỘ
1. Số hóa
Mọi thứ đều có thể được biểu diễn dưới dạng số. Ví dụ:
 Bảng mã ký tự
 Hình ảnh
 Âm thanh
Quy trình làm việc được số hóa (thay thế bằng phần
mềm)
Tri thức (knowledge) cũng được số hóa
Giá trị Dữ liệu ngày càng tăng. Bơi trong biển số, ai có
dữ liệu và biết cách xử lý dữ liệu sẽ chiến thắng.
Chương trình = Đưa dữ liệu vào, xử lý dữ liệu, xuất dữ
liệu ra.
2. MÔ HÌNH LMC
Các thành phần: Calculator, Tủ đồ, LM, Rổ vào, rổ ra.
Các lệnh cơ bản mà LM hiểu
Các
3. Nấu Ăn
Nguyên liệu
Dụng cụ
Công thức nấu ăn
4. SƠ ĐỒ KHỐI

1
5. IDE

2
Chương II. NHẬP – XUẤT
1. Hello World
Một chương trình C thường gồm các phần sau đây:
 Các lệnh tiền xử lý (Preprocessor Command)
 Hàm (Function)
 Biến (Variable)
 Lệnh (Statement)
 Biểu thức (Expression)
 Bình luận (Comment)
Ví dụ chương trình sau đây in ra màn hình dòng chữ
Hello World
#include <stdio.h> // bình luận 1 dòng
int main() {
/* Chương trình đầu tiên */
printf("Hello, World! \n");
}

Dòng đầu tiên #include <stdio.h> là câu lệnh tiền xử lý,


yêu cầu trình biên dịch kết hợp với thư viện stdio (trong
file stdio.h) trước khi biên dịch.
Dòng thứ 2 int main() là hàm chính trong chương trình C,
là nơi các đoạn code sẽ được thực thi ngay khi chạy
chương trình. Các đoạn code trong chương trình main()
phải được đặt giữa 2 dấu mở và đóng ngoặc nhọn {}.
Dòng kế tiếp /*...*/ sẽ được trình biên dịch bỏ qua. Nội
dung nằm giữa /* và */ là bình luận (comment) của
chương trình. Nếu chỉ nằm trên một dòng, có thể đánh
dấu bình luận bằng //. Các bình luận sẽ giúp việc đọc
code dễ dàng hơn.

3
Dòng printf(...) là một hàm có trong thư viện stdio của
ngôn ngữ C. Hàm này sẽ in ra dòng chữ "Hello, World!"
trên màn hình.
Chương trình có thể gồm nhiều câu lệnh (statement).
Câu lệnh có thể là lệnh gán, lệnh cộng trừ nhân chia,
lệnh điều kiện, lệnh lặp hay lời gọi hàm. Để phân biệt 2
lệnh kề nhau, sau một lệnh phải đặt dấu chấm phẩy (;).
Các lệnh có thể được viết liền nhau trên một dòng, tuy
nhiên để dễ nhìn, dễ sửa lỗi, mỗi lệnh nên được viết trên

Thiếu dấu chấm phẩy là lỗi hay


gặp nhất khi lập trình lúc ban đầu.
một dòng.

Biên dị
dịch và chạ
chạy chương trình
trình
 Mở một trình biên dịch, chẳng hạn Code Block
hay Dev-C++, tạo ra một file mới, gõ đoạn code
trên vào.
 Lưu thành file hello.cpp.
 Nhấn F9 hoặc Execute/ Compile để biên dịch file
mã nguồn .cpp thành file khả thi .exe.
 Mở command prompt và di chuyển tới thư mục
bạn lưu file hello.cpp. Nếu chương trình không có
lỗi, bạn sẽ thấy file hello.exe.
 Gõ hello trên màn hình command prompt, bạn sẽ
thấy chương trình hello.exe chạy và in ra dòng
chữ Hello World.

4
2. Biến
Muốn thao tác với dữ liệu, cần có nơi để chứa dữ liệu.
Trong chương trình C, nơi chứa dữ liệu chính là Biến
(variable – giá trị có thể biến đổi được). Về bản chất,
Biến là một vùng nhớ (gồm một hoặc nhiều byte). Thay
vì bắt buộc người lập trình sử dụng địa chỉ tuyệt đối của
vùng nhớ, trình biên dịch cho phép người lập trình đặt
tên vùng nhớ đó, và sử dụng tên – chứ không phải địa
chỉ (khi dịch và chạy, trình biên dịch sẽ ánh xạ tên này
thành địa chỉ).
Quy tắc đặt tên biến trong C:
 Tên biến chỉ chứa ký tự trong bảng chữ cái tiếng
Anh (a…z, A…Z), chữ số (0..9) và dấu gạch
dưới (_).
 Tên biến không được bắt đầu bằng chữ số.
 Chữ hoa và chữ thường phân biệt nhau (biến a
khác biến A).
 Tên biến không trùng với các từ khóa (keyword).

5
Từ khóa
Dưới đây là danh sách các từ khóa trong C. Chúng
không được dùng để đặt tên hằng số, tên biến hay tên
hàm.

auto else long switch


break enum register typedef
case extern return union
char float short unsigned
const for signed void
continue goto sizeof volatile
default if static while
do int struct _packed
double

Khoả
Khoảng trố
trống (whitespace)
Khoảng trống (các dấu cách, dấu tab) được dùng để chia
cách các phần khác nhau của lệnh, giúp nhìn lệnh dễ
dàng hơn. Khi dịch, khoảng trống sẽ bị bỏ qua.

3. Kiểu
Mọi thứ lưu trong máy tính đều là số nhị phân, nên khi
đưa dữ liệu vào, chúng ta phải coi dữ liệu thuộc kiểu gì.
Ngôn ngữ lập trình C cung cấp một số kiểu đơn giản sau
đây:
 Kiểu cơ bản liên quan đến các phép toán số học
(kiểu số nguyên, kiểu số thực).

6
 Kiểu đếm (enumerated) liên quan đến tập hợp
các giá trị rời rạc cố định
 Kiểu void
 Kiểu phức hợp (cấu trúc structure, mảng).

Kiể
Kiểu số
số nguyên
Bảng dưới đây chỉ ra kiểu, số byte nhớ được cấp phát và
miền giá trị.

Kiểu #byte Miền giá trị


-128 → 127 hoặc 0
char 1
→ 255
unsigned char 1 0 →255
signed char 1 -128 →127
-32,768 → 32,767
int 2 hoặc 4 hoặc -2,147,483,648
→2,147,483,647
0 →65,535 hoặc 0
unsigned int 2 hoặc 4
→4,294,967,295
short 2 -32,768 →32,767
unsigned short 2 0 → 65,535
-2,147,483,648 →
long 4
2,147,483,647
unsigned long 4 0 → 4,294,967,295

Để biết số lượng byte của một kiểu, có thể dùng toán tử


sizeof : sizeof(type) trả lại số lượng byte của một kiểu.

7
Kiể
Kiểu số
số thự
thực
Bảng dưới đây chỉ ra kiểu, số byte nhớ được cấp phát và
miền giá trị, đọ chính xác của kiểu số thực.

Kiểu #byte Miền giá trị


float 4 1.2E-38 → 3.4E+38
2.3E-308 →
double 8
1.7E+308
3.4E-4932 →
long double 10
1.1E+4932

Kiể
Kiểu void
Kiểu void (hư vô trống rỗng) không mang một giá trị
nào, thường được sử dụng trong những tình huống sau:
 Hàm trả về void. Đôi khi một số hàm chỉ thực
hiện một công việc nào đó mà không trả lại giá
trị nào. Khi đó kiểu trả về là void.
 Hàm không có tham số đầu vào. Khi đó, tham số
đầu vào là void.

3. Khai báo biến


Khai báo biến là việc xin trình biên dịch một vùng nhớ
để lưu trữ giá trị. Một khai báo biến gồm việc chỉ ra kiểu
và danh sách các tên biến thuộc kiểu đó đi kèm.
type variable_list;

8
Ở đây, type phải là danh sách các kiểu chuẩn trong hoặc
đã được người dùng định nghĩa trước và variable_list là
danh sách tên các biến hoặc mảng. Tên các biến cách
nhau một dấu phẩy. Sau một khai báo biến là dấu chấm
phẩy.
Ví dụ:
int i, j, k, count;
char ch;
float f, amount;
double result;

Dòng int i, j, k; khai báo 3 biến kiểu int có tên i, j và k.


Biến có thể được khởi tạo (gán giá trị ban đầu) ngay
trong khai báo bằng cách sau
type variable_name = value;

Ví dụ :

int a = 3;
char x = 'x';

Nếu không khởi tạo trước, có thể giá trị ban đầu của biến
không xác định trước.

Hằng số cũng là một loại biến – nhưng sau khi định


nghĩa thì chương trình không thể thay đổi giá trị. Có 2
cách định nghĩa hằng số như sau

1. Bằng từ khóa “const”


2. Bằng tiền chỉ dẫn “#define”

9
Ví dụ:

Phạm vi (scope) trong bất kỳ ngôn ngữ lập trình là đoạn


code trong đó mọi biến khai báo trong đó đều tồn tại và
sẽ không tồn tại bên ngoài phạm vi này.

 Biến nằm bên trong hàm hay trong một khối code
{} được gọi là biến cục bộ (local) variables.
 Nằm ngoài tất cả các hàm được gọi là biến toàn
cục (global) variables.

Biến cục bộ chỉ có thể được các câu lệnh bên trong phạm
vi của nó sử dụng. Phạm vi bên ngoài không thể sử dụng
được.
Trong ví dụ sau, các biến a, b, and c là biến cục bộ của
hàm main().
#include <stdio.h>
int main () {
/* khai báo biến cục bộ */
int a, b, c;
/* khởi tạo biến */
a = 10;
b = 20;
c = a + b;
printf ("%d,%d,%d\n", a, b, c);
return 0;
}

Biến toàn cục có khai báo không nằm trong bất kỳ hàm
nào cả, thường đặt ở đầu chương trình. Biến toàn cục tồn
tại trong suốt thời gian chương trình chạy và có thể được
bất kỳ câu lệnh nào (có thể bên trong các hàm) sử dụng.

10
Chú ý biến toàn cục sẽ được hệ thống tự khởi tạo các giá
trị như sau:
Kiểu Giá trị khởi tạo
int 0
char '\0'
float 0
double 0
pointer NULL

#include <stdio.h>
/* khai báo biến toàn cục */
int g;
int main () {
/* khai báo biến cục bộ */
int a, b;

/* khởi tạo */
a = 10;
b = 20;
g = a + b;

printf ("%d", a, b,g);

return 0;
}

Chương trình có thể có biến toàn cục và biến cục bộ với


cùng tên, nhưng ở bên trong hàm, giá trị biến cục bộ bên
trong có độ ưu tiên cao hơn.
#include <stdio.h>

/* khai báo biến toàn cục */


int g = 20;

11
int main () {

/* khai báo biến cục bộ */


int g = 10;
printf ("%d", g);
return 0;
}

Kết quả in ra

10

4. Nhập - Xuất
Input (nhập) là việc đưa dữ liệu vào chương trình.
Thường luồng dữ liệu vào có thể đến từ file hay từ dòng
lệnh.
Output (xuất) là việc đưa kết quả chương trình ra ngoài.
Thường có thể ghi ra file hay in ra màn hình.
C có các hàm để nhập từ file, từ dòng lệnh và xuất ra file
hay ra màn hình.
Ngôn ngữ C coi mọi thiết bị là file – do đó, cả bàn phím
hay màn hình đều được coi là file và chúng được mở
ngay khi chương trình chạy, để giúp chương trình tương
tác với bàn phím hay màn hình.
Standard File File Pointer Device
Standard input stdin Keyboard
Standard
stdout Screen
output
Standard error stderr Your screen

12
Sau đây chúng ta sẽ cùng xem cách đọc từ bàn phím và
viết ra màn hình.

Hàm getchar() và putchar()


Hàm int getchar(void) đọc ký tự được gõ từ bàn phím,
trả lại một con số. Hàm này chỉ đọc mỗi lần một ký tự.
Nếu muốn đọc nhiều ký tự, phải đặt hàm này trong vòng
lặp.
Hàm int putchar(int c) ghi ký tự c lên màn hình và trả
lại chính ký tự c này. Mỗi lần hàm chỉ in ra 1 ký tự. Nếu
muốn in ra nhiều ký tự, phải đặt hàm trong vòng lặp
Chương trình sau sẽ đọc vào một chữ cái thường và in ra
chữ cái hoa.
#include <stdio.h>
int main() {
int c;
c = getchar();
putchar(c-32);
}

Chú ý trong bảng mã ASCII, ký tự hoa đứng trước ký tự


thường 32.

Hàm gets() và puts()


Hàm char *gets(char *s) đọc một dòng từ stdin vào
vùng nhớ được trỏ bởi s cho đến khi gặp ký tự xuống
dòng (Newline) hoặc EOF (End of File).
Hàm int puts(const char *s) ghi chuỗi s ra stdout.

13
Chương trình sau đọc một chuỗi từ bàn phím và in ra
chuỗi đó 2 lần trên màn hình
#include <stdio.h>
int main() {
char str[100];
gets(str);
puts(str);
puts(str);
return 0;
}

Hàm scanf() và printf()


Đây là 2 hàm rất mạnh, có thể đọc và in ra nhiều kiểu dữ
liệu khác nhau.
Hàm int scanf(const char *format, ...) đọc dữ liệu từ
stdin và duyệt theo khuôn format, đưa dữ liệu tương ứng
vào các biến..
Hàm int printf(const char *format, ...) viết dữ liệu ra
stdout theo khuôn format.
Khuôn format có thể là một xâu ký tự cố định, nhưng
cũng có thể là %s, %d, %c, %f, … - dùng để đọc và in
chuỗi, số nguyên, ký tự hay số thực.
Xét ví dụ sau:
#include <stdio.h>
int main( ) {
char ch, str[100];
int i;
scanf("%s %c %d", str, &ch, &i);
printf( "\n string %s:", str);
printf( "\n char %c:", ch);

14
printf( "\n number %d:", i);
return 0;
}

Chương trình trên sẽ đọc vào một xâu ký tự (đưa vào


biến str), một ký tự (đưa vào biến ch) và một số nguyên
(đưa vào biến i). Sau đó, chương trình sẽ lần lượt in ra
xâu str, ký tự ch và số i.
Chú ý hàm scanf() coi dữ liệu nhập vào có đúng khuôn
dạng %s và %d, nghĩa là chuỗi – số nguyên. Khi đọc
chuỗi, scanf() sẽ dừng khi gặp ký tự space, do đó "Hello
World" được coi là 2 chuỗi.
Đối với kiểu số thực, để in ra 2 chữ số sau dấu phẩy thập
phân, khuôn sẽ là “%0.2f”.

15
Chương 3. TOÁN TỬ & BIỂU THỨC
Toán tử (operator) yêu cầu trình biên dịch thực hiện một
phép toán hay một hàm logic cụ thể.
Toán tử có 1 hoặc 2 toán hạng.
Ví dụ biểu thức a+b thì + là toán tử, và a, b là 2 toán
hạng.
Ngôn ngữ lập trình C có một số loại toán tử sau:

 Toán tử số học
 Toán tử quan hệ
 Toán tử logic
 Toán tử Bitwise Operators
 Toán tử gán
 Các toán tử đặc biệt

1. Toán tử Số học
Bảng sau liệt kê định nghĩa các toán tử số học. Giả sử
biến A = 10, biến B = 20. Gọi là số học nhưng các toán
tử này áp dụng cho cả kiểu số thực.

Toán tử Mô tả Ví dụ
+ Cộng 2 toán hạng. A + B = 30
Lấy toán hạng trái trừ đi
− A − B = -10
toán hạng phải.
* Nhân 2 toán hạng A * B = 200
Lấy toán hạng trái chia
/ B/A=2
cho toán hạng phải, với số

16
nguyên, kết quả là thương
số, với số thực, kết quả là
số thực
Lấy dư khi chia toán hạng
trái cho toán hạng phải
% B%A=0
(với 2 toán hạng kiểu số
nguyên).
++ Tăng toán hạng lên 1. A++ = 11
-- Giảm toán hạng đi 1. A-- = 9
Ví dụ: Chương trình sau nhập vào 2 số nguyên, sau đó in
ra tổng, hiệu của 2 số.
#include <stdio.h>
int main() {
int a, b;
scanf ("%d %d", &a, &b);
printf("%d %d %d %d",a+b, a-b);
}

Chú ý nếu a = 10, b = 3 thì a/b = 3 (thương trong phép


chia 2 số nguyên).
Nếu muốn lấy kết quả là 3,333 thì hoặc:
 Khai báo a hoặc b là kiểu float.
 Ép kiểu của a hoặc b về kiểu float bằng cách sau
(float) a. Như vậy (float) a/ b = 3.333.
printf(“%f", (float)a/b);

2. Toán tử Quan hệ
Toán tử quan hệ nằm trong các biểu thức quan hệ. Biểu
thức quan hệ chỉ có giá trị đúng (true) hoặc sai (false).

17
Bảng sau liệt kê các toán tử quan hệ. Giả sử biến A = 10,
biến B = 20.

Toán tử Mô tả Ví dụ
Kiểm tra xem 2 toán
hạng có bằng nhau
không. Nếu có, điều
== (A == B) sai
kiện có giá trị đúng.
Nếu sai, điều kiện có
giá trị sai.
Kiểm tra xem 2 toán
hạng có khác nhau
không. Nếu có, điều (A != B)
!=
kiện có giá trị đúng. đúng.
Nếu sai, điều kiện có
giá trị sai.
Kiểm tra xem toán
hạng bên trái có lớn
hơn toán hạng bên phải
> không. Nếu có, điều (A > B) sai
kiện có giá trị đúng.
Nếu sai, điều kiện có
giá trị sai.
Kiểm tra xem toán
hạng bên trái có nhỏ
hơn toán hạng bên phải
(A < B)
< không. Nếu có, điều
đúng.
kiện có giá trị đúng.
Nếu sai, điều kiện có
giá trị sai.

18
Kiểm tra xem toán
hạng bên trái có lớn
hơn hoặc bằng toán
>= hạng bên phải không. (A >= B) sai
Nếu có, điều kiện có
giá trị đúng. Nếu sai,
điều kiện có giá trị sai.
Kiểm tra xem toán
hạng bên trái có nhỏ
hơn hoặc bằng toán
(A <= B)
<= hạng bên phải không.
đúng.
Nếu có, điều kiện có
giá trị đúng. Nếu sai,
điều kiện có giá trị sai.

3. Toán tử Logic
Toán tử Logic thao tác trên các giá trị logic (đúng / sai)
và kết quả có giá trị logic. Giá trị khác 0 được coi là
đúng, giá trị 0 được coi là sai.
Bảng sau liệt kê các toán tử logic. Giả sử biến A = 1,
biến B = 0.

Toán tử Mô tả Ví dụ
Phép toán logic
AND (và). Nếu cả
2 toán hạng có giá (A && B) =
&&
trị khác 0, kết quả false.
là đúng. Ngược lại
kết quả sai.

19
Phép toán logic OR
(hoặc). Nếu 1 trong
2 toán hạng có giá
|| (A || B) = true.
trị khác 0, kết quả
là đúng. Ngược lại
kết quả sai.
Phép toán logic
NOT (phủ định).
Phép toán đảo !(A && B) =
!
ngược kết quả của true.
toán hạng: 1 thành
0, 0 thành 1.

4. Toán tử trên bit


Các toán tử trên bit thực thi trên từng bit tương ứng của
2 toán hạng. Bảng chân lý cho các toán tử &, |, và ^ trên
các bit p và q như sau:

p q p&q p|q p^q


0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1

Biểu diễn nhị phân của A = 60 = 0011 11002 và B = 13


= 0000 11012:

Kết quả các phép toán là:

20
A & B = 0000 1100
A|B = 0011 1101
A^B = 0011 0001
~A = 1100 0011
Bảng sau liệt kê các toán tử trên bit trong ngôn ngữ C.
Giả sử biến A = 60, biến B = 13.

Toán tử Mô tả Ví dụ
Toán tử binary AND
tạo ra bit 1 nếu cả 2
(A & B) = 0000
& bit tương ứng của
11002 = 12.
toán hạng đều là 1.
Ngược lại tạo ra 0.
Toán tử Binary AND
tạo ra bit 1 nếu 1
(A | B) = 0011
| trong 2 bit tương ứng
11012 = 61
của toán hạng là 1.
Ngược lại tạo ra 0.
Toán tử Binary XOR
tạo ra bit 1 nếu 2 bit
tương ứng ở 2 toán (A ^ B) = 0011
^
hạng khác nhau. 00012 = 49
Ngược lại tạo ra bit
0.
Toán tử này chỉ có 1
toán hạng và đảo (~A ) = 1100
~
ngược giá trị các bit 01002 = -60
của toán hạng
Toán tử dịch trái. Tất
A << 2 = 1111
<< cả các bit trong toán
0000 = 240 i.e.,
hạng bên trái dịch

21
sang trái x vị trí,
trong đó x là toán
hạng phải. >>
Toán tử dịch phải.
Tất cả các bit trong
toán hạng bên trái A >> 2 = 0000
>>
dịch sang phải x vị 111115
trí, trong đó x là toán
hạng phải.
Chú ý
 x >> 1 tương đương với việc lấy x chia 2
 x << 1 tương đương với việc lấy x nhân 2

5. Toán tử gán
Bảng sau liệt kê các toán tử gán.

Toán tử Mô tả Ví dụ
C = A + B thực
Tính giá trị biểu thức
hiện việc gán
= bên phải và gán giá trị
giá trị A + B
đó cho biến bên trái
cho C
Cộng biến bên trái với
C += A tương
biểu thức bên phải. Gán
+= đương với C =
giá trị thu được vào biến
C+A
bên trái
Trừ biến bên trái với
C-= A tương
biểu thức bên phải. Gán
-= đương với C =
giá trị thu được vào biến
C-A
bên trái.

22
Nhân biến bên trái với
C *= A tương
biểu thức bên phải. Gán
*= đương C = C *
giá trị thu được vào biến
A
bên trái.
Chia biến bên trái với
C /= A tương
biểu thức bên phải. Gán
/= đương với C =
giá trị thu được vào biến
C/A
bên trái.
Lấy phần dư khi chia
biến bên trái cho biểu C %= A tương
%= thức bên phải. Gán giá đương với C =
trị thu được vào biến C%A
bên trái.
Dịch các bit của biến
C <<= 2 tương
bên trái sang bên trái x
<<= đương với C =
vị trí, trong đó x là toán
C << 2
hạng bên phải.
Dịch các bit của biến
C >>= 2 tương
bên trái sang bên phải x
>>= đương với C =
vị trí, trong đó x là toán
C >> 2
hạng bên phải
C &= 2 tương
Thực hiện phép toán
&= đương với C =
Bitwise AND và gán
C&2
C ^= 2 tương
Thực hiện phép toán
^= đương với C =
Bitwise XOR và gán
C^2
C |= 2 tương
Thực hiện phép toán
|= đương với C =
Bitwise OR và gán
C|2

23
6. Một số toán tử đặc biệt

Dưới đây là một số toán tử đặc biệt

Toán tử Mô tả Ví dụ
sizeof(a), nếu a
Trả lại kích thước
sizeof() thuộc kiểu int,
(số byte) của biến
trả lại giá trị 4.
Trả lại địa chỉ của &a; trả lại địa
&
một biến chỉ biến a.
* Con trỏ đến biến. *a;
Con ? X : Y Nếu
điều kiện Con đúng
thì biểu thức nhận
?:
giá trị biểu thức X,
nếu không nhận giá
trị biểu thức Y

7. Thứ tự ưu tiên
Bảng dưới đây phân nhóm các toán tử và liệt kê theo thứ
tự ưu tiên giảm dần. Ví dụ phép nhân chia có thứ tự ưu
tiên cao hơn cộng trừ. Trong biểu thức, toán tử có độ ưu
tiên cao được thực hiện trước. Nếu có độ ưu tiên bằng
nhau, thực hiện từ trái qua phải.

Nhóm Toán tử Thứ tự


Ngoặc () [] -> . ++ - - Trái qua phải

24
1 toán hạng, ! ~ ++ -- (type)*
Phải qua trái
cộng trừ & sizeof
Nhân chia */% Trái qua phải
Cộng trừ +- Trái qua phải
Dịch << >> Trái qua phải
Quan hệ < <= > >= Trái qua phải
So sánh bằng == != Trái qua phải
Bitwise AND & Trái qua phải
Bitwise XOR ^ Trái qua phải
Bitwise OR | Trái qua phải
Logical AND && Trái qua phải
Logical OR || Trái qua phải
Điều kiện ?: Phải qua trái
= += -= *= /=
Gán %=>>= <<= &= Phải qua trái
^= |=
Dấu phẩy , Trái qua phải

25
Chương 4. LỆNH RẼ NHÁNH
Đôi khi luồng thực thi của chương trình không tuần tự
mà rẽ sang một nhánh khác, tùy theo một điều kiện nào
đó.
Chú ý trong ngôn ngữ lập trình C, mọi giá trị khác 0 và
khác null đều có giá trị true. Ngược lại là giá trị false
Ngôn ngữ lập trình C cung cấp 1 số lệnh rẽ nhánh sau
đây if, if else, switch case.

1. Lệnh if
Cú pháp lệnh if như sau
if(cond) {
statements
}

Nếu biểu thức logic cond có giá trị true, khối lệnh
statements bên trong dấu {} sẽ được thực thi.
Nếu biểu thức cond có giá trị false, khối lệnh statements
không được thực thi và lệnh đầu tiên sau dấu } của if sẽ
được thực thi.
Khối lệnh bên trong nên thụt vào so với if một dấu tab.
Nếu khối lệnh chỉ có 1 lệnh đơn thì có thể bỏ {}.
Sau if (cond) nếu có dấu ; thì khối lệnh statements luôn
được thực thi, bất kể giá trị của exp (Vì khi đó, khối lệnh
của if là khối lệnh rỗng.

26
if (cond) {
statements;
}

cond
iti

Nếu điều kiện


cond đúng

statements

Nếu điều kiện


cond sai

2. Lệnh if….else
Cú pháp lệnh if else như sau
if(con) {
if-statements
/* các câu lệnh được thực hiện khi
biểu thức logic con có giá trị true */
} else {
else-statements
/* các câu lệnh được thực hiện khi
biểu thức logic con có giá trị false */

27
}

Nếu biểu thức cond có giá trị true, khối lệnh if-
statements sẽ được thực thi. Ngược lại, khối lệnh else-
statements sẽ được thực thi.

if (cond) {
if-statements
} else {
else-statements
}

cond
d
Nếu điều
kiện cond sai
Nếu điều kiện
cond đúng

if-statements else-statements

3. Lệnh if else nối nhau


Ngay sau phần else, có thể lại có lệnh if else khác. Khi
đó, có thể xét nhiều trường hợp chỉ bằng cách sử dụng
lệnh if else. Kiểu này có dạng sau:
if(cond 1 ) {

28
/* Chạy đoạn lệnh nếu cond 1 có giá trị
true */
} else if(cond 2 ) {
/* Chạy đoạn lệnh nếu cond 2 có giá trị
true */
} else if(cond 3 ) {
/* Chạy đoạn lệnh nếu cond 3 có giá trị
true */
} else {
/* Chạy đoạn lệnh nếu cả 3 biểu thứ
có giá trị false */
}

4. Lệnh if lồng nhau


Trong lòng nhóm lệnh if có thể chứa lệnh if khác, dạng
như sau
if(cond 1 ) {
/* Chạy đoạn lệnh nếu cond 1 có giá
trị true */
if(cond 2 ) {
/* Chạy đoạn lệnh nếu cond 2 có giá
trị true */
}
}

Có thể lồng if else trong phần else theo cách tương tự.
Chú ý các lệnh if 2 thụt vào 1 dấu tab so với lệnh if 1,
các lệnh trong lòng if 2 thụt 1 dấu tab so với lệnh if 2.

29
5. Lệnh switch
Lệnh switch cho phép kiểm tra giá trị của biến trên một
tập hợp các giá trị. Tùy vào giá trị biến, đoạn code nào sẽ
được thực thi.
Cú pháp của lệnh này như sau:
switch(exp) {
case Const_1 :
statements1;
break; /* có thể có hoặc không */

case Const_2 :
statements2;
break; /* có thể có hoặc không */

/* Có thể có thêm nhiều case */


default : /* có thể có hoặc không */
statement(s);
}

Lệnh này thực thi như sau:


 Biểu thức exp phải có kiểu số nguyên hoặc đếm
được.
 Số lượng các case có thể tùy ý. Sau case là giá trị
dùng để so sánh với giá trị biểu thức exp. Sau đó
là dấu :
 Giá trị các hằng số Const_1,… phải có kiểu cùng
kiểu của biểu thức.
 Khi giá trị exp = Const_i, các lệnh sau case đó sẽ
lần lượt được thực hiện – cho đến khi gặp lệnh
break.
 Khi gặp lệnh break, khối lệnh switch kết thúc.
Lệnh ngay sau switch được thực thi.

30
Nếu không có break, tất cả các lệnh sau đó (có thể của
các case khác) sẽ được thực thi – cho đến khi gặp lệnh

switch (exp) {
Const1 : statements1;
Const2 : statements2;

default : statementsN;
}
exp

Const
statements1

Const
statements2

default
statementsN

break.
Có thể có hoặc không có nhánh default. Nếu có, phải
xuất hiện ở phần cuối lệnh switch. Cụm lệnh sau default
sẽ được thực hiện nếu giá trị biểu thức exp không trùng
với bất kể giá trị liệt kê trong các case ở trên.
Chú ý các switch có thể lồng nhau.

6. Toán tử ? :
Khuôn dạng biểu thức:
Exp1 ? Exp2 : Exp3

31
Trong đó Exp1, Exp2, and Exp3 là 3 biểu thức. Chú ý
đến vị trí của ? và :.
Giá trị biểu thức này được tính như sau:
 Tính giá trị Exp1.
 Nếu Exp1 có giá trị true, biểu thức ? nhận giá trị
của biểu thức Exp2.
 Nếu Exp1 có giá trị false, biểu thức ? nhận giá trị
của biểu thức Exp3.

32
Chương 5. CÂU LỆNH LẶP
Thông thường, các lệnh sẽ được thực hiện tuần tự. Tuy
nhiên trong nhiều trường hợp, một cụm lệnh được thực
hiện lặp đi lặp lại nhiều lần. Các ngôn ngữ lập trình đều
có những cấu trúc cho phép một cụm lệnh được thực
hiện nhiều lần.
1. Vòng lặp while
Vòng lặp while thực hiện cụm lệnh trong thân vòng lặp
chừng nào điều kiện lặp còn đúng.
Cú pháp lệnh như sau:
while(condition) {
code block
}
Ở đây, code block có thể là một hoặc nhiều lệnh.
condition có thể là một biểu thức, (nếu nó là một giá trị
khác 0, thì nó mang giá trị true). Cụm lệnh sẽ liên tục
được thực hiện cho đến khi điều kiện con có giá trị true.
Khi điều kiện condition có giá trị false, dòng lệnh ngay
sau } của while được thực hiện (khối lệnh không được
thực hiện nữa).
Lược đồ:

33
Chú ý rằng nếu khi kiểm tra điều kiện condition sai, khối
lệnh code block có thể không được thực hiện (bị bỏ qua)
và câu lệnh đầu tiên sau vòng lặp while sẽ được thực thi.

2. Vòng lặp for


Vòng lặp for cho phép viết một đoạn code được thực
hiện một số lần cụ thể.
Cú pháp vòng lặp for như sau:

34
for (init; condition; increment ) {
code block;
}
Luồng thực thi của lệnh for như sau:
Các lệnh trong phần init được thực thi đầu tiên, và chỉ
được thực hiện đúng một lần. Bước này thường được sử
dụng để khai báo và khởi tạo các biến kiểm soát vòng
lặp. Khối này có thể không chứa lệnh nào, nhưng vẫn
phải có đủ dấu ;.
Kế tiếp, đánh giá điều kiện condition. Nếu điều kiện này
có giá trị true, phần thân vòng lặp được thực hiện. Nếu
điều kiện này có giá trị false, vòng lặp chấm dứt và câu
lệnh ngay sau vòng lặp được thực hiện.
Sau khi phần thân vòng lặp được thực hiện, các lệnh
trong phần increment được thực thi. Phần này thường
dùng để cập nhật lại các biến kiểm soát vòng lặp. Phần
này có thể để trống. Trước phần này có dấu ;.
Biểu thức condition lại được đánh giá lại. Nếu nhận giá
trị true, thân vòng lặp lại được thực thi và quá trình này
được lặp lại (thân vòng lặp, khối increment, kiểm tra
điều kiện). Chỉ khi điều kiện có giá trị false, vòng lặp
mới ngừng lại.

35
36
3. Vòng lặp do while
Vòng lặp do while check điều kiện ở cuối vòng lặp. Như
vậy thân vòng lặp được thực thi ít nhất một lần.
Cú pháp lệnh này như sau:
do {
code block
} while( condition );
Nếu biểu thức condition có giá trị true, khối lệnh lại
được thực thi. Quá trình này chỉ được dừng khi biểu thức
condition nhận giá trị false.

37
4. Vòng lặp lồng nhau
Bên trong thân vòng lặp có thể chứa vòng lặp khác. Ví
dụ:
for ( init; condition; increment ) {

for ( init; condition; increment ) {


statement(s);
}
statement(s);
}
Tùy vào dạng bài toán, vòng lặp while (for) có thể nằm
bên trong vòng lặp while (for).

5. Các lệnh điều khiển vòng lặp


Các lệnh trong vòng lặp thay đổi luồng thực thi chương
trình (không còn tuần tự nữa, ví dụ khi từ cuối khối lệnh
thân vòng lặp quay về phần đầu thân vòng lặp).
Chú ý khi luồng thực thi chương trình rời một phạm vi
(scope), các biến được khai báo và khởi tạo trong phạm
vi đó sẽ không còn tồn tại.
Lệnh break
Khi gặp lệnh break trong vòng lặp, chương trình sẽ thoát
khỏi vòng lặp và lệnh đầu tiên ở ngay sau vòng lặp sẽ
được thực hiện
Kết thúc một nhánh case trong cấu trúc switch, và lệnh
đầu tiên ở ngay sau switch sẽ được thực hiện
Nếu có nhiều vòng lặp lồng nhau, lệnh break thoát khỏi
vòng lặp trong cùng.

38
Luồng thực thi:

Lệnh continue
Lệnh continue tương tự lệnh break – nhưng với lệnh này,
chương trình không thực hiện đoạn code sau continue,
mà quay lại thực hiện chu trình lặp tiếp theo.
Với vòng lặp for, lệnh continue ngưng việc thực hiện các
lệnh trong thân vòng lặp. Chương trình sau đó thực hiện
phần increment, sau đó sẽ kiểm tra điều kiện. Nếu điều
kiện đúng, sẽ thực hiện thân vòng lặp (thực hiện từ đầu).

39
Đối với vòng lặp while và do … while, câu lệnh
continue ngưng phần thân vòng lặp, chuyển luôn sang
phần kiểm tra điều kiện.

Lệnh goto
Lệnh goto cho phép nhảy tới bất kỳ câu lệnh nào với
nhãn định trước.
Chú ý: nên hạn chế sử dụng goto vì khó kiểm soát được
luồng thực thi của chương trình.
Cú pháp lệnh goto:
goto label;

40
..
.
label: statement;
Ở đây label, có thể là xâu văn bản (không trùng từ khóa
của C) và có thể để ở bất kỳ đâu trong chương trình,
trước hoặc sau goto.

Vòng lặ
lặp vô hạ
hạn
Vòng lặp có thể được thực thi vô hạn nếu điều kiện luôn
đúng. Ví dụ
for (;;) {
}

hoặc

41
while (1) {

Chương 6. Mảng và Xâu


1. Mảng
Mảng là kiểu dữ liệu dùng để lưu trữ nhiều phần tử của
cùng một kiểu, nằm liên tiếp nhau. Để dễ hình dung, có
thể coi mảng là dãy các biến có cùng kiểu liền nhau.
Thay vì phải khai báo a0, a1, …., a1000, bạn có thể sử
dụng a[0], a[1],… a[1000] để tương tác với từng biến
(đó là kiểu truy cập theo chỉ số).
Trong bộ nhớ, các phần tử của mảng sẽ nằm kề nhau,
phần tử đầu tiên nằm ở khu vực nhớ có địa chỉ bé nhất,
sau đó đến phần tử thứ hai.

Khai báo mả
mảng
Để khai báo mảng, cần chỉ ra kiểu của mảng, tên của
mảng, và kích thước mảng.
type arrayName [ arraySize ];
Đây là mảng một chiều. Trong đó arraySize là hằng số
nguyên dương và type là một kiểu chuẩn hay được định
nghĩa trước.
42
Ví dụ khai báo mảng số nguyên 10 phần tử
int num[10];
Khở
Khởi tạ
tạo mả
mảng
Có 2 cách khởi tạo mảng như sau:
int num[5] = {1, 2, 3, 4, 5};
Số lượng các giá trị trong {} không được lớn hơn số
lượng phần tử của mảng.
Nếu không khai báo kích thước mảng, nhưng có khởi
tạo, mảng chỉ đủ lớn để chứa đúng các số khởi tạo. Do
đó cách khai báo sau cũng tạo ra kết quả giống cách khai
báo trước.
int num[] = {1, 2, 3, 4, 5};
Ví dụ sau gán phần tử thứ 5 của mảng giá trị 5.
num[4] = 5;

Truy cậ
cập vào các phầ
phần tử
tử trong mả
mảng
Các phần tử trong mảng có thể được đọc/ ghi qua chỉ số.
Ví dụ lệnh gán i = num[8] sẽ gán cho biến i giá trị phần
tử thứ 9 của mảng num.
Mảng nhiề
nhiều chiề
chiều
Khai báo mảng nhiều chiều có dạng
type name[size1][size2]...[sizeN];
Ví dụ sau khai báo mảng num với các phần tử kiểu int,
có 3 chiều

43
int num [5][10][4];
Mảng hai chiề
chiều
Dạng hay gặp nhất của mảng nhiều chiều là mảng 2
chiều. Có thể coi đây là mảng 1 chiều, mà mỗi phần tử
của mảng 1 chiều này lại là 1 mảng 2 chiều. Khai báo
mảng 2 chiều với kích thước MxN như sau
type arrayName [M][N];
Trong đó type là kiểu hợp lệ, arrayName là 1 tên hợp lệ.
Ở đây, arrayName có thể coi như một bảng 2 chiều có M
hàng, N cột, mỗi phần tử ứng với một ô trong bảng.

Mọi phần tử của mảng a có thể truy cập qua a[i][j], trong
đó a là tên mảng, i và j lần lượt là chỉ số hàng và cột của
phần tử.
Khở
Khởi tạ
tạo mả
mảng 2 chiề
chiều
Sau đây là một minh họa cách khởi tạo mảng 3 hàng 4
cột
int a[3][4] = {
{0, 1, 2, 3} , /* khởi tạo hàng 0 */
{4, 5, 6, 7} , /* khởi tạo hàng 1 */
{8, 9, 10, 11} /* khởi tạo hàng 2 */
};

Cách thứ 2 cũng có hiệu quả tương tự

44
int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
Truy cậ
cập mả
mảng 2 chiề
chiều
Tương tự mảng 1 chiều, có thể truy xuất mảng 2 chiều
qua chỉ số hàng và cột. Ví dụ lệnh gán biến a giá trị một
phần tử trong mảng a như sau:
int val = a[2][3];

2. Chuỗi
Chuỗi là mảng ký tự 1 chiều, kết thúc bằng ký tự null
'\0'.
Sau đây là cách khai báo và khởi tạo một chuỗi chứa từ
Hello. Chú ý vì chứa ký tự null nên chiều dài chuỗi lớn
hơn chiều dài từ Hello.
char greeting[6] = {'H', 'e', 'l', 'l', 'o',
'\0'};

tương đương với

char greeting[] = "Hello";

C cung cấp một số hàm thao tác trên chuỗi như sau:
strcpy(s1, s2): sao chép chuỗi s2 vào chuỗi s1.
strcat(s1, s2): Nối chuỗi s2 vào cuối chuỗi s1.

45
strlen(s1): trả lại kích thước chuỗi s1.
strcmp(s1, s2): So sánh 2 chuỗi. Trả lại 0 nếu 2 chuỗi s1
và s2 giống nhau, giá trị âm nếu s1 < s2; giá trị dương
nếu s1 > s2.
strchr(s1, ch): Trả lại con trỏ, trỏ đến vị trí đầu tiên của
ký tự ch nằm trong chuỗi s1.
strstr(s1, s2); Trả lại con trỏ, trỏ đến vị trí đầu tiên của
chuỗi s2 nằm trong chuỗi s1.
Ví dụ minh họa

#include <stdio.h>
#include <string.h>

int main () {

char str1[12] = "Hello";


char str2[12] = "World";
char str3[12];
int len ;

/* sao chép chuỗi str1 vào str3 */


strcpy(str3, str1);
printf("strcpy( str3, str1) : %s\n", str3
);

/* ghép nối 2 chuỗi str1 và str2 */


strcat( str1, str2);
printf("strcat( str1, str2): %s\n", str1
);

/* độ dài chuỗi sau ghi ghép */


len = strlen(str1);
printf("strlen(str1) : %d\n", len );

return 0;

46
}

Kết quả khi chạy

strcpy(str3, str1) : Hello


strcat( str1, str2): HelloWorld
strlen(str1) : 10

47
Chương 7. Hàm
1. Khai báo, sử dụng hàm
Hàm là nhóm các lệnh cùng nhau thực hiện một công
việc nào đó.
Trong chương trình C, có ít nhất một hàm – hàm main()
– sẽ được thực thi khi chương trình được chạy. Bên cạnh
đó, thư viện C hỗ trợ rất nhiều hàm (print, scanf…).
Tuy nhiên, lập trình viên có thể tự xây dựng ra những
hàm mới trong chương trình của mình.
Chương trình có thể chia thành nhiều hàm, mỗi hàm thực
hiện một nhiệm vụ cụ thể.
Khai báo hàm (declaration) nói cho chương trình dịch về
tên hàm, kiểu trả về và các tham số đầu vào của hàm.
Phần định nghĩa hay thân hàm là nơi đặt các đoạn code
của hàm.

2. Định nghĩa hàm


Hàm có dạng sau:
return_type function_name (parameter list) {
body of the function
}
Trong đó:
Return Type – Hàm phải trả lại một giá trị nào đó.
return_type là kiểu của giá trị hàm trả về. Một số hàm
có thể thực hiện công việc nào đó mà không trả lại giá trị
nào, khi đó kiểu trả về là void.
48
Function Name – Tên của hàm (là xâu ký tự không
trùng từ khóa). Ngay sau tên là danh sách tham số.
Parameters − Tham số là giá trị đầu vào của hàm.
Trong danh sách tham số. Mỗi tham số được khai báo
dưới dạng: kiểu tham số, tên tham số. Các tham số cách
nhau một dấu cách. Chú ý hàm có thể không có tham số.
Thân hàm: Chứa các câu lệnh bên trong hàm.
Ví dụ
dụ
Hàm số nguyên tố.

3. Gọi hàm
Sau khi tạo ra hàm, bạn chỉ cần gọi tên hàm (chú ý
truyền các tham số đúng kiểu như trong phần khai báo
hàm). Sau khi gọi, đoạn code trong thân hàm sẽ được
thực thi và kết quả (giá trị sau return trong thân hàm) sẽ
được trả về cho chương trình đoạn gọi hàm.

Ví dụ snt

Tham số
Các tham số truyền cho hàm cũng giống như các biến
khai báo bên trong hàm, sẽ bị hủy khi hàm kết thúc.
Có hai cách truyền tham số cho hàm
Tham trị
trị (call by value)
Phương pháp truyền tham số này là sao chép giá trị vào
tham số của hàm. Việc thay đổi giá trị trong ham không
làm thay đổi giá trị tham số.

49
Ví dụ xét hàm swap sau:

/* function definition to swap the values */


void swap(int x, int y) {

int temp;

temp = x; /* save the value of x */


x = y; /* put y into x */
y = temp; /* put temp into y */

return;
}

Sử dụng hàm swap bằng cách truyền tham số cụ thể

#include <stdio.h>

/* function declaration */
void swap(int x, int y);

int main () {

/* local variable definition */


int a = 100;
int b = 200;

printf("Before swap, value of a : %d\n", a


);
printf("Before swap, value of b : %d\n", b
);

/* calling a function to swap the values


*/
swap(a, b);

printf("After swap, value of a : %d\n", a


);

50
printf("After swap, value of b : %d\n", b
);

return 0;
}
Sau khi dịch và chạy, chương trình in ra
Before swap, value of a :100
Before swap, value of b :200
After swap, value of a :100
After swap, value of b :200

Như vậy dù trong hàm, có thay đổi 2 giá trị, nhưng sau
khi gọi hàm, giá trị 2 biến không bị thay đổi.

Tham biế
biến (call by reference)
Phương pháp này sao chép địa chỉ cho tham số của hàm.
Trong thân hàm, có thể truy cập vào tham số nên có thể
thay đổi giá trị thật của tham số. Thay đổi giá trị tham số
làm thay đổi giá trị của tham số truyền vào.
Trong phương pháp này, con trỏ được sử dụng để truyền.
Chú ý khi khao báo, kiểu tham số truyền vào phải là kiểu
con trỏ. Xét ví dụ sau đây
/* hoán đổi giá trị 2 biến */
void swap(int *x, int *y) {

int temp;
temp = *x; /* lưu tạm địa chỉ biến x */
*x = *y; /* trỏ y vào x */
*y = temp; /* trỏ temp vào y */
return;
}

Trong chương trình chính, nếu gọi swap()

51
#include <stdio.h>

/* khai báo hàm */


void swap(int *x, int *y);

int main () {
/* các biến cục bộ */
int a = 100;
int b = 200;

printf("Before swap, value of a : %d\n", a


);
printf("Before swap, value of b : %d\n", b
);

/* gọi hàm, &a, &b là địa chỉ 2 biến a và


b.
*/
swap(&a, &b);

printf("After swap, value of a : %d\n", a


);
printf("After swap, value of b : %d\n", b
);

return 0;
}

Khi chạy

Before swap, value of a :100


Before swap, value of b :200
After swap, value of a :200
After swap, value of b :100
Thay đổi bên trong hàm sẽ ảnh hưởng đến giá trị bên
ngoài.

52

You might also like