You are on page 1of 59

Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

MỤC LỤC
I. Những khái niệm cơ bản trong ngôn ngữ lập trình C:................................ 3
1. Cấu trúc cơ bản của một chương trình C:....................................................3
2. Biến và hằng:............................................................................................... 4
a) Biến:....................................................................................................... 4
b) Hằng:...................................................................................................... 6
3. Kiểu dữ liệu:................................................................................................ 7
a) Kiểu dữ liệu số nguyên:..........................................................................8
b) Kiểu dữ liệu số thực:.............................................................................. 8
c) Kiểu dữ liệu ký tự:..................................................................................9
d) Kiểu boolean:....................................................................................... 11
4. Nhập/xuất cơ bản trong C:......................................................................... 11
a) Nhập/xuất chuẩn:.................................................................................. 11
b) Nhập/xuất với file:................................................................................13
5. Comment trong C:..................................................................................... 14
6. Câu lệnh đơn và khối lệnh:........................................................................ 15
7. Toán tử:...................................................................................................... 15
a) Toán tử số học:..................................................................................... 16
b) Toán tử quan hệ:................................................................................... 17
c) Toán tử gán:.......................................................................................... 18
d) Toán tử logic:........................................................................................19
e) Toán tử bitwise:.................................................................................... 20
f) Toán tử con trỏ:..................................................................................... 22
II. Cấu trúc điều khiển:.................................................................................... 23
1. Cấu trúc rẽ nhánh:......................................................................................23
a) Cấu trúc if-else:.................................................................................... 23
b) Cấu trúc switch-case:........................................................................... 25
2. Cấu trúc lặp:...............................................................................................28
a) Vòng lặp while:.................................................................................... 28
b) Vòng lặp do-while:...............................................................................29
c) Vòng lặp for:.........................................................................................30
3. Câu lệnh break và continue:...................................................................... 32
a) Câu lệnh break:.....................................................................................32
b) Câu lệnh continue:................................................................................33

1
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

4. Câu lệnh goto:............................................................................................34


III. Hàm trong C:.............................................................................................. 35
1. Hàm và các thành phần của hàm:.............................................................. 35
2. Hàm trong thư viện chuẩn:........................................................................ 37
IV. Cấu trúc dữ liệu mảng:...............................................................................39
1. Mảng 1 chiều:............................................................................................ 40
2. Mảng đa chiều:.......................................................................................... 41
V. Thuật toán sắp xếp và tìm kiếm:.................................................................42
1. Thuật toán sắp xếp nổi bọt (Bubble sort):................................................. 42
2. Thuật toán tìm kiếm tuần tự (Linear search):............................................ 44
3. Thuật toán tìm kiếm nhị phân (Binary search): (Tìm hiểu thêm trong tài
liệu Cấu trúc dữ liệu & giải thuật)................................................................. 44
VI. Xâu ký tự:....................................................................................................45
1. Cơ bản về xâu ký tự (chuỗi) trong C:........................................................ 45
2. Mảng ký tự 2 chiều trong C:......................................................................46
3. Các hàm xử lý ký tự:..................................................................................46
4. Các hàm xử lý xâu ký tự:...........................................................................48
5. Tách xâu ký tự:.......................................................................................... 49
VII. Con trỏ:...................................................................................................... 51
1. Biến và địa chỉ:.......................................................................................... 51
2. Con trỏ:...................................................................................................... 51
3. Mối quan hệ giữa con trỏ và mảng 1 chiều:.............................................. 52
4. Toán tử tăng/giảm của con trỏ:.................................................................. 54
VIII. Kiểu dữ liệu tự định nghĩa - struct:....................................................... 55

2
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

TÓM TẮT LÝ THUYẾT


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

I. Những khái niệm cơ bản trong ngôn ngữ lập trình C:


1. Cấu trúc cơ bản của một chương trình C:
- Một chương trình C cơ bản gồm các phần sau đây:
+ Các lệnh tiền xử lý

+ Các hàm

+ Các biến

+ Các lệnh và biểu thức

+ Các comment

3
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

2. Biến và hằng:
a) Biến:
- Trong lập trình, một biến là một thùng chứa (vùng lưu trữ) để chứa
dữ liệu.
- Để chỉ ra khu vực lưu trữ, mỗi biến phải được đặt một tên duy nhất
(mã định danh). Tên biến chỉ là đại diện tượng trưng của một vị trí
trong bộ nhớ.
- Khai báo biến: <Kiểu dữ liệu> <tên biến>;

- Nếu muốn khai báo 1 danh sách các biến có cùng kiểu dữ liệu, tên các
biến được ngăn cách bởi dấu ‘,’.

- Các quy tắc đặt tên cho biến:


+ Một tên biến chỉ có thể có các chữ cái (cả chữ hoa và chữ thường ),
chữ số và dấu gạch dưới.
+ Chữ cái đầu tiên của một biến phải là chữ cái hoặc dấu gạch dưới.
+ Không có quy tắc về độ dài tối đa của tên biến. Tuy nhiên, bạn có
thể gặp vấn đề trong một số trình biên dịch nếu tên biến dài hơn 31
ký tự.
+ Chú ý: Bạn nên luôn cố gắng đặt tên có ý nghĩa cho các biến. Ví
dụ: firstName là một tên biến tốt hơn fn.
+ Không đặt tên biến trùng với từ khóa trong C.

4
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- C là một ngôn ngữ có kiểu dữ liệu mạnh. Điều này có nghĩa là kiểu dữ
liệu của biến không thể thay đổi sau khi nó được khai báo.

- C là một ngôn ngữ phân biệt chữ hoa và chữ thường nên biến a và biến A
là 2 biến khác nhau.
- Một số quy tắc ngầm trong việc đặt tên biến:
+ Sử dụng tên biến có ý nghĩa: Đặt tên biến sao cho nó thể hiện rõ
mục đích và ý nghĩa của biến. Tránh sử dụng các tên biến vô nghĩa
như a, b, x, y.
+ Sử dụng chữ cái viết thường: Đặt tên biến bằng chữ cái viết thường
và sử dụng dấu gạch dưới (_) để phân cách các từ trong tên biến (ví
dụ: my_variable).
+ Sử dụng kiểu camelCase: Trong trường hợp tên biến bao gồm nhiều
từ, sử dụng kiểu camelCase (chữ cái đầu tiên của từ đầu tiên viết
thường, các chữ cái đầu tiên của các từ sau viết hoa, không có dấu
cách hoặc dấu gạch dưới). Ví dụ: myVariable.
+ Tránh sử dụng tên biến quá dài: Tránh đặt tên biến quá dài và phức
tạp, vì nó có thể làm cho code khó đọc và dễ gây nhầm lẫn.
- Phân loại biến: Trong ngôn ngữ lập trình C, có hai loại biến là biến toàn
cục (global variable) và biến cục bộ (local variable).
+ Biến toàn cục: là biến được khai báo bên ngoài bất kỳ hàm nào
trong chương trình và có phạm vi hoạt động trên toàn bộ các hàm
trong cùng một tệp nguồn (file).
+ Biến cục bộ: là biến được khai báo bên trong một khối lệnh, chẳng
hạn như bên trong một hàm, một khối lặp hoặc một khối rẽ nhánh.

5
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

Biến cục bộ chỉ có phạm vi hoạt động và thời gian sống trong khối
lệnh mà nó được khai báo.

b) Hằng:
- Trong ngôn ngữ lập trình C, literal (hằng số) là giá trị cố định được trực
tiếp nhập vào trong mã chương trình. Chúng là các giá trị không thay đổi
và được đại diện bởi các dạng cụ thể, chẳng hạn như số nguyên, số thực,
ký tự và chuỗi.
- Để khai báo một hằng số, ta thêm từ khóa const vào trước kiểu dữ liệu của
biến.

- Ngoài ra, chúng ta cũng có thể định nghĩa hằng số thông qua chỉ thị tiền
xử lý #define.

6
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Quy tắc đặt tên cho hằng cũng giống như quy tắc đặt tên cho biến.
- Quy tắc ngầm trong việc đặt tên cho hằng:
+ Sử dụng chữ in hoa: Đặt tên hằng bằng chữ in hoa và sử dụng dấu
gạch dưới (_) để phân cách các từ trong tên hằng (ví dụ:
MAX_VALUE).
+ Sử dụng chữ cái viết hoa: Đặt tên hằng bằng chữ cái viết hoa và sử
dụng dấu gạch dưới (_) để phân cách các từ trong tên hằng (ví dụ:
PI_VALUE).
+ Đặt tên hằng có ý nghĩa: Đặt tên hằng sao cho nó thể hiện rõ mục
đích và ý nghĩa của hằng. Ví dụ, sử dụng tên hằng như
"MAX_LENGTH" hoặc "DEFAULT_VALUE".
+ Đặt tên hằng mô tả loại dữ liệu: Đặt tên hằng sao cho nó mô tả loại
dữ liệu mà hằng đại diện. Ví dụ, sử dụng tên hằng như
"SECONDS_PER_MINUTE" hoặc "MONTHS_IN_YEAR".
3. Kiểu dữ liệu:

* Trong ngôn ngữ lập trình C, các kiểu dữ liệu được sử dụng để khai báo biến.
Điều này xác định kiểu và kích thước dữ liệu liên quan đến các biến.

* Trong C, các kiểu dữ liệu cung cấp thông tin cho trình biên dịch về tính chất
của dữ liệu được lưu trữ, cho phép nó cấp phát đúng lượng bộ nhớ và thực hiện
các phép toán trên dữ liệu.

* Có một số kiểu dữ liệu cơ bản trong C, bao gồm: Kiểu dữ liệu số nguyên, số
thực, ký tự, kiểu mảng, con trỏ, kiểu dữ liệu tự định nghĩa, … Trong phần này,

