Professional Documents
Culture Documents
Con trỏ là một biến chứa địa chỉ của biến khác.
Con trỏ được dùng để truy cập biến thông qua địa chỉ biến.
Ví dụ 1:
x
int x = 6, y = 7;
int *px, *py;
px = &x; y
py = &y;
3
CHƯƠNG 6. MẢNG, CON TRỎ VÀ CHUỖI KÝ TỰ
6.2.1 Định nghĩa con trỏ.
int x = 6, y = 7;
int *px, *py; 16 x
px = &x;
py = &y;
*px += 10;
*py += 10; y
17
Chú ý:
• là toán tử reference và có thể đọc là “ địa chỉ của”
• * là toán tử dereference có thể được đọc là “ giá trị được trỏ bởi “ 4
CHƯƠNG 6. MẢNG, CON TRỎ VÀ CHUỖI KÝ TỰ
6.2.1 Định nghĩa con trỏ
#include <stdio.h>
void main(void)
{
int x = 6, y = 7;
int *px, *py;
printf("x = %d, y = %d\n", x, y);
px = &x; Gán cho ô nhớ được trỏ bởi
px giá trị bằng giá trị của ô
py = &y;
nhớ đó +10
printf("gia tri cua o nho duoc tro boi px= %d, py = %d\n", *px, *py);
*px += 10;
*py += 10;
printf("x = %d, y = %d\n", x, y);
printf("gia tri cua o nho duoc tro boi px= %d, py = %d\n", *px, *py); Gán cho ô nhớ được trỏ bởi
py giá trị bằng giá trị của ô
getch(); nhớ đó +10
} 5
CHƯƠNG 6. MẢNG, CON TRỎ VÀ CHUỖI KÝ TỰ
6.2. Định nghĩa con trỏ, cấp phát và hủy vùng nhớ.
Như đã biết, mảng là một tập hợp của các phần tử nằm liên tiếp nhau trên bộ nhớ và
có cùng kiểu dữ liệu.
Khi khai báo mảng, bạn phải chỉ định rõ kích thước tối đa (số lượng phần tử tối đa).
Và sau khi khai báo, bạn không thể thay đổi kích thước của mảng
Cấp phát tĩnh (bộ nhớ được cấp phát khi bắt đầu thực hiện chương trình)
Một số hạn chế có thể gặp phải khi sử dụng các biến tĩnh:
- Cấp phát ô nhớ dư, gây ra lãng phí ô nhớ;
- Cấp phát ô nhớ thiếu, chương trình thực thi bị lỗi. 6
CHƯƠNG 6. MẢNG, CON TRỎ VÀ CHUỖI KÝ TỰ
6.2. Cấp phát và hủy vùng nhớ động
Nếu khi thiết kế chương trình, người lập trình chưa biết số phần tử của mảng ( mà
người sử dụng cần)
Khi đó sử dụng cấp phát bộ nhớ động.
- Cấp phát bộ nhớ động: vùng bộ nhớ được cấp phát trong quá trình thực hiện chương
trình chứ không cấp phát lúc bắt đầu chương trình.
- Sau khi sử dụng xong có thể giải phóng để tiết kiệm chỗ trong bộ nhớ.
7
CHƯƠNG 6. MẢNG, CON TRỎ VÀ CHUỖI KÝ TỰ
6.2.2 Cấp phát và hủy vùng nhớ động
Để cấp phát vùng nhớ động cho biến con trỏ trong ngôn ngữ C, sử dụng hàm:
malloc() hoặc hàm calloc().
Để giải phóng bộ nhớ đã cấp phát khi không cần sử dụng, sử dụng hàm free().
Để thay đổi (phân bổ lại) kích thước bộ nhớ đã cấp phát sử dụng realloc()
Bốn hàm malloc, calloc, realloc và free nằm trong thư viện stdlib.h hoặc alloc.h
8
CHƯƠNG 6. MẢNG, CON TRỎ VÀ CHUỖI KÝ TỰ
Cấp phát bộ nhớ động trong C
Hàm malloc() thực hiện cấp phát bộ nhớ bằng cách chỉ định số byte cần cấp phát. Hàm
này trả về con trỏ kiểu void cho phép chúng ta có thể ép kiểu về bất cứ kiểu dữ liệu nào.
Cú pháp của hàm malloc(): ptr = (kiểu dữ liệu*) malloc(số ô nhớ cần cấp phát);
Cú pháp của hàm calloc():
ptr = (kiểu dữ liệu*) calloc(so-phan-tu, kich-co-phan-tu);
Số phần tử cố định
Ví dụ 1: int n, *ptr;
ptr = (int*) malloc(100 * sizeof(int));
Ví dụ 2: int n, *ptr;
printf("Nhap so luong phan tu: ");
scanf("%d", &n); Số phần tử động ( do người dùng
ptr = (int *)malloc(n * sizeof(int)); nhập) 9
CHƯƠNG 6. MẢNG, CON TRỎ VÀ CHUỖI KÝ TỰ
6.2.2 Cấp phát và hủy vùng nhớ động
Giải phóng bộ nhớ động trong C
Việc cấp phát bộ nhớ động trong C sử dụng malloc() hay calloc() thì chúng cũng
đều không thể tự giải phóng bộ nhớ.
Sau nhiều lần chạy chương trình, bộ nhớ sẽ tràn.
Bộ nhớ động chỉ cần thiết ở thời điểm cụ thể trong chương trình, khi nó không còn
cần thiết nữa thì nên được giải phóng để bộ nhớ sẵn sàng cho các yêu cầu cấp phát
bộ nhớ động khác Tránh được tràn bộ nhớ.
Để giải phóng bộ nhớ động dùng hàm free()
Cú pháp của hàm free():
int a, *p, *q ; float *f; Ta cũng có thể ép kiểu con trỏ theo cú pháp:
a = 5 ; p = &a ; q = p ; /* đúng */ (<Kiểu kết quả>*)<Tên con trỏ>
f = p ; /* sai do khác kiểu */ Chẳng hạn, ví dụ trên được viết lại:
int a, *p, *q ; float *f;
a = 5 ; p = &a ; q = p ; /* đúng */
f = (float*)p; /* Đúng nhờ ép kiểu*/
13
CHƯƠNG 6. MẢNG, CON TRỎ VÀ CHUỖI KÝ TỰ
6.2.3 Một số phép toán trên con trỏ
b. Phép so sánh con trỏ.
Hai con trỏ có thể được so sánh trong một biểu thức quan hệ nếu chúng trỏ đến các biến
có cùng kiểu dữ liệu
Ví dụ: p_a < p_b Trả về giá trị true nếu a được lưu trữ ở vị trí trước b
p_a > p_b Trả về giá trị true nếu a được lưu trữ ở vị trí sau b
int a, b, *p_a, *p_b ; p_a <= p_b Trả về giá trị true nếu a được lưu trữ ở vị trí trước b hoặc
p_a và p_b trỏ đến cùng một vị trí
p_a=&a;
p_a => p_b Trả về giá trị true nếu a được lưu trữ ở vị trí sau b hoặc
p_b=&b; p_a và p_b trỏ đến cùng một vị trí
p_a == p_b Trả về giá trị true nếu cả hai con trỏ p_a và p_b trỏ đến
cùng một phần tử dữ liệu.
p_a! = p_b Trả về giá trị true nếu cả hai con trỏ p_a và p_b trỏ đến các
phần tử dữ liệu khác nhau nhưng có cùng kiểu dữ liệu.
p_a == NULL Trả về giá trị true nếu p_a được gán giá trị NULL(0)
14
CHƯƠNG 6. MẢNG, CON TRỎ VÀ CHUỖI KÝ TỰ
6.2.3 Một số phép toán trên con trỏ
c. Cộng, trừ con trỏ với một số nguyên
Chỉ phép cộng và trừ là được thực hiện với con trỏ, các phép tính khác không có ý
nghĩa đối với con trỏ
Ta có thể cộng (+), trừ (-) 1 con trỏ với 1 số nguyên N nào đó; kết quả trả về là 1 con
trỏ. Con trỏ này chỉ đến vùng nhớ cách vùng nhớ của con trỏ hiện tại N phần tử.
Ví dụ
int x = 6, y = 7;
int *px, *py; x int x = 6, y = 7;
px = &x; int *px, *py;
py = &y; 1217
px = &x;
px += 7; y py = &y;
1213
py += 3; *px += 10;
*py += 10;
Giá trị nào thay đổi? 15
CHƯƠNG 6. MẢNG, CON TRỎ VÀ CHUỖI KÝ TỰ
6.3. Con trỏ và mảng. numbers [0]
Giữa mảng và con trỏ có một sự liên hệ rất chặt chẽ.
[1]
• Tên của mảng tương đương với địa chỉ phần tử đầu tiên của
nó, tương đương với địa chỉ của phần tử đầu tiên mà con trỏ [2]
trỏ tới.
[3]
Ví dụ: int numbers [5];
int* p; [4]
p = numbers;
• Những phần tử của mảng có thể được xác định bằng chỉ số
trong mảng, hoặc được xác lập qua biến con trỏ.
Ví dụ: numbers[2] = 2; *(p+2)=2; 16
CHƯƠNG 6. MẢNG, CON TRỎ VÀ CHUỖI KÝ TỰ
6.3. Con trỏ và mảng. a [0] 10 2000
Ví dụ 1:
[1] 20 2002
#include <stdio.h> for(i=0; i<5; i++)
int main () printf("Phan tu thu %d cua mang la:
{ %d \n ",i+1, a[i]); [2] 30 2004
int a[5], i; return 0;
int* p; }
[3] 40 2006
p = a;
*p = 10;
p++; Chú ý: Cách sử dụng con trỏ để truy cập các [4] 50 2008
*p = 20; phần tử của mảng.
p = &a[2]; - Con trỏ chứa địa chỉ trực tiếp của các
*p = 30; p 2002
2004
2006
2000 8000
phần tử mảng
p = a + 3; - Truy cập đến phần tử được trỏ bởi (Con
*p = 40;
trỏ+ số nguyên)
p = a;
*(p+4) = 50; 17
CHƯƠNG 6. MẢNG, CON TRỎ VÀ CHUỖI KÝ TỰ
6.3. Con trỏ và mảng .
Ví dụ 2: Về truy cập phần tử theo kiểu mảng và con trỏ.
18
CHƯƠNG 6. MẢNG, CON TRỎ VÀ CHUỖI KÝ TỰ
6.3. Con trỏ và mảng . a [0] [0] 2000 (p)
20
CHƯƠNG 6. MẢNG, CON TRỎ VÀ CHUỖI KÝ TỰ
6.3. Con trỏ và mảng .
Con trỏ là tham số truyền cho hàm
Một hàm cũng giống như một biến: có tên gọi , có địa chỉ lưu trong bộ nhớ và
có thể truy nhập đến hàm thông qua tên gọi hoặc địa chỉ của nó
Con trỏ mà trỏ đến điểm xâm nhập vào hàm. Ta gọi đây là con trỏ hàm.
Việc sử dụng con trỏ cho phép các hàm cũng được truyền như là tham số
cho các hàm khác.
26
CHƯƠNG 6. MẢNG, CON TRỎ VÀ CHUỖI KÝ TỰ
6.4. Con trỏ hàm . main { 2000
2002
⋮
}
line { 2020
2022
Khi biên dịch hàm, trình biên dịch chuyển chương trình
nguồn sang dạng mã máy và thiết lập một điểm xâm nhập ⋮
vào hàm (chính là địa chỉ mã máy đầu tiên của hàm). }
⋮
Gọi hàm bằng tên hàm Gọi hàm trực tiếp
Gọi hàm thông qua địa chỉ của hàm (sử dụng con trỏ hàm)
Gọi hàm gián tiếp
27
CHƯƠNG 6. MẢNG, CON TRỎ VÀ CHUỖI KÝ TỰ
6.4. Con trỏ hàm .
6.4.1 Khai báo .
Để truy nhập (gọi) hàm thông qua địa chỉ chúng ta phải khai báo một con trỏ chứa
địa chỉ này và sau đó gọi hàm bằng cách gọi tên con trỏ.
Cú pháp
<kiểu dữ liệu trả về> (*<tên con trỏ hàm>)(<danh sách các tham số> );
Ví dụ:
float (*f)(int); // khai báo con trỏ hàm có tên là f trỏ đến hàm, có một tham số kiểu int và giá
// trị trả về kiểu float.
void (*f)(float, int); // con trỏ hàm, trỏ đến hàm với cặp tham số là (float, int) và không có
// giá trị trả về.
char* (*m[10])(int, char) // khai báo một mảng 10 con trỏ hàm, trỏ đến các hàm có cặp tham
// số truyền (int, char), giá trị trả về là con trỏ kiểu char
28
CHƯƠNG 6. MẢNG, CON TRỎ VÀ CHUỖI KÝ TỰ
6.4. Con trỏ hàm .
6.4.2 Sử dụng con trỏ hàm .
Ví dụ:
Con trỏ hàm thay tên hàm
Để sử dụng con trỏ hàm ta phải gán nó với #include <stdio.h>
tên hàm cụ thể. Sau đó bất kỳ nơi nào được float bphuong(float x)
{
phép xuất hiện tên hàm thì ta đều có thể thay return x*x;
nó bằng tên con trỏ. }
int main()
Chú ý: phân biệt giữa 2 khai báo {
float (*f)(float);
float (*f)(int); // Khai báo con trỏ hàm f = bphuong;
printf("Binh phuong cua 3.5 =%f",f(3.5));
float *f(int); //Khai báo hàm f có giá trị trả về getch();
// là con trỏ float. return 0;
}
29
CHƯƠNG 6. MẢNG, CON TRỎ VÀ CHUỖI KÝ TỰ
6.4. Con trỏ hàm .
6.4.2 Sử dụng con trỏ hàm .
Ví dụ:
Con trỏ hàm thay tên hàm
Để sử dụng con trỏ hàm ta phải gán nó với #include <stdio.h>
tên hàm cụ thể. Sau đó bất kỳ nơi nào được float bphuong(float x)
{
phép xuất hiện tên hàm thì ta đều có thể thay return x*x;
nó bằng tên con trỏ. }
int main()
Chú ý: phân biệt giữa 2 khai báo {
float (*f)(float);
float (*f)(int); // Khai báo con trỏ hàm f = bphuong;
printf("Binh phuong cua 3.5 =%f",f(3.5));
float *f(int); //Khai báo hàm f có giá trị trả về getch();
// là con trỏ float. return 0;
}
30
Bài Tập Phần Con Trỏ
Bài tập 1
Viết chương trình nhập vào một mảng số nguyên ( số phần tử của mảng do người dùng nhập),
hãy xuất ra màn hình:
Phần tử lớn nhất của mảng.
Phần tử nhỏ nhất của mảng.
Tính tổng của các phần tử trong mảng .
Sắp xếp mảng theo giá trị tăng dần
Đếm các số chính phương có trong mảng
Đếm số nguyên tố có trong mảng
Yêu cầu: Chương trình sử dụng con trỏ để cấp phát bộ nhớ động và truy cập các phần tử của
mảng. 31
Bài Tập Phần Con Trỏ
Bài tập 2
Viết chương trình nhập vào một dãy các số (số lượng phần tử trong dãy do
người dung nhập) theo thứ tự tăng, nếu nhập sai quy cách thì yêu cầu nhập lại. In
dãy số sau khi đã nhập xong. Nhập thêm một số mới và chèn số đó vào dãy đã
có sao cho dãy vẫn đảm bảo thứ tự tăng. In lại dãy số để kiểm tra.
Yêu cầu: Chương trình sử dụng con trỏ để cấp phát bộ nhớ động và truy cập các
phần tử của mảng.
32
Bài Tập Phần Con Trỏ
Bài tập 3
Viết chương trình nhập vào một ma trận (mảng hai chiều) các số nguyên, gồm m
hàng, n cột (m, n do người dùng nhập). In ma trận đó lên màn hình. Nhập một số
nguyên khác vào và xét xem có phần tử nào của ma trận trùng với số này không
? Ở vị trí nào ? Có bao nhiêu phần tử ?
Yêu cầu: Chương trình sử dụng con trỏ để cấp phát bộ nhớ động và truy cập các
phần tử của mảng.
33
Bài Tập Phần Con Trỏ
Bài tập 4
Viết chương trình nhập vào một ma trận (mảng hai chiều) các số nguyên, gồm m
hàng, n cột (m, n do người dùng nhập). In ma trận đó lên màn hình. Nhập một số
nguyên khác vào và xét xem có phần tử nào của ma trận trùng với số này không
? Ở vị trí nào ? Có bao nhiêu phần tử ?
Yêu cầu: Chương trình sử dụng con trỏ để cấp phát bộ nhớ động và truy cập các
phần tử của mảng.
34