1. ĐẠI HỌC CÔNG NGHỆ THÔNG TIN - ĐHQG Tp HCM – SE05
CON TRỎ TRONG NGÔN NGỮ
C/C++
SE-PRO GROUP
1
2. NỘI DUNG:
DẪN NHẬP
CON TRỎ CƠ BẢN
CON TRỎ NÂNG CAO
TÀI LIỆU THAM KHẢO
2
3. DẪN NHẬP Byte 2n
Memory
(RAM)
Win n bit thì
Hệ điều hành n bits
RAM tối đa
2n byte
Code data stack
Con trỏ chỉ là
một biến kiểu
int lưu địa chỉ Byte 0
của biến khác
Process Hệ điều
program hành nạp
code Process
lên RAM
data
*.exe để thực thi
quản lý theo
stack
địa chỉ
3
4. Con trỏ cơ bản
1 Khai báo và cách sử dụng
2 Các cách truyền đối số cho hàm
3 Con trỏ và mảng một chiều
4 Con trỏ và cấu trúc
4
5. Khai báo con trỏ và cách sử dụng
• Khai báo:
Giống như mọi biến khác, biến con trỏ muốn sử dụng cũng
cần phải được khai báo
<kiểu dữ liệu> *<tên biến con trỏ>;
Ví
dụ:
int a = 5;
int *ptr;
ptr = &a; a ptr
Tên : a Tên : ptr
Giá tri mà biến lưu Giá trị mà biến lưu
trữ : 5 trữ : 1025
Địa chỉ lưu trong Địa chỉ lưu trong
bộ nhớ: 1025(giả bộ nhớ: 5000(giả
định) định)
5
6. Khai báo con trỏ và cách sử dụng
• Sử dụng từ khóa typedef
typedef <kiểu dữ liệu> *<tên kiểu con trỏ>;
<tên kiểu con trỏ> <tên biến con trỏ>;
• Ví dụ:
typedef int *pInt;
int *p1;
pInt p2, p3;
Chý ý:
• Giảm bối rối khi mới tiếp xúc với con trỏ.
• Nhưng dễ nhầm lẫn với biến thường.
6
7. Khai báo con trỏ và cách sử dụng
• Con trỏ NULL
– Con trỏ NULL là con trỏ không trỏ vào đâu cả.
– Khác với con trỏ chưa được khởi tạo.
int n;
int *p1 = &n;
int *p2; // Trỏ đến vùng nhớ kiểu int một cách ngẫu
int *p3 = NULL;nhiên
p2
NULL
Chú ý:
*p1 và n đều chỉ nội dung của biến n.
p1 và &n đều chỉ địa chỉ của biến n.
7
8. Cách truyền đối số cho hàm
• Truyền địa chỉ (con trỏ) cho hàm
#include <stdio.h>
void hoanvi(int *x, int *y);
void main()
{
int a = 3; b = 6;
hoanvi(&a, &b);
printf(“a = %d, b = %d”, a, b);
}
void hoanvi(int *x, int *y)
{
int t = *x; *x = *y; *y = t;
}
8
9. Con trỏ và mảng một chiều
• Mảng một chiều
int Array[3];
– Tên mảng Array là một hằng con trỏ
không thể thay đổi giá trị của hằng này.
– Giá trị của Array là địa chỉ phần tử đầu tiên của
mảng
Array == &Array[0]
0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17
… …
Array 9
10. Con trỏ và mảng một chiều
• Con trỏ đến mảng một chiều
int Array[3], *pArray;
pArray = Array; // Cách 1
pArray = &Array[0]; // Cách 2
18 19 1A 1B 1C 1D 1E 1F
… 0B 00 00 00 …
pArray
0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17
… …
Array
10
11. Con trỏ và mảng một chiều
Các phép toán số học trên con trỏ
• Phép cộng (tăng)
+ n + n * sizeof(<kiểu dữ liệu>)
Có thể sử dụng toán tử gộp += hoặc ++
p = Array
+2
+1
0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17
… …
int Array[3] 11
12. Con trỏ và mảng một chiều
• Phép trừ (giảm)
+ n + n * sizeof(<kiểu dữ liệu>)
Có thể sử dụng toán tử gộp -= hoặc --
p = &Array[2]
–2
–1
0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17
… …
int Array[3] 12
13. Con trỏ và mảng một chiều
// Nhập mảng // Xuất mảng
void main() void main()
{ {
int a[10], n = 10, *pa; int a[10], n = 10, *pa;
pa = a; // hoặc pa = &a[0]; pa = a; // hoặc pa = &a[0];
for (int i = 0; i<n; i++) for (int i = 0; i<n; i++)
scanf(“%d”, &a[i]); printf(“%d”, a[i]);
// scanf(“%d”, &pa[i]); // printf(“%d”, pa[i]);
// scanf(“%d”, a + i); // printf(“%d”, *(a + i));
// scanf(“%d”, pa + i); // printf(“%d”, *(pa + i));
// scanf(“%d”, a++); // printf(“%d”, *(a++);
// scanf(“%d”, pa++); // printf(“%d”, *(pa++));
} }
&a[i] (a + i) a[i] *(a + i)
(pa + i) &pa[i] *(pa + i) pa[i]
13
14. Con trỏ và mảng một chiều
Đối số mảng truyền cho hàm
void xuat(int _a[10], int n)
{
for (int i = 0; i<n; i++)
printf(“%d”, *(_a++)); // OK
}
void main()
{
int a[10], n = 10;
for (int i = 0; i<n; i++)
printf(“%d”, *(a++)); // Lỗi
}
Đối số mảng truyền cho hàm không phải hằng con trỏ.
14
15. Con trỏ và mảng một chiều
• Các phép toán khác
– Phép so sánh: So sánh địa chỉ giữa hai con trỏ
(thứ tự ô nhớ)
== !=
> >=
< <=
– Không thể thực hiện các phép toán: * / %
15
16. Con trỏ và cấu trúc
Truy xuất bằng 2 cách
<tên biến con trỏ cấu trúc>-><tên thành phần>
(*<tên biến con trỏ cấu trúc>).<tên thành phần>
Ví dụ
typedef struct
{
int tu, mau;
} PHANSO;
PHANSO ps1, *ps2 = &ps1; // ps2 là con trỏ
ps1.tu = 1; ps1.mau = 2;
ps2->tu = 1; ps2->mau = 2;
(*ps2).tu = 1; (*ps2).mau = 2;
16
17. Bài tập con trỏ cơ bản
Bài 1: Cho đoạn chương trình sau:
float pay;
float *ptr_pay;
pay=2313.54;
ptr_pay = &pay;
Hãy cho biết giá trị của:
a. pay
b. *ptr_pay
c. *pay
d. &pay
17
18. Bài tập con trỏ cơ bản
• Bài 2: Tìm lỗi
#include <stdio.h>
#include <conio.h>
void main()
{
int *x, y = 2;
*x = y; Con trỏ x chưa được khởi
*x += y++; tạo
printf("%d %d", *x, y);
getch();
}
18
19. Bài tập con trỏ cơ bản
• Bài tập 3: Cho biết giá trị xuất ra
#include <stdio.h>
#include <conio.h> Output: ???
void main()
{
int x=25;
int *ptr=&x;
int **temp=&ptr;
printf(“%d %d %d”,x,*ptr,**temp);
}
20. Bài tập con trỏ cơ bản
• Bài 4: Cho đoạn chương trình:
#include <stdio.h> Output: ???
#include <conio.h>
void main()
{
int *ptr=( int *)1000;
ptr=ptr+1;
printf(" %u",ptr);
}
21. Bài tập con trỏ cơ bản
• Bài 5: Cho đoạn chương trình:
#include <stdio.h> Output: ???
#include <conio.h>
void main()
{
double *p=( double *)1000;
p=p+3;
printf(" %u",p);
}
22. Bài tập con trỏ cơ bản
• Bài 6: Cho đoạn chương trình:
#include <stdio.h>
#include <conio.h> Output: ???
void calc(int* a, int b)
{
*a = b;
*a += b++;
}
int main()
{
int x=5,y=6;
calc (&x,y);
printf(“%d %d”, x, y);
return 0;
}
23. Con trỏ nâng cao
1 Cấp phát động
2 Mảng con trỏ
3 Mảng động hai chiều
4 Con trỏ hàm
23
24. Cấp phát động
Để cấp phất động chúng ta sử dụng thư viện <stdlib.h>
Cấp phát một khối nhớ size bytes trả về con trỏ void trỏ đến
đầu khối nhớ đó và trả về NULL nếu thất bại.
void *malloc( size );
Số byte cần cấp phát
Cấp phát một khối nhớ num*size bytes trả về con trỏ void trỏ
đến đầu khối nhớ đó và trả về NULL nếu thất bại.
Kích thước một khối nhớ
void *calloc( num, size );
Số lượng khối nhớ cần cấp phát
24
25. Cấp phát động
Ví dụ:
Dùng con trỏ dVar để quản lý khối nhớ động kiểu double vừa được malloc cấp phát.
double *dVar;
dVar = (double*) malloc ( sizeof(double) );
Tương tự với calloc
double *dVar;
dVar = (double*) calloc ( 1 , sizeof(double) );
Để có được một mảng động với n phần tử trong bộ nhớ ta làm như sau:
double *dVar;
dVar = (double*) malloc ( n*sizeof(double) );
double *dVar;
dVar = (double*) calloc ( n , sizeof(double) );
25
26. Mảng con trỏ
Đặt vấn đề
• Khi cần lưu trữ những dãy dữ liệu lớn kích thước không bằng nhau ví dụ
như một dãy tên sinh viên chẳng hạn nếu ta dùng mảng hai chiều để lưu
trữ sẽ dẫn đến lãng phí do kích thước mỗi tên phải bằng nhau.
Giải quyết vấn đề
•Ta dùng một cách lưu trữ mới đó là mảng con trỏ. Với bài toán lưu trữ và xử lý dãy
tên sinh viên ta khai báo mảng sau.
• Khai báo : •Nhập
#define max 100; for( int i=0; i<max ; i++)
char *pChar[max]; {
printf(“Name*%d+= “,i);
•Xuất scanf(“%s”,pChar[i]);
for( int i=0; i<max ; i++) }
{
printf(“Name*%d+=%s“,pChar[i]);
}
26
27. Mảng động hai chiều
Khai báo và cấp phát
• Khai báo:
DataType **Matrix;
int Rows, Columns;
• Cấp phát Rows dòng cho ma trận thực chất là mảng
một chiều gồm Rows con trỏ kiểu DataType.
Matrix = (DataType**) calloc(Rows , sizeof(DataType*));
• Cấp phát cho mỗi dòng Columns phần tử
for (int i=0 ; i<Rows; i++)
Matrix[i]=(DataType *) malloc(Columns*sizeof(DataType));
27
28. Mảng động hai chiều
Sử dụng:
typedef int DataType; typedef int DataType;
void Nhap(DataType **M,int r,int c) void Xuat(DataType **M,int r,int c)
{ {
int i,j; int i,j;
for ( i=0 ; i<r ; i++) for ( i=0 ; i<r ; i++)
for ( j=0 ; j<c ; j++) for ( j=0 ; j<c ; j++)
{ {
printf("Nhap M[%d][%d]= ",i,j);
scanf("%d",&M[i][j]); printf(“M*%d+*%d+ = %d ", i,j,M[i][j]);
// Hoặc *(M+i)+j // Hoặc *(*(M+i)+j)
} }
} }
28
29. Mảng động hai chiều
Hủy:
• Giải phóng từng dòng một
for ( i=0 ; i<Rows ; i++)
free (Matrix[i]);
• Giải phóng Matrix
free (Matrix);
29
30. Con trỏ hàm
Khái niệm
– Hàm cũng đuợc lưu trữ trong bộ nhớ, tức là cũng
có địa chỉ.
– Con trỏ hàm là con trỏ trỏ đến vùng nhớ chứa
hàm và có thể gọi hàm thông qua con trỏ đó.
0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17
… 11 00 00 00 …
p int Cong(int, int)
30
31. Con trỏ hàm
Khai báo tường minh
<kiểu trả về> (* <tên biến con trỏ>)(ds tham số);
Khai báo không tường minh – bằng typedef
typedef <kiểu trả về> (* <tên con trỏ hàm>)(ds tham số);
<tên con trỏ hàm> <tên biến con trỏ>;
Ví dụ
int (*pt1)(int, int); // Tường minh
typedef int (*PhepToan)(int, int);
PhepToan pt2, pt3; // Không tường minh
31
32. Con trỏ hàm
Gán giá trị cho con trỏ hàm
<biến con trỏ hàm> = <tên hàm>; // Dạng ngắn gọn
<biến con trỏ hàm> = &<tên hàm>; // Dạng sử dụng địa chỉ
Hàm được gán phải cùng dạng (ds tham số)
Ví dụ
int Cong( int x, int y); // Hàm
int Tru( int x, int y); // Hàm
int (*tinhtoan)(int x, int y); // Con trỏ hàm
tinhtoan = Cong; // Dạng ngắn gọn
tinhtoan = &Tru; // Dạng sử dụng địa chỉ
tinhtoan = NULL; // Không trỏ đến đâu cả
32
33. Thu hoạch
Câu nói nào sau đây là đúng !
A. “ Dùng con trỏ để giải bài A”
B. “ Giải bài tập B sử dụng con trỏ”
C. “ Giải bài tập C bằng con trỏ ”
33
34. Tài liệu tham khảo
• Kĩ thuật lập trình – Đặng Bình Phương –
Khoa Công nghệ thông tin - ĐH KHTN.
• Everything you need to know about pointers
in C- Version 1.3 - Copyright 2005–2010 Peter
Hosey.
• Understanding Pointers In C By Yashwant
Kanetkar.
34