7
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

chúng ta sẽ tìm hiểu về 3 kiểu dữ liệu cơ bản nhất trong C: Kiểu số nguyên, số
thực và kiểu ký tự.
* Đặc tả: Trong ngôn ngữ lập trình C, format specifier (đặc tả) là một chuỗi ký
tự được sử dụng để xác định kiểu dữ liệu của một giá trị khi hiển thị hoặc đọc từ
đầu vào/đầu ra.
a) Kiểu dữ liệu số nguyên:
- Kiểu số nguyên (Integers): Được sử dụng để biểu diễn số nguyên. Ví dụ:
int, short, long long, char, …
- Thông tin của 3 kiểu dữ liệu số nguyên thường dùng:

Kiểu dữ liệu Kích thước Tập giá trị Đặc tả

char 1 byte [-27; 27 - 1] %c

int 4 bytes [-231; 231 - 1] %d, %i

long long 8 bytes [-263; 263 - 1] %lld

b) Kiểu dữ liệu số thực:


- Kiểu số thực (Floating-Point): Được sử dụng để biểu diễn số thập phân.
Ví dụ bao gồm float và double.
- Thông tin của 2 kiểu dữ liệu số thực thường dùng:

Kiểu dữ liệu Kích thước Độ chính xác Đặc tả

float 4 bytes Xấp xỉ 6 chữ số thập phân %f

double 8 bytes Xấp xỉ 15 chữ số thập phân %lf

8
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Kiểu double thường được sử dụng nhiều hơn kiểu float vì nó cung cấp độ
chính xác cao hơn và khả năng lưu trữ các giá trị lớn hơn. Tuy nhiên, kiểu
float vẫn có ứng dụng trong các trường hợp cần tiết kiệm bộ nhớ hoặc yêu
cầu độ chính xác không cao.
- Khi thực hiện các phép toán số học với các biến kiểu float và double, cần
chú ý rằng có thể xảy ra sai số độ chính xác do cách biểu diễn số thực
trong máy tính. Điều này có thể gây ra các hiện tượng như làm tròn, tràn
số hay mất chính xác trong kết quả tính toán.
c) Kiểu dữ liệu ký tự:
- Kiểu ký tự (Characters): Được sử dụng để biểu diễn các ký tự riêng lẻ.
Kiểu dữ liệu char được sử dụng cho mục đích này.
- Bảng mã ASCII (American Standard Code for Information Interchange)
là một bảng mã ký tự tiêu chuẩn được sử dụng để biểu diễn các ký tự
trong ngôn ngữ. Bảng mã ASCII gồm 128 ký tự, bao gồm các chữ cái,
chữ số, ký hiệu và các ký tự đặc biệt.
- Mỗi ký tự trong bảng mã ASCII được đại diện bởi một số nguyên từ 0
đến 127. Ví dụ, ký tự 'A' có giá trị ASCII là 65, 'a' có giá trị ASCII là 97,
và ký tự '@' có giá trị ASCII là 64. Bạn có thể sử dụng giá trị ASCII để
thực hiện các phép toán và so sánh trên các ký tự.
- Ví dụ, để khai báo một biến char và gán giá trị ký tự cho nó, bạn có thể sử
dụng như sau:

9
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Bảng mã ASCII cung cấp một cách tiêu chuẩn để biểu diễn các ký tự
trong ngôn ngữ, giúp trao đổi dữ liệu giữa các hệ thống và xử lý các chuỗi
ký tự một cách đồng nhất.
- Trong ngôn ngữ lập trình C, có một số ký tự đặc biệt được gọi là "ký tự
điều khiển" (control characters) được sử dụng để điều khiển luồng và định
dạng dữ liệu. Dưới đây là một số ký tự điều khiển phổ biến trong C:

Ký tự Tác dụng

'\n' Ký tự này được sử dụng để di chuyển con trỏ xuống dòng mới trong một chuỗi
ký tự hoặc tập tin đang được xử lý. Nó thường được sử dụng để in ra một dòng
mới hoặc tạo ra định dạng theo dòng.

'\t' Ký tự này được sử dụng để tạo ra một khoảng cách ngang (tương đương 4
khoảng trống) giữa các ký tự. Nó thường được sử dụng để định dạng và căn lề dữ
liệu.

'\b' Ký tự này được sử dụng để xóa một ký tự ngay trước đó và di chuyển con trỏ về
vị trí trước đó.

'\r' Ký tự này được sử dụng để di chuyển con trỏ về đầu dòng trong một chuỗi ký tự
hoặc tập tin đang được xử lý.

'\”' Ký tự \ được sử dụng để định dạng và biểu diễn một số ký tự đặc biệt khác trong
chuỗi ký tự. Ví dụ: '\”' biểu diễn ký tự ” trong chuỗi, bạn cũng có thể thay ” bằng
các ký tự đặc biệt khác.

10
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

d) Kiểu boolean:
- Trong ngôn ngữ lập trình C, boolean không phải kiểu dữ liệu nguyên thủy.
Tuy nhiên, ngôn ngữ C mặc định giá trị 0 là false và khác 0 là true
(thường là 1).
4. Nhập/xuất cơ bản trong C:
a) Nhập/xuất chuẩn:
- Trong ngôn ngữ lập trình C, stdio.h (stdio: standard input/output) là một
trong những header file chuẩn được cung cấp bởi C Standard Library.
Header file này chứa các định nghĩa và khai báo liên quan đến nhập xuất
chuẩn trong C.
- Hàm printf() là một hàm trong ngôn ngữ lập trình C được định nghĩa
trong header file stdio.h. Nó được sử dụng để in các định dạng được định
trước ra màn hình hoặc ghi vào một luồng xuất chuẩn.
- Cú pháp của hàm printf() như sau:

- Giá trị trả về: Hàm printf() trả về giá trị tương ứng với số ký tự đã được in
thành công. Nếu có lỗi xảy ra trong quá trình in, hàm sẽ trả về một giá trị
âm.

11
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Hàm scanf() là một hàm trong ngôn ngữ lập trình C được định nghĩa trong
header file stdio.h. Nó được sử dụng để đọc và gán giá trị từ bàn phím
hoặc từ một luồng nhập chuẩn khác vào các biến.
- Cú pháp của hàm scanf() như sau:

- Lưu ý: Để nhập giá trị cho biến, ta cần truyền vào địa chỉ của biến bằng
cách đặt toán tử & trước tên biến.
- Toán tử & (address-of operator) được sử dụng để lấy địa chỉ của một biến
trong bộ nhớ. Điều này cho phép hàm scanf() ghi giá trị nhập vào trực tiếp
vào địa chỉ của biến x, thay vì tạo một bản sao của biến.
- Chú ý rằng kiểu dữ liệu của biến và đặc tả định dạng trong hàm scanf()
phải khớp nhau. Ví dụ, để nhập một số nguyên, ta sử dụng %d trong
scanf(). Đối với kiểu dữ liệu khác như số thực %f, ký tự %c, hoặc chuỗi
%s, ta cần sử dụng đặc tả định dạng tương ứng.
- Nếu không truyền địa chỉ của biến bằng toán tử &, hàm scanf() sẽ không
biết nơi để ghi giá trị nhập vào và có thể gây ra lỗi không xác định hoặc
thay đổi giá trị của biến khác trong bộ nhớ.

12
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Giá trị trả về: Hàm scanf() trả về số lượng thành phần đã được gán giá trị
thành công. Nếu có lỗi xảy ra trong quá trình đọc, hàm sẽ trả về một giá
trị âm. Ta có thể lợi dụng tính chất này để đọc input với số lượng đầu vào
không biết trước:

b) Nhập/xuất với file:


- Trong ngôn ngữ C, ta có thể sử dụng hàm freopen() để thay đổi
hướng nhập xuất của tiêu chuẩn vào/ra (standard input/output) của
chương trình.
- Hàm freopen() có cú pháp như sau:

- Ngoài ra còn rất nhiều cách để nhập/xuất với file khác, nhưng cách
sử dụng hàm freopen() là cách đơn giản và thông dụng nhất.

13
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

5. Comment trong C:
- Comment trong ngôn ngữ lập trình C là một phần của mã nguồn không
được biên dịch hoặc thực thi. Nó được sử dụng để giải thích và ghi chú
cho mã nguồn, để làm cho mã dễ hiểu hơn cho người đọc và để tạm thời
vô hiệu hóa một phần của mã.
- Comment không ảnh hưởng đến việc biên dịch chương trình và không
được thực thi khi chương trình chạy. Nó chỉ là một phần của mã nguồn
dùng để giúp lập trình viên giải thích và ghi chú về mã.
- Có 2 loại comment trong C:
+ Comment trên 1 dòng: Bắt đầu bằng hai dấu gạch chéo ngược //.
Mọi nội dung sau dấu gạch chéo ngược trên cùng một dòng sẽ
được xem là comment.
+ Comment trên nhiều dòng: Bắt đầu bằng /* và kết thúc bằng */. Bất
kỳ nội dung nằm giữa hai cặp ký tự này sẽ được coi là comment.

- Comment giúp lập trình viên ghi chú, giải thích code, và làm cho mã
nguồn dễ đọc và dễ hiểu hơn cho người khác hoặc cho chính người viết
code sau này.

14
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

6. Câu lệnh đơn và khối lệnh:


- Trong ngôn ngữ lập trình C, câu lệnh đơn và khối lệnh là 2 khái niệm cơ
bản để thực hiện các hành động và điều khiển luồng của chương trình.
- Câu lệnh đơn (Single statement): Câu lệnh đơn chỉ gồm một dòng mã
lệnh. Nó thực hiện một hành động cụ thể và kết thúc bằng dấu chấm phẩy
‘;’. Dấu ‘;’ đánh dấu sự kết thúc 1 câu lệnh và ngăn cách câu lệnh này với
câu lệnh khác.
- Khối lệnh (Block statement): Khối lệnh là một nhóm câu lệnh đơn nằm
trong cặp dấu ngoặc nhọn {}. Các câu lệnh trong khối lệnh được thực
hiện tuần tự từ trên xuống dưới. Khối lệnh thường được sử dụng để nhóm
các câu lệnh lại với nhau và tạo thành một phạm vi thực thi (scope). Các
biến nằm trong các phạm vi thực thi khác nhau thì khác nhau.

7. Toán tử:

* Ngôn ngữ lập trình C cung cấp nhiều loại toán tử để thực hiện các phép tính
và thao tác trên dữ liệu.

15
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

* Số ngôi (unary) của toán tử là một thuộc tính miêu tả số lượng toán hạng mà
một toán tử sử dụng. Ví dụ: Toán tử chia (/): a / b ta thấy toán tử chia (/) hoạt
động với 2 toán hạng a và b nên toán tử chia (/) là toán tử 2 ngôi.

a) Toán tử số học:
- Trong ngôn ngữ lập trình C, có các toán tử số học để thực hiện các
phép tính số học trên các giá trị số:
+ Toán tử 2 ngôi cộng (+): Thực hiện phép cộng hai giá trị. Ví
dụ: a + b.
+ Toán tử 2 ngôi trừ (-): Thực hiện phép trừ hai giá trị. Ví dụ:
a - b.
+ Toán tử 2 ngôi nhân (*): Thực hiện phép nhân hai giá trị. Ví
dụ: a * b.
+ Toán tử 2 ngôi chia (/): Thực hiện phép chia hai giá trị, kết
quả sẽ trả về số thực khi và chỉ khi có ít nhất 1 toán hạng là
số thực. Không được thực hiện phép chia cho 0 nếu không
thì chương trình sẽ trả về lỗi thực thi. Ví dụ: a / b.
+ Toán tử 2 ngôi chia lấy phần dư (%): Trả về phần dư của
phép chia hai giá trị Không được thực hiện chia dư cho 0 nếu
không chương trình sẽ trả về lỗi thực thi. Ví dụ: a % b.
+ Toán tử 1 ngôi tăng (++): Tăng giá trị của biến lên một đơn
vị. Ví dụ: a++ hoặc ++a.
+ Toán tử 1 ngôi giảm (--): Giảm giá trị của biến đi một đơn vị.
Ví dụ: a-- hoặc --a.
- So sánh ++a và a++:
+ (++a) (prefix increment): Đây là toán tử tăng giá trị của biến
a trước khi sử dụng giá trị đó trong biểu thức. Nó được gọi là

16
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

prefix vì nó được đặt trước tên biến. Kết quả của ++a là giá
trị mới của biến a sau khi đã tăng.
+ (a++) (postfix increment): Đây là toán tử tăng giá trị của
biến a sau khi đã sử dụng giá trị cũ trong biểu thức. Nó được
gọi là postfix vì nó được đặt sau tên biến. Kết quả của a++ là
giá trị cũ của biến a trước khi tăng.
+ Tóm lại, khác biệt chính giữa ++a và a++ là trong thời điểm
mà giá trị của biến được tăng. ++a tăng giá trị trước khi sử
dụng trong biểu thức, trong khi a++ tăng giá trị sau khi sử
dụng.
- Các toán tử số học này cho phép thực hiện các phép tính số học cơ
bản như cộng, trừ, nhân, chia và lấy phần dư. Chúng có thể được
sử dụng trên các kiểu dữ liệu số như int, float, double để thực hiện
các phép tính và thao tác trên các giá trị số.
b) Toán tử quan hệ:
- Trong ngôn ngữ lập trình C, toán tử quan hệ được sử dụng để so
sánh giữa hai giá trị và trả về kết quả là một giá trị boolean
(true/false).
- Các loại toán tử quan hệ trong C:
+ Toán tử 2 ngôi == (bằng): So sánh hai giá trị có bằng nhau
hay không.
+ Toán tử 2 ngôi != (không bằng): So sánh hai giá trị có khác
nhau hay không.
+ Toán tử 2 ngôi > (lớn hơn): Kiểm tra xem giá trị bên trái có
lớn hơn giá trị bên phải hay không.
+ Toán tử 2 ngôi < (nhỏ hơn): Kiểm tra xem giá trị bên trái có
nhỏ hơn giá trị bên phải hay không.

17
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

+ Toán tử 2 ngôi >= (lớn hơn hoặc bằng): Kiểm tra xem giá trị
bên trái có lớn hơn hoặc bằng giá trị bên phải hay không.
+ Toán tử 2 ngôi <= (nhỏ hơn hoặc bằng): Kiểm tra xem giá trị
bên trái có nhỏ hơn hoặc bằng giá trị bên phải hay không.
- Kết quả của các toán tử quan hệ là một giá trị boolean, có thể là 1
(true) nếu biểu thức so sánh là đúng và 0 (false) nếu biểu thức so
sánh là sai.
- Toán tử quan hệ thường được sử dụng trong các câu lệnh điều
khiển điều kiện như if, while, for để kiểm tra điều kiện và thực hiện
các hành động tương ứng.
c) Toán tử gán:
- Trong ngôn ngữ lập trình C, toán tử gán được sử dụng để gán giá
trị cho một biến. Cú pháp chung của toán tử gán là =.
- Có thể sử dụng các toán tử khác trong kết hợp với toán tử gán để
thực hiện các phép toán và gán giá trị cho biến trong một bước.
- Các loại toán tử gán:
+ Toán tử 2 ngôi = : Gán giá trị từ biểu thức bên phải cho biến
bên trái.
+ Toán tử 2 ngôi += : Cộng giá trị bên phải vào giá trị hiện tại
của biến và gán kết quả cho biến.
+ Toán tử 2 ngôi -= : Trừ giá trị bên phải khỏi giá trị hiện tại
của biến và gán kết quả cho biến.
+ Toán tử 2 ngôi *=: Nhân giá trị bên phải với giá trị hiện tại
của biến và gán kết quả cho biến.
+ Toán tử 2 ngôi /=: Chia giá trị hiện tại của biến cho giá trị
bên phải và gán kết quả cho biến.

18
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

+ Toán tử 2 ngôi %=: Lấy phần dư của giá trị hiện tại của biến
khi chia cho giá trị bên phải và gán kết quả cho biến.
- Toán tử gán là một cách tiện lợi để thực hiện các phép toán và gán
giá trị cho biến trong cùng một biểu thức.
d) Toán tử logic:
- Trong ngôn ngữ lập trình C, toán tử logic được sử dụng để thực
hiện các phép logic trên các biểu thức hoặc giá trị logic. Các toán
tử logic thường được sử dụng để kiểm tra và đánh giá các điều kiện
trong các câu lệnh điều kiện và vòng lặp. Giá trị trả về của biểu
thức sử dụng toán tử logic là boolean (true/false).
- Dưới đây là các toán tử logic phổ biến trong C:
+ Toán tử 2 ngôi && (AND): Trả về giá trị true nếu cả hai biểu
thức bên trái và bên phải đều đúng, ngược lại trả về giá trị
false. Ví dụ: a && b.
+ Toán tử 2 ngôi || (OR): Trả về giá trị true nếu ít nhất một
trong hai biểu thức bên trái và bên phải là đúng, ngược lại trả
về giá trị false. Ví dụ: a || b.
+ Toán tử 1 ngôi ! (NOT): Phủ định giá trị của một biểu thức.
Trả về giá trị true nếu biểu thức là sai, và trả về giá trị false
nếu biểu thức là đúng. Ví dụ: !a.
- Lưu ý rằng các toán tử logic thực hiện theo quy tắc đánh đúng
(short-circuit evaluation). Điều này có nghĩa là nếu kết quả cuối
cùng có thể được xác định từ các biểu thức gần nhất, các biểu thức
xa hơn sẽ không được thực hiện:
+ Quy tắc đánh đúng cho toán tử && (AND): Nếu biểu thức
bên trái của && là sai (giá trị false), thì biểu thức bên phải sẽ
không được đánh giá, vì kết quả cuối cùng sẽ là sai. Tuy

19
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

nhiên, nếu biểu thức bên trái là đúng (giá trị true), thì biểu
thức bên phải sẽ được đánh giá để xác định kết quả cuối
cùng.
+ Quy tắc đánh đúng cho toán tử || (OR): Nếu biểu thức bên
trái của || là đúng (giá trị true), thì biểu thức bên phải sẽ
không được đánh giá, vì kết quả cuối cùng sẽ là đúng. Tuy
nhiên, nếu biểu thức bên trái là sai (giá trị false), thì biểu
thức bên phải sẽ được đánh giá để xác định kết quả cuối
cùng.
- Ngoài ra ta có thể sử dụng biểu thức hoặc hàm có trả về giá trị để
làm toán hạng với toán tử logic:

e) Toán tử bitwise:
- Trong ngôn ngữ lập trình C, toán tử bitwise (bitwise operators)
được sử dụng để thực hiện các phép toán bit trên các giá trị số
nguyên. Các toán tử bitwise hoạt động trực tiếp trên các bit của các
giá trị, từ bit thấp nhất đến bit cao nhất.
- Danh sách các toán tử bitwise trong C:

20
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

+ Toán tử 2 ngôi AND bitwise (&): Thực hiện phép AND


logic bit-by-bit giữa hai giá trị. Kết quả sẽ có giá trị 1 chỉ khi
cả hai bit tương ứng đều bằng 1, ngược lại sẽ là 0.
+ Toán tử 2 ngôi OR bitwise (|): Thực hiện phép OR logic
bit-by-bit giữa hai giá trị. Kết quả sẽ có giá trị 1 nếu ít nhất
một trong hai bit tương ứng là 1, ngược lại sẽ là 0.
+ Toán tử 2 ngôi XOR bitwise (^): Thực hiện phép XOR logic
bit-by-bit giữa hai giá trị. Kết quả sẽ có giá trị 1 chỉ khi các
bit tương ứng khác nhau, ngược lại sẽ là 0.
+ Toán tử 1 ngôi NOT bitwise (~): Thực hiện phép đảo bit
(bitwise negation) trên một giá trị. Kết quả là giá trị đảo bit
của giá trị ban đầu.
+ Toán tử 2 ngôi Left shift (<<): Dịch trái các bit của một giá
trị theo số lượng bit được xác định. Các bit mới được chèn
vào từ phải với các bit trái nhất được đặt thành 0.
+ Toán tử 2 ngôi Right shift (>>): Dịch phải các bit của một
giá trị theo số lượng bit được xác định. Các bit mới được
chèn vào từ trái với các bit phải nhất được đặt thành 0.
- Các toán tử bitwise được sử dụng để thao tác trực tiếp với các bit
của các giá trị số nguyên, ví dụ như thay đổi, kiểm tra hoặc trích
xuất các bit cụ thể từ các giá trị.
- Các biểu thức với toán tử bitwise thường dùng:

21
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Các toán tử bitwise cũng có thể kết hợp với toán tử gán để thực
hiện các phép toán bitwise và gán giá trị vào biến. Dưới đây là các
toán tử bitwise kết hợp với toán tử gán trong C:
+ Bitwise AND và gán (&=): Toán tử &= thực hiện phép AND
bitwise giữa hai giá trị và gán kết quả vào biến bên trái. Ví
dụ: a &= b; tương đương với a = a & b;.
+ Bitwise OR và gán (|=): Toán tử |= thực hiện phép OR
bitwise giữa hai giá trị và gán kết quả vào biến bên trái. Ví
dụ: a |= b; tương đương với a = a | b;.
+ Bitwise XOR và gán (^=): Toán tử ^= thực hiện phép XOR
bitwise giữa hai giá trị và gán kết quả vào biến bên trái. Ví
dụ: a ^= b; tương đương với a = a ^ b;.
+ Left shift và gán (<<=): Toán tử <<= thực hiện phép dịch trái
bitwise của giá trị và gán kết quả vào biến bên trái. Ví dụ:
a <<= 1; tương đương với a = a << 1;.
+ Right shift và gán (>>=): Toán tử >>= thực hiện phép dịch
phải bitwise của giá trị và gán kết quả vào biến bên trái. Ví
dụ: a >>= 1; tương đương với a = a >> 1;.
f) Toán tử con trỏ:
- Trong ngôn ngữ lập trình C, toán tử con trỏ được sử dụng để khai
báo con trỏ và thực hiện các phép toán liên quan đến con trỏ.
- Các toàn tử con trỏ thường dùng:
+ Toán tử 1 ngôi * (con trỏ): Được sử dụng để khai báo con trỏ
và truy cập giá trị mà con trỏ đang trỏ tới. Ví dụ: int* ptr;
khai báo một con trỏ kiểu int và *ptr truy cập giá trị của biến
mà con trỏ ptr đang trỏ tới.

22
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

+ Toán tử 1 ngôi & (địa chỉ): Được sử dụng để lấy địa chỉ của
một biến. Ví dụ: int a = 10; int* ptr = &a; gán địa chỉ của
biến a cho con trỏ ptr.
+ Toán tử 2 ngôi -> (truy cập đến giá trị): Được sử dụng để
truy cập thành viên của một biến con trỏ trỏ tới, thường là
thành viên của một cấu trúc.
- Các toán tử con trỏ trong ngôn ngữ C cho phép bạn làm việc với
địa chỉ và giá trị của biến, đồng thời cung cấp khả năng truy cập
các thành viên của cấu trúc thông qua biến con trỏ.
II. Cấu trúc điều khiển:
1. Cấu trúc rẽ nhánh:

* Cấu trúc rẽ nhánh (branching structure) là một phần của ngôn ngữ lập trình
cho phép thực hiện các hành động khác nhau dựa trên điều kiện hoặc giá trị của
biến. Cấu trúc rẽ nhánh cho phép chương trình thực hiện các hành động khác
nhau tùy thuộc vào các điều kiện được đưa ra.

a) Cấu trúc if-else:


- Cấu trúc if-else trong ngôn ngữ lập trình C cho phép thực hiện một
khối lệnh nếu một điều kiện được đưa ra là đúng và một khối lệnh
khác nếu điều kiện sai. Nếu điều kiện phía trên đúng thì chương
trình sẽ coi như các điều kiện phía dưới (điều kiện của else if và
else) là sai và sẽ không thực hiện kiểm tra. Dưới đây là cú pháp và
cách sử dụng của cấu trúc if-else:

23
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

24
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

b) Cấu trúc switch-case:


- Cấu trúc switch-case trong C được sử dụng để thực hiện một lựa
chọn đa trường hợp dựa trên giá trị của một biểu thức. Cú pháp
chung của cấu trúc switch-case như sau:

25
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Cách hoạt động của cấu trúc switch-case như sau:


+ Biểu thức trong switch sẽ được kiểm tra và so sánh với các
giá trị trong các trường hợp case.
+ Nếu giá trị của biểu thức trùng khớp với giá trị trong một
trường hợp case, khối lệnh bên trong trường hợp đó sẽ được
thực thi.

26
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

+ Sau khi thực thi xong khối lệnh của một trường hợp case,
chương trình sẽ thoát khỏi cấu trúc switch-case nếu gặp lệnh
break.
+ Trường hợp default sẽ được thực thi khi không có trường
hợp nào khớp với giá trị của biểu thức.
+ Lệnh break được sử dụng để thoát khỏi cấu trúc switch-case
và ngăn chặn việc thực thi các khối lệnh tiếp theo.
+ Nếu trong một trường hợp case không có lệnh break,
chương trình sẽ tiếp tục thực hiện các khối lệnh của các
trường hợp case tiếp theo mà không kiểm tra điều kiện.
Điều này được gọi là "rơi vào" (fall-through) giữa các
trường hợp case.
+ Khi rơi vào giữa các trường hợp case, các khối lệnh của các
trường hợp case sau nó sẽ được thực thi tuần tự cho đến khi
gặp lệnh break hoặc kết thúc cấu trúc switch-case.
+ Đôi khi việc sử dụng rơi vào giữa các trường hợp case có thể
được sử dụng để thực hiện một loạt các trường hợp có cùng
một khối lệnh hoặc để thực hiện một số logic phức tạp hơn.
Tuy nhiên, cần chú ý rằng việc sử dụng rơi vào có thể làm
cho mã nguồn trở nên khó hiểu và dễ gây nhầm lẫn. Do đó,
nếu không có nhu cầu đặc biệt, nên luôn sử dụng lệnh break
để thoát khỏi cấu trúc switch-case sau khi thực thi xong khối
lệnh của một trường hợp case.

27
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

2. Cấu trúc lặp:

* Cấu trúc lặp trong ngôn ngữ lập trình C là một cách để thực hiện việc lặp lại
một khối lệnh nhiều lần, dựa trên một điều kiện được xác định. Cấu trúc lặp cho
phép chương trình thực hiện một tác vụ lặp đi lặp lại cho đến khi điều kiện
không còn đúng.

* Cần chú ý điều kiện lặp có điểm dừng để vòng lặp không bị rơi vào tình trạng
lặp vô hạn. Vòng lặp vô hạn (infinite loop) là một loại vòng lặp mà điều kiện để
thoát khỏi vòng lặp không bao giờ trở thành sai. Trong ngôn ngữ lập trình C,
vòng lặp vô hạn thường được tạo ra bằng cách sử dụng một điều kiện luôn luôn
đúng hoặc không có điều kiện thoát khỏi vòng lặp.

a) Vòng lặp while:


- Vòng lặp while trong ngôn ngữ lập trình C được sử dụng để lặp lại
một khối lệnh cho đến khi một điều kiện cụ thể là đúng. Cú pháp
của vòng lặp while như sau:

- Cách thức hoạt động:


+ Kiểm tra điều kiện: Đầu tiên, điều kiện được kiểm tra. Nếu
điều kiện là đúng (trả về giá trị khác 0), khối lệnh bên trong

28
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

vòng lặp sẽ được thực thi. Nếu điều kiện là sai (trả về giá trị
bằng 0), vòng lặp sẽ dừng và chương trình tiếp tục thực hiện
các câu lệnh sau vòng lặp while.
+ Thực thi khối lệnh: Nếu điều kiện là đúng, khối lệnh bên
trong vòng lặp sẽ được thực thi. Điều này có thể bao gồm
một hoặc nhiều câu lệnh. Khối lệnh này sẽ được lặp lại cho
đến khi điều kiện trở thành sai.
+ Cập nhật điều kiện: Sau khi thực thi khối lệnh, điều kiện
được kiểm tra lại. Nếu điều kiện vẫn là đúng, vòng lặp sẽ
tiếp tục lặp lại từ bước 2. Nếu điều kiện trở thành sai, vòng
lặp sẽ dừng và chương trình tiếp tục thực hiện các câu lệnh
sau vòng lặp while.
- Lưu ý rằng nếu điều kiện ban đầu là sai, vòng lặp while sẽ không
được thực thi và chương trình sẽ bỏ qua khối lệnh bên trong vòng
lặp.
- Vòng lặp while thường được sử dụng khi số lần lặp không xác định
trước hoặc dựa trên một điều kiện cụ thể.
b) Vòng lặp do-while:
- Vòng lặp do-while trong ngôn ngữ lập trình C là một loại vòng lặp
mà khối lệnh sẽ được thực thi ít nhất một lần trước khi kiểm tra
điều kiện để tiếp tục hoặc thoát khỏi vòng lặp. Cấu trúc của vòng
lặp do-while như sau:

29
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Đầu tiên, khối lệnh bên trong vòng lặp sẽ được thực thi một lần mà
không cần kiểm tra điều kiện. Sau đó, điều kiện sẽ được kiểm tra.
Nếu điều kiện là đúng, khối lệnh sẽ được thực thi tiếp tục. Nếu
điều kiện là sai, vòng lặp do-while sẽ kết thúc và quá trình thực thi
sẽ tiếp tục với các câu lệnh tiếp theo sau vòng lặp.
- Một điểm quan trọng của vòng lặp do-while là khối lệnh được thực
thi ít nhất một lần, ngay cả khi điều kiện ban đầu đã sai. Điều này
khác với vòng lặp while, trong đó khối lệnh chỉ được thực thi khi
điều kiện ban đầu là đúng.
c) Vòng lặp for:
- Vòng lặp for trong ngôn ngữ lập trình C được sử dụng để thực hiện
một khối lệnh lặp lại một số lần xác định. Cấu trúc chung của vòng
lặp for như sau:

30
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Các phần của vòng lặp for có ý nghĩa như sau:


+ Biểu thức khởi tạo: Đây là phần để khởi tạo giá trị ban đầu
cho biến điều khiển vòng lặp (thường là một biến đếm). Nó
được thực hiện chỉ một lần, trước khi vòng lặp bắt đầu. Có
thể khởi tạo nhiều biến có cùng kiểu dữ liệu, các biến phân
cách nhau bởi dấu ‘,’.
+ Điều kiện: Đây là biểu thức kiểm tra điều kiện để quyết định
xem vòng lặp có tiếp tục thực hiện hay không. Nếu điều kiện
là đúng (trả về giá trị khác 0), khối lệnh sẽ được thực thi.
Nếu điều kiện là sai (trả về giá trị bằng 0), vòng lặp kết thúc.
+ Biểu thức tăng/giảm: Đây là biểu thức được thực hiện sau
mỗi lần lặp của khối lệnh. Nó thường được sử dụng để thay
đổi giá trị của biến điều khiển vòng lặp. Có thể có nhiều biểu
thức, mỗi biểu thức phân cách nhau bởi dấu ‘,’.
+ Vòng lặp for() có thể khuyết 1, 2 hoặc cả 3 biểu thức trên,
nhưng vẫn phải đảm bảo có đủ 2 dấu ‘;’ trong vòng lặp.
- Vòng lặp for hoạt động theo các bước sau:
+ Bước 1 (Khởi tạo): Trước khi vòng lặp bắt đầu, biểu thức
khởi tạo được thực hiện. Điều này thường là để khởi tạo giá
trị ban đầu cho biến điều khiển vòng lặp (thường là một biến
đếm).
+ Bước 2: (Kiểm tra điều kiện): Sau mỗi lần lặp, điều kiện
được kiểm tra. Nếu điều kiện là đúng (khác 0), khối lệnh bên
trong vòng lặp được thực thi. Nếu điều kiện là sai (bằng 0),
vòng lặp kết thúc và chương trình tiếp tục thực hiện từ phần
code sau vòng lặp.

31
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

+ Bước 3 (Thực thi khối lệnh): Nếu điều kiện kiểm tra là đúng,
khối lệnh bên trong vòng lặp được thực thi. Đây là nơi bạn
đặt các câu lệnh và tác vụ muốn lặp lại một số lần.
+ Bước 4 (Thực hiện biểu thức tăng/giảm): Sau mỗi lần thực
thi khối lệnh, biểu thức tăng/giảm được thực hiện. Điều này
thường được sử dụng để thay đổi giá trị của biến điều khiển
vòng lặp. Sau đó, quá trình kiểm tra điều kiện lại được thực
hiện để quyết định xem vòng lặp có tiếp tục hay kết thúc.
+ Bước 5 (Lặp): Quá trình từ bước 2 đến bước 4 được lặp lại
cho đến khi điều kiện kiểm tra trở thành sai, khi đó vòng lặp
kết thúc.
- Với cấu trúc for, bạn có thể kiểm soát được số lần lặp, giá trị ban
đầu và các bước tăng/giảm của biến điều khiển vòng lặp. Điều này
rất hữu ích khi bạn muốn lặp lại một khối lệnh một số lần cụ thể
hoặc khi bạn muốn lặp qua một loạt giá trị với một biến đếm.
3. Câu lệnh break và continue:

* Câu lệnh break và continue là hai câu lệnh điều khiển được sử dụng trong các
vòng lặp trong ngôn ngữ lập trình C.

* Sử dụng câu lệnh break và continue có thể giúp kiểm soát quá trình lặp và
thay đổi luồng điều khiển trong vòng lặp.

a) Câu lệnh break:


- Câu lệnh break trong ngôn ngữ lập trình C được sử dụng để kết
thúc ngay lập tức một vòng lặp hoặc một khối lệnh switch-case.
Khi câu lệnh break được thực thi, nó sẽ ngừng thực hiện các câu

32
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

lệnh trong vòng lặp hoặc khối lệnh switch-case và thoát khỏi nó,
tiếp tục thực hiện các câu lệnh sau vòng lặp hoặc khối lệnh.

- Câu lệnh break thường được sử dụng trong các trường hợp sau:
+ Trong vòng lặp: Khi một điều kiện cụ thể được đáp ứng, ta
có thể sử dụng break để kết thúc vòng lặp sớm. Việc này
giúp tiết kiệm thời gian và ngừng thực hiện các lần lặp còn
lại của vòng lặp.
+ Trong khối lệnh switch-case: Mỗi trường hợp (case) trong
khối lệnh switch-case sẽ thực hiện một chuỗi câu lệnh. Bằng
cách sử dụng break ở cuối mỗi trường hợp, ta có thể kết thúc
khối lệnh switch-case sau khi thực hiện trường hợp tương
ứng và ngăn chặn việc thực hiện các trường hợp tiếp theo.
b) Câu lệnh continue:
- Câu lệnh continue trong ngôn ngữ lập trình C được sử dụng để bỏ
qua các câu lệnh còn lại trong vòng lặp hiện tại và chuyển đến lần
lặp tiếp theo. Khi câu lệnh continue được thực thi, các câu lệnh
trong vòng lặp sau câu lệnh continue sẽ không được thực hiện và
chương trình sẽ tiếp tục với lần lặp kế tiếp.
- Câu lệnh continue thường được sử dụng trong trường hợp: Trong
vòng lặp khi một điều kiện cụ thể được đáp ứng, ta có thể sử dụng
continue để bỏ qua các câu lệnh trong vòng lặp và chuyển đến lần

33
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

lặp tiếp theo. Việc này giúp loại bỏ các lần lặp không cần thiết và
tiếp tục với các lần lặp khác.

4. Câu lệnh goto:


- Câu lệnh goto trong ngôn ngữ lập trình C là một câu lệnh nhảy không
điều kiện, cho phép chương trình nhảy tới một điểm gắn nhãn (label)
được chỉ định. Câu lệnh goto thường được sử dụng để thoát khỏi một
khối lệnh hoặc thực hiện nhảy tới một điểm cụ thể trong chương trình.
- Cú pháp của câu lệnh goto như sau:

- Lưu ý rằng việc sử dụng câu lệnh goto có thể làm cho mã nguồn trở nên
khó hiểu và khó bảo trì, do đó nên sử dụng nó cẩn thận và chỉ khi thực sự
cần thiết.

34
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

III. Hàm trong C:


1. Hàm và các thành phần của hàm:
- Trong ngôn ngữ lập trình, hàm là một khối mã được đặt tên, thực hiện
một tác vụ cụ thể và có thể được gọi và sử dụng nhiều lần trong chương
trình. Hàm giúp tạo ra sự tái sử dụng mã và tăng tính tổ chức của chương
trình.
- Một hàm trong C (và cũng trong nhiều ngôn ngữ lập trình khác) bao gồm
các phần sau:
+ Kiểu trả về (return type): Là kiểu dữ liệu mà hàm trả về sau khi
thực hiện xong công việc của mình. Kiểu trả về có thể là kiểu
nguyên, kiểu số thực, kiểu ký tự, kiểu con trỏ, hoặc cũng có thể là
kiểu void nếu hàm không trả về giá trị nào.
+ Tên hàm: Là tên định danh duy nhất của hàm, được sử dụng để gọi
và truy cập vào hàm.
+ Đối số (parameters): Là các biến hoặc giá trị được truyền vào hàm
để thực hiện các tính toán hoặc xử lý. Đối số là tùy chọn, có thể
không có trong một hàm.
+ Khối lệnh (function body): Là phần mã lệnh thực hiện các công
việc cụ thể của hàm. Mã lệnh này được đặt trong một khối lệnh
được định nghĩa bằng cặp dấu ngoặc nhọn {}.
+ Lệnh return: Dùng để trả về kết quả từ hàm và chấm dứt việc thực
thi hàm. Lệnh return chỉ có thể xuất hiện trong hàm có kiểu trả về
khác void.
- Để sử dụng một hàm trong C, bạn cần thực hiện các bước sau:
+ Khai báo hàm: Bạn cần khai báo hàm cùng các tham số (nếu có) để
chương trình biết sự hiện diện của hàm trong chương trình, lúc này
bạn có thể định nghĩa hàm bất cứ đâu trong chương trình miễn là

35
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

sau phần khai báo. Bạn có thể bỏ qua bước khai báo hàm và có thể
định nghĩa hàm luôn, nhưng điều này dẫn đến bạn không được gọi
hàm trước khi định nghĩa hàm.
+ Định nghĩa hàm: Đầu tiên, bạn cần định nghĩa hàm bằng cách khai
báo kiểu trả về, tên hàm và các tham số (nếu có). Định nghĩa hàm
chứa một khối lệnh thực hiện các công việc cụ thể của hàm.
+ Gọi hàm: Sau khi hàm đã được định nghĩa, bạn có thể gọi hàm
bằng cách sử dụng tên hàm và truyền các đối số cần thiết (nếu có).
Khi gọi hàm, chương trình sẽ thực hiện các công việc trong khối
lệnh của hàm.

- Trong ngôn ngữ lập trình C, bạn có thể định nghĩa cho tham số mặc định
(default parameter) trong định nghĩa hàm. Khi bạn truyền đối số tương
ứng với tham số này, tham số đó sẽ nhận giá trị là đối số tương ứng, còn
nếu không thì tham số đó sẽ nhận giá trị mặc định mà bạn đã định nghĩa
trước đó. Lưu ý: Các tham số mặc định phải được định nghĩa sau cùng!

36
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Hàm đệ quy trong ngôn ngữ lập trình C là một hàm mà trong quá trình
thực thi có thể gọi lại chính nó. Kỹ thuật đệ quy rất hữu ích khi cần giải
quyết các bài toán được phân tách thành các bước nhỏ hơn, mỗi bước
tương tự như bài toán gốc. Cấu trúc của một hàm đệ quy bao gồm:
+ Điều kiện dừng: Một điều kiện được xác định để đảm bảo việc gọi
đệ quy sẽ dừng lại và trả về kết quả mong muốn. Điều kiện này
thường được đặt ở đầu hàm đệ quy để kiểm tra và trả về kết quả
ngay khi thỏa mãn điều kiện.
+ Gọi đệ quy: Trong thân hàm, gọi lại chính hàm đó với các giá trị
khác nhau để giải quyết bài toán trong quy mô nhỏ hơn. Các tham
số của hàm đệ quy thường thay đổi để tiến tới điều kiện dừng.
+ Xử lý kết quả: Kết quả từ các lời gọi đệ quy được kết hợp để đạt
được kết quả cuối cùng của bài toán.
+ Phần này tìm hiểu thêm trong tài liệu Cấu trúc dữ liệu và giải thuật
nhé!
2. Hàm trong thư viện chuẩn:
- Thư viện chuẩn C (Standard Library) cung cấp một tập hợp các hàm tiện
ích đã được định nghĩa sẵn mà bạn có thể sử dụng trong chương trình C
của mình. Dưới đây là một số ví dụ về các hàm quan trọng có sẵn trong
thư viện chuẩn C:
+ printf/scanf trong stdio.h: Hàm nhập/xuất chuẩn.

37
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

+ (Trong math.h) double exp(double x): Tính giá trị mũ của x.


+ (Trong math.h) double log(double x): Tính logarithm tự nhiên của
x.
+ (Trong math.h) double log10(double x): Tính logarithm cơ số 10
của x.
+ (Trong math.h) double pow(double x, double y): Tính giá trị x mũ
y.
+ (Trong math.h) double ceil(double x): Làm tròn x lên thành số
nguyên gần nhất.
+ (Trong math.h) double floor(double x): Làm tròn x xuống thành số
nguyên gần nhất.
+ (Trong math.h) double round(double x): Làm tròn x thành số
nguyên gần nhất.
+ (Trong math.h) double trunc(double x): Cắt bỏ phần thập phân của
x.
+ (Trong math.h) double fabs(double x): Lấy giá trị tuyệt đối của x.
+ (Trong math.h) double fmod(double x, double y): Lấy phần dư khi
x chia y.
+ (Trong math.h) double sin(double x): Tính sin của x (x đơn vị
radian).
+ (Trong math.h) double cos(double x): Tính cos của x (x đơn vị
radian).
+ (Trong math.h) double tan(double x): Tính tan của x (x đơn vị
radian).
+ (Trong math.h) double asin(double x): Tính arcsin của x và trả về
kết quả trong khoảng [-π/2, π/2] radian.

38
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

+ (Trong math.h) double acos(double x): Tính arccos của x và trả về


kết quả trong khoảng [0, π] radian.
+ (Trong math.h) double atan(double x): Tính arctan của x và trả về
kết quả trong khoảng [-π/2, π/2] radian.
+ (Trong math.h) double sqrt(double x): Tính căn bậc hai của x.
+ (Trong math.h) double cbrt(double x): Tính căn bậc ba của x.
+ (Trong math.h) double hypot(double x, double y): Tính độ dài của
đoạn thẳng từ điểm (0, 0).
- Lưu ý rằng cần phải include header tương ứng trước khi gọi hàm có sẵn
và một số hàm trong thư viện chuẩn C yêu cầu bổ sung đối số hoặc kiểu
dữ liệu phù hợp để hoạt động chính xác.

IV. Cấu trúc dữ liệu mảng:

* Mảng trong ngôn ngữ lập trình C là một cấu trúc dữ liệu dùng để lưu trữ một
tập hợp các phần tử có cùng kiểu dữ liệu. Mảng được định nghĩa bằng cách khai
báo kiểu dữ liệu của phần tử và kích thước của mảng.

* Trong mảng, các phần tử được lưu trữ liên tiếp trong bộ nhớ và truy cập thông
qua chỉ số của mảng. Chỉ số của mảng bắt đầu từ 0 và kết thúc ở kích thước của
mảng trừ 1.

* Mảng cũng có thể là một 1 chiều, mảng 2 chiều hoặc mảng đa chiều, cho phép
lưu trữ dữ liệu trong một cấu trúc hai hoặc nhiều chiều.

* Lưu ý rằng kích thước của mảng phải là một giá trị cố định được xác định tại
thời điểm biên dịch, không thể thay đổi trong quá trình thực thi chương trình.

39
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

Nếu mảng được khai báo trong hàm và không khởi tạo giá trị cho mảng thì các
phần tử của mảng sẽ mang giá trị ngẫu nhiên (giá trị rác).
1. Mảng 1 chiều:
- Mảng 1 chiều trong ngôn ngữ lập trình C là một cấu trúc dữ liệu được sử
dụng để lưu trữ và quản lý một tập hợp các phần tử có cùng kiểu dữ liệu
và được xếp theo thứ tự.

- Khai báo mảng 1 chiều:

- Để truy cập các đến phần tử trong mảng ta sử dụng chỉ số của các phần tử
đó. Ví dụ muốn gán giá trị 10 cho phần tử có chỉ số 5 trong mảng a ta làm
như sau: a[5] = 10;
- Các thao tác với mảng 1 chiều:

40
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

2. Mảng đa chiều:
- Mảng đa chiều (Multi-dimensional array) là một mảng của các mảng.
Mảng 2 chiều hay ma trận là mảng đa chiều thường được sử dụng nhất.
Khi đó, mảng 2 chiều là một mảng của các mảng 1 chiều. Mảng 2 chiều
có cách lưu trữ các phần tử giống như một bảng.

- Giả sử mảng 1 chiều có n phần tử nếu mỗi phần tử của mảng là 1 mảng 1
chiều ⇒ Ta có mảng 2 chiều.

- Lưu ý: Mảng 2 chiều có kích thước m x n thì chỉ số của hàng là từ 0 đến
m - 1, của cột là từ 0 đến n - 1.

- Khai báo mảng 2 chiều:

- Cách truy cập tới phần tử của ma trận cũng giống như với mảng 1 chiều.
Tuy nhiên, chúng ta sẽ có 2 chỉ số khác nhau là chỉ số hàng và chỉ số cột.
Cú pháp như sau: arr[row_index][col_index]. Ví dụ muốn gán giá trị 5
cho phần tử ở hàng 3, cột 4 ta làm như sau: a[2][3] = 5;

- Mảng 2 chiều còn có tên gọi khác là Ma trận.

- Các thao tác với mảng 2 chiều:

41
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

V. Thuật toán sắp xếp và tìm kiếm:


1. Thuật toán sắp xếp nổi bọt (Bubble sort):
- Bubble sort là liên tục duyệt trên một mảng chưa được sắp xếp, so sánh
các cặp phần tử lân cận và hoán đổi chúng nếu chúng không theo đúng
thứ tự, cho đến khi không có hoán đổi nào xảy ra, điều này cho biết mảng
hiện tại đã được sắp xếp.
- Thuật toán này đơn giản để học và dễ hiểu, nhưng chạy quá chậm và
không thực tế khi so sánh với các thuật toán nâng cao hơn như Quick
sort, Merge sort, Radix sort, ...
- Trường hợp tốt nhất của Bubble sort khi mảng đầu vào gần như đã được
sắp xếp.
- Trường hợp xấu nhất của Bubble sort là khi phần tử nhỏ nhất ở cuối
mảng.

42
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Bài toán: Cho mảng a[] = {4, 1, 3, 2, 5}, hãy đưa ra mảng a[] đã được
sắp xếp.
- Cách thực hiện:
+ Bước 1: Đầu tiên, chúng ta sẽ duyệt từ đầu đến cuối mảng, tại mỗi
lần duyệt chúng ta sẽ so sánh phần tử hiện tại với phần tử kế tiếp
nó và đổi chỗ nếu cần thiết, thực hiện liên tục cho đến cuối mảng.
[4 1 3 2 5] ⇨ [1 4 3 2 5] Swap vì 4 > 1.
[1 4 3 2 5] ⇨ [1 3 4 2 5] Swap vì 4 > 3.
[1 3 4 2 5] ⇨ [1 3 2 4 5] Swap vì 4 > 2.
[1 3 2 4 5] ⇨ [1 3 2 4 5] Không swap vì 4 < 5.
+ Bước 2: Kết thúc lần duyệt đầu tiên, thuật toán đã xác định rằng [5]
là số lớn nhất trong mảng và đã được đưa về đúng vị trí (Vị trí cuối
mảng). Thuật toán bây giờ sẽ bắt đầu duyệt lần 2 và bỏ qua vị trí
index = 4 do phần tử lớn nhất đã được đặt đúng vị trí.
[1 3 2 4 5] ⇨ [1 3 2 4 5] Không swap vì 1 < 3.
[1 3 2 4 5] ⇨ [1 2 3 4 5] Swap vì 3 > 2.
[1 2 3 4 5] ⇨ [1 2 3 4 5] Không swap vì 3 < 4.
+ Bước 3: Bây giờ, mảng đã được sắp xếp theo thứ tự tăng dần trước
khi thuật toán hoàn thành, tuy nhiên thuật toán không biết danh
sách đã được sắp xếp hay chưa nên sẽ tiếp tục duyệt 1 lần nữa để
chắc chắn rằng không có bất kỳ 1 lần swap nào trong lần duyệt này.
[1 2 3 4 5] ⇨ [1 2 3 4 5] Không swap vì 1 < 2
[1 2 3 4 5] ⇨ [1 2 3 4 5] Không swap vì 2 < 3
+ Nhận xét: Tại bước này, ta thấy không có bất kỳ 1 lần swap nào tức
là tất cả các phần tử đã đứng đúng vị trí của nó, do đó ta có thể kết
thúc thuật toán tại bước này.
𝑛(𝑛 − 1)
+ Công thức tính số lần so sánh và swap tối đa:
2

43
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Code:

- Một số thuật toán sắp xếp khác: https://tinyurl.com/nukbkwfb


2. Thuật toán tìm kiếm tuần tự (Linear search):
- Tư tưởng thuật toán: Duyệt từ đầu mảng đến cuối mảng, nếu phần tử
đang xét bằng với phần tử đang tìm kiếm thì ta trả về kết quả.
- Code:

- Độ phức tạp: O(n) với n là số lượng phần tử trong mảng.


3. Thuật toán tìm kiếm nhị phân (Binary search): (Tìm hiểu thêm trong tài liệu
Cấu trúc dữ liệu & giải thuật)

44
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

VI. Xâu ký tự:


1. Cơ bản về xâu ký tự (chuỗi) trong C:
- Trong ngôn ngữ lập trình C, xâu ký tự (hay còn gọi là chuỗi) là một mảng
các ký tự liên tiếp được kết thúc bằng ký tự null '\0'. Xâu ký tự được sử
dụng để lưu trữ và xử lý các dữ liệu dạng văn bản, như các từ, câu, tên,
địa chỉ, v.v.
- Xâu ký tự thường được lưu trữ dưới dạng mảng kiểu char và được khai
báo như sau:

- Để truy cập và thao tác với các phần tử trong xâu ký tự, chúng ta có thể
sử dụng chỉ số của mảng như các mảng thông thường.

- Các thao tác với xâu ký tự:

45
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Xử lý trôi lệnh: Khi trước đó ta nhập 1 số và sau đó xuống dòng để nhập


chuỗi, lúc này lệnh xuống dòng sinh ra ký tự điều khiển '\n', nếu ta không
xử lý ký tự '\n' này thì nó sẽ được đọc vào chuỗi thay vì chuỗi cần đọc.
Để xử lý trôi lệnh gây ra bởi ký tự '\n' có rất nhiều cách, nhưng cách đơn
giản và an toàn nhất là sử dụng scanf(“\n”); trước khi đọc chuỗi từ input.
2. Mảng ký tự 2 chiều trong C:
- Mảng ký tự 2 chiều trong C cũng được gọi là mảng chuỗi, là một mảng 2
chiều chứa các ký tự. Mỗi phần tử trong mảng là một chuỗi ký tự, được
biểu diễn bằng một mảng ký tự 1 chiều.

- Cách khai báo và khởi tạo mảng ký tự 2 chiều tương tự mảng số nguyên 2
chiều, chỉ có điều là các phần tử là ký tự và mỗi hàng là 1 xâu ký tự
(chuỗi).
3. Các hàm xử lý ký tự:
- Trước tiên, cần phải biết thư viện chứa các hàm xử lý ký tự: ctype.h
- Các hàm xử lý ký tự thường gặp:
+ Hàm kiểm tra một ký tự có phải là ký tự số hay không?
● Cú pháp: isdigit(<kí tự cần kiểm tra>)

46
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

● Hàm này trả về giá trị boolean: Trả về true nếu ký tự nhập
vào là ký tự nằm trong đoạn từ ‘0’ đến ‘9’ và false trong
trường hợp ngược lại.
+ Hàm kiểm tra một ký tự có phải là ký tự chữ cái hay không?
● Cú pháp: isalpha(<kí tự cần kiểm tra>)
● Hàm này trả về giá trị boolean: Trả về true nếu ký tự nhập
vào là ký tự nằm trong đoạn từ ‘a’ đến ‘z’ hoặc từ ‘A’ đến
‘Z’ và false trong trường hợp ngược lại.
+ Hàm kiểm tra một ký tự có phải là ký tự chữ in thường hay không?
● Cú pháp: islower(<kí tự cần kiểm tra>)
● Hàm này trả về giá trị boolean: Trả về true nếu ký tự nhập
vào là ký tự nằm trong đoạn ‘a’ đến ‘z’ và false trong trường
hợp ngược lại.
+ Hàm kiểm tra một ký tự có phải là ký tự in thường hay không?
● Cú pháp: isupper(<kí tự cần kiểm tra>)
● Hàm này trả về giá trị boolean: Trả về true nếu ký tự nhập
vào là ký tự nằm trong đoạn từ ‘A’ đến ‘Z’ và false trong
trường hợp ngược lại.
+ Hàm kiểm tra một ký tự có phải là ký tự số hoặc chữ cái hay
không?
● Cú pháp: isalnum(<kí tự cần kiểm tra>)
● Hàm này trả về giá trị boolean: Trả về true nếu ký tự nhập
vào là ký tự nằm trong đoạn từ ‘0’ đến ‘9’ hoặc từ ‘a’ đến ‘z’
hoặc từ ‘A’ đến ‘Z’ và false trong trường hợp ngược lại.
+ Hàm chuyển một ký tự chữ cái sang ký tự chữ cái viết hoa:
● Cú pháp: toupper(<kí tự cần chuyển>)

47
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

● Hàm này trả về giá trị là 1 ký tự char: Nếu ký tự nhập vào là


1 kí tự chữ cái thì trả về ký tự in hoa của chữ cái đó, còn nếu
ký tự nhập vào không phải ký tự chữ cái thì trả về chính ký
tự đó.
+ Hàm chuyển một ký tự chữ cái sang ký tự chữ cái viết thường:
● Cú pháp: tolower(<kí tự cần chuyển>)
● Hàm này trả về giá trị là 1 ký tự char: Nếu ký tự nhập vào là
1 ký tự chữ cái thì trả về ký tự in thường của chữ cái đó, còn
nếu ký tự nhập vào không phải ký tự chữ cái thì trả về chính
ký tự đó.
4. Các hàm xử lý xâu ký tự:
- Thư viện chứa các hàm xử lý xâu ký tự: string.h
- Các hàm xử lý xâu ký tự thường sử dụng:
+ Hàm nối xác định độ dài chuỗi:
● Cú pháp: strlen(str)
● Hàm này sẽ trả về giá trị là độ dài chuỗi tính từ đầu chuỗi
đến ký tự ‘\0’ đầu tiên (không tính ký tự ‘\0’) và mang kiểu
dữ liệu là long long. “abc\0cde”
+ Hàm nối chuỗi str2 vào chuỗi str1:
● Cú pháp: strcat(str1, str2);
● Hàm này sẽ nối chuỗi str2 vào chuỗi str1, lưu ý là chuỗi str1
phải có kích thước đủ lớn để chứa chuỗi str2 nếu không sẽ
gây tràn bộ nhớ.
+ Hàm copy chuỗi str2 vào chuỗi str1:
● Cú pháp: strcpy(str1, str2);

48
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

● Hàm này sẽ copy chuỗi str2 vào chuỗi str1, lưu ý là chuỗi
str1 phải có kích thước đủ lớn để chứa chuỗi str2 nếu không
sẽ gây tràn bộ nhớ.
+ Hàm so sánh 2 chuỗi str1, str2:
● Cú pháp: strcmp(str1, str2)
● Hàm này sẽ trả về 1 trong 3 giá trị:
★ Một giá trị âm nếu thứ tự từ điển của chuỗi str1 nhỏ
hơn thứ tự từ điển của chuỗi str2.
★ Một giá trị dương nếu thứ tự từ điển của chuỗi str1 lớn
hơn thứ tự từ điển của chuỗi str2.
★ Giá trị 0 nếu thứ tự từ điển của chuỗi str1 bằng thứ tự
từ điển của chuỗi str2 (Tức 2 chuỗi giống y hệt nhau).
5. Tách xâu ký tự:
- Hàm char *strtok (char *str, const char *delim) chia chuỗi str thành
một dãy các token được phân biệt riêng rẽ bởi dấu tách delim (như dấu
phẩy, chấm phẩy …).
- Tham số:
+ str: Nội dung của chuỗi này được sửa đổi và được chia thành các
chuỗi nhỏ hơn (các token).
+ delim: Đây là chuỗi chứa Delimiter (chỉ các dấu phân tách). Chúng
có thể rất đa dạng tùy vào từng lời gọi.
- Giá trị trả về: Hàm này trả về con trỏ trỏ tới token đầu tiên được tìm thấy
trong chuỗi. Một con trỏ NULL được trả về nếu không thu được token
nào.
- strtok() quét chuỗi nhập vào từ trái sang phải. Bất cứ khi nào nó tìm thấy
một dấu phân tách, nó sẽ thay thế dấu phân cách bằng một ký tự kết thúc
NULL (‘\0’) và trả về con trỏ đến token đã trích xuất.

49
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Cách gọi hàm:


+ strtok(s, delim) – lấy token đầu tiên.
+ strtok(NULL, delim) - trích xuất các token còn lại (chi tiết xem ở
ví dụ dưới).

- Giải thích từng bước:


+ char* p = strtok(s, " \n"), dấu phân cách đầu tiên được tìm thấy và
được thay thế bằng ký tự NULL (‘\0’), con trỏ tới token đầu tiên:
"Xin" được trả về và in ra màn hình.
+ p = strtok(NULL, " \n"), dấu phân cách thứ hai được tìm thấy và
được thay thế, con trỏ tới token "chao" được trả về và in ra màn
hình.
+ p = strtok(NULL, " "), dấu phân cách thứ ba được tìm thấy và được
thay thế, con trỏ tới "cac" được trả về và in ra màn hình.
+ p = strtok(NULL, " "), dấu phân cách thứ ba được tìm thấy và được
thay thế, con trỏ tới "ban!" được trả về và in ra màn hình.
+ Không còn token nào được tìm thấy, một con trỏ NULL được trả
về, kết thúc vòng lặp.
- Lưu ý rằng khi sử dụng hàm strtok(), chuỗi ban đầu sẽ bị thay đổi bởi
việc thay thế ký tự phân cách bằng ký tự NULL ('\0'). Nếu bạn muốn giữ

50
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

nguyên chuỗi ban đầu, hãy sao chép nó vào một chuỗi khác trước khi sử
dụng strtok().
VII. Con trỏ:
1. Biến và địa chỉ:

- Cấu trúc của 1 biến khi được khai báo gồm 3 phần:
+ Tên biến.
+ Giá trị của biến (Trong trường hợp biến chưa được gán giá trị ban
đầu, biến sẽ mang giá trị ngẫu nhiên hay còn gọi là giá trị rác).
+ Địa chỉ của biến.
- Muốn thay đổi giá trị của biến, ta phải thay đổi giá trị tại địa chỉ của biến.
- Muốn nhập giá trị cho biến, ta phải nhập giá trị tại địa chỉ của biến.
2. Con trỏ:
- Con trỏ là biến trỏ tới địa chỉ hay nói cách khác là nó mang giá trị là một
địa chỉ.
- Con trỏ cũng là một biến nên nó cũng có giá trị và địa chỉ của riêng nó.
- Cách khai báo con trỏ: <Kiểu dữ liệu>* <Tên con trỏ>;
- Ví dụ: int* a;
- Vì con trỏ là biến mang giá trị là 1 địa chỉ nên khi gán giá trị cho con trỏ
ta phải gán giá trị cho nó là 1 địa chỉ.
- Để lấy địa chỉ của 1 biến, ta sử dụng toán tử 1 ngôi &.
- Để lấy giá trị tại 1 địa chỉ hay giá trị tại địa chỉ mà con trỏ đang trỏ tới ta
sử dụng toán tử 1 ngôi *.

51
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Nếu như thay đổi giá trị tại địa chỉ mà con trỏ đang trỏ tới thì giá trị của
biến cũng thay đổi theo.

- Giá trị của con trỏ: địa chỉ mà con trỏ trỏ đến.
- Địa chỉ của con trỏ: địa chỉ của bản thân biến con trỏ đó.
- Giá trị của biến nơi con trỏ đang trỏ tới.
- Địa chỉ của biến nơi con trỏ đang trỏ tới = giá trị của con trỏ.
3. Mối quan hệ giữa con trỏ và mảng 1 chiều:
- Nhắc lại khái niệm về mảng: “Mảng là một tập hợp tuần tự các phần tử
có cùng kiểu dữ liệu và các phần tử được lưu trữ trong một dãy các ô nhớ
liên tục trên bộ nhớ”.
- Các bạn đặc biệt lưu ý tới tính chất được lưu trên các ô nhớ liên tục, bây
giờ chúng ta sẽ chứng minh tính đúng đắn của nó bằng ví dụ dưới đây:

52
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Nhận xét: Địa chỉ của mỗi phần tử cách nhau 4 đơn vị tương ứng với 4
byte của kiểu dữ liệu int. Nên ta chắc chắn các phần tử mảng được xếp
cạnh nhau trong bộ nhớ.

⇒ Như vậy, &arr[0] tương đương &arr và tương đương arr. Điều đó có
được là do biến arr trỏ tới phần tử đầu tiên của mảng.

⇒ Do đó, nếu ta thay đổi giá trị các phần tử của mảng ở ngoài hàm
main() thì giá trị của các phần tử trong hàm main() cũng thay đổi theo!
Theo dõi ví dụ dưới đây:

53
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Mối liên hệ giữa con trỏ và mảng một chiều là:


+ Địa chỉ: Tên mảng trong C đại diện cho địa chỉ của phần tử đầu
tiên trong mảng. Điều này có nghĩa là một mảng trong C cũng có
thể được xem như một con trỏ trỏ tới phần tử đầu tiên của mảng
đó.
+ Thao tác trên phần tử: Bằng cách sử dụng con trỏ và các toán tử
con trỏ như * (toán tử dereference) và ++ (toán tử tăng), chúng ta
có thể truy cập và thao tác trên các phần tử của mảng một chiều. Ví
dụ: *(arr + i) truy cập phần tử thứ i trong mảng, arr++ di chuyển
con trỏ tới phần tử tiếp theo trong mảng.
+ Truyền mảng vào hàm: Khi truyền một mảng vào hàm, mảng được
truyền theo cơ chế "truyền con trỏ". Điều này có nghĩa là chỉ địa
chỉ của phần tử đầu tiên trong mảng được truyền vào hàm, không
phải toàn bộ mảng. Hàm có thể sử dụng con trỏ để truy cập các
phần tử của mảng và thay đổi giá trị của chúng.
- Tóm lại, mảng một chiều và con trỏ có mối liên hệ chặt chẽ trong ngôn
ngữ C. Sử dụng con trỏ, chúng ta có thể thao tác trực tiếp trên các phần tử
của mảng và truyền mảng vào hàm để thực hiện các thao tác trên mảng.
4. Toán tử tăng/giảm của con trỏ:
- Chỉ sử dụng được các toán tử cộng và trừ trên con trỏ:

54
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Lưu ý: Khi dùng toán tử tăng/giảm trên biến con trỏ, nó sẽ nhảy sang ô
nhớ liền kề chứ không phải tăng địa chỉ mà nó đang trỏ lên 1. Do con trỏ
p là kiểu int nên mỗi bước tăng, giá trị của p tăng thêm 4 giá trị. (Lưu ý:
giá trị của con trỏ là địa chỉ mà nó đang trỏ tới).

- &x[0] và x có cùng giá trị, và x[0] hay *x là tương đương nhau.


- &x[1] tương đương với x+1 và x[1] tương đương với *(x+1).
- &x[2] tương đương với x+2 và x[2] tương đương với *(x+2).
- …
- Tóm lại, &x[i] tương đương với x+i và x[i] tương đương với *(x+i).
VIII. Kiểu dữ liệu tự định nghĩa - struct:
- Khi nào chúng ta cần phải tự định nghĩa 1 kiểu cấu trúc? Khi bạn cần lưu
trữ một đối tượng có nhiều thuộc tính. Ví dụ, đối tượng SinhVien có các
thuộc tính (Mã sinh viên, họ, tên, giới tính, quê quán,…) hay đối tượng
LopHoc có các thuộc tính (Mã lớp, tên lớp, giáo viên chủ nhiệm, sĩ
số,…). Khi đó chúng ta nên dùng struct để quản lý chương trình.
- Cú pháp:

55
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Cách khai báo biến kiểu struct: Việc khai báo biến với struct cũng giống
như cách khai báo biến thông thường, trong đó kiểu dữ liệu là kiểu struct
trong C mà bạn vừa định nghĩa.

- Cách truy xuất các thuộc tính của biến kiểu struct: Để truy xuất các thuộc
tính của 1 biến kiểu struct ta sử dụng toán tử “.”

56
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Cấu trúc struct lồng nhau: Trong 1 struct ta có thể định nghĩa 1 thuộc tính
trong struct đó bằng 1 struct khác, tuy nhiên 1 struct không thể định nghĩa
chính nó là 1 thuộc tính.

- Khởi tạo biến kiểu cấu trúc: Ta có thể khởi tạo 1 biến kiểu struct ngay khi
khai báo bằng cách liệt kê các thuộc tính của nó nằm trong cặp {} lần
lượt theo những gì đã định nghĩa trong struct.

57
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Câu lệnh gán: Hai biến cấu trúc cùng kiểu thì ta có thể sử dụng toán tử
gán “=” để copy tất cả các thuộc tính của biến cấu trúc này cho biến cấu
trúc kia.

- Mảng kiểu struct: Mảng kiểu struct được khai báo tương tự như mảng
thông thường. Một kiểu struct phải được định nghĩa trước sau đó một
biến mảng có kiểu đó mới được khai báo.

58
Tài liệu ngôn ngữ lập trình C - Nguyễn Mạnh Quân ITIS Study

- Con trỏ kiểu struct: Con trỏ cấu trúc được khai báo bằng cách đặt toán tử
“*” trước tên của 1 biến cấu trúc. Con trỏ cấu trúc được truyền vào hàm
cho phép hàm đó thay đổi các thuộc tính của biến cấu trúc mà nó trỏ đến.
Để truy cập đến các thuộc tính của biến cấu trúc mà con trỏ đang trỏ đến,
ta sử dụng toán tử “->”.

- Ngoài struct, ta còn có kiểu dữ liệu tự định nghĩa nữa là union, cái này
các bạn tự tìm hiểu nhé!

59

You might also like