Bài giảng Kỹ thuật lập trình: Bài 7 do TS. Ngô Hữu Dũng biên soạn cung cấp cho người học các kiến thức: Khái niệm con trỏ, con trỏ và địa chỉ, kiểu con trỏ, phạm vi ứng dụng con trỏ, lợi hại của con trỏ, kiểu nguyên thủy, con trỏ và hằng số, con trỏ và đối số của hàm,...
Trang 1Kỹ thuật lập trình
Bài 7 – Kiểu con trỏ
TS Ngô Hữu Dũng
Trang 2Khái niệm con trỏ (pointer)
Con trỏ là biến mà giá trị của nó là địa chỉ bộ nhớ
int * p; // Khai báo con trỏ p
p = &i; // Con trỏ p được gán bằng địa chỉ của biến i
Ta nói con trỏ p “trỏ vào” biến i
Trang 3Con trỏ và địa chỉ
1 int x; // Biến số nguyên x
2 int *p; // Con trỏ p kiểu số nguyên
3 p = &x; // p trỏ vào x
4 x = 20;
5 printf( "%d " , x); // Giá trị của x
6 printf( "%d " , *p); // Giá trị của x
7 printf( "%p " , &x); // Địa chỉ của x
8 printf( "%p " , p); // Địa chỉ của x
9 *p = 40;
10 printf( "Gia tri: %d = %d" , *p, x);
11 printf( "Dia chi: %p = %p" , p, &x);
Trang 69 printf( "x=%d\ny=%d\n" ,*px,*py);
10 printf( "%d+%d=%d\n" ,x,y,*px + *py);
11 printf( "%d*%d=%d\n" ,x,y,*px * *py);
12 printf( "&x=%p\n&y=%p\n" ,px,py);
Trang 7Phạm vi ứng dụng con trỏ
Con trỏ có thể trỏ vào bất kỳ dữ liệu nào
Một kiểu nguyên thủy: int, float, char
NULL (rỗng, không trỏ đến đâu)
Trang 8Lợi hại của con trỏ
Có thể dùng để thao tác với bất kỳ loại dữ liệu nào
Thuật lợi để truy cập đến cấu trúc dữ liệu lớn
Sử dụng bộ nhớ linh hoạt
Cấp phát / giải phóng bộ nhớ động trong quá trình thực thi
Cấp phát bộ nhớ mới: Hàm malloc trong stdlib.h
Giải phóng bộ nhớ: Hàm free trong stdlib.h
Nếu sử dụng con trỏ không cẩn thận
Dễ nhầm lẫn
Khó tìm lỗi
Trang 9Kiểu nguyên thủy
Trang 10 Khởi tạo con trỏ: int *p = NULL;
Kiểm tra một con trỏ
if (p != NULL) printf(“%d”,*p);
if (p) printf(“%d”, *p);
Kết thúc một danh sách dữ liệu
Trang 11Con trỏ và hằng số
Con trỏ trỏ vào hằng số
int m, n;
const int *ip = &m;
ip = &n; // OK! Có thể thay đổi giá trị con trỏ
*ip = 10; // ERROR !!! Không thể thay đổi giá trị của biến
Con trỏ là hằng số: Chỉ trỏ vào một biến
int m, n;
int * const cip = &m;
*cip = 10; // OK! Có thể thay đổi giá trị của biến
cip = &n; // ERROR !!! Không thể thay đổi giá trị con trỏ
Trang 12Con trỏ và đối số của hàm
1 void swap ( int *px , int *py ) // Tham biến
2 {
Trang 13 pa = a; // Mặc định trỏ vào phần tử đầu tiên của mảng, a[0]
pa
b[0] b[1] b[2] b[3] b[4] b[5] b[6]
Trang 16Mảng là con trỏ hằng số (constant pointer)
Mảng là con trỏ hằng số
int a[10];
int *pa;
pa = a; // a là con trỏ, pa có thể được gán cho a, không cần &a
pa++; // Con trỏ pa có thể thay đổi giá trị
a = pa; // ERROR! a là hằng số
a++; // ERROR! a là hằng số
Mảng a là con trỏ trỏ vào phần tử đầu tiên của mảng và không thể thay đổi giá trị địa chỉ
Trang 17Truyền đối số là mảng / chuỗi
Truyền đối số là một mảng vào một hàm
void sapxep(int a[] , int n)
Địa chỉ của phần tử đầu tiên của mảng được truyền: &a[0]
Có thể thay thế bằng con trỏ
void sapxep(int *a , int n)
Tương tự với chuỗi
Tham số function(char s[] ){…} và function(char *s ){…} là tương đương
Trang 18Ví dụ truyền đối số là mảng/chuỗi
Trang 19Các phép toán của con trỏ
Phép gán hai con trỏ cùng kiểu dữ liệu
int a, *pa, *pb; pa=&a; pb = pa;
Cộng/trừ con trỏ với số nguyên
Tăng/giảm giá trị của con trỏ theo sizeof()
Ví dụ: pb+n tăng giá trị của p lên n x sizeof(int)
So sánh giữa hai con trỏ trỏ vào phần tử trong cùng một mảng
Các phép toán ==, !=, >, <, >=, <=
Phép trừ giữa hai con trỏ trỏ vào phần tử trong cùng một mảng
Gán hoặc so sánh với NULL (zero)
Trang 20Ví dụ về các phép toán của con trỏ
1 int printRevertArray( int *pa , int n)
Trang 238 ptr = &today; // Trỏ vào cấu trúc
9 (*ptr).day = 15; // Truy cập member, cách 1
Trang 255 printf( "%s " ,pSV->ten+2); //Từ ký tự ‘u’->hết
6 printf( "%d " ,++pSV->pB->day); //Tăng day
Trang 26Con trỏ và hàm
Hàm có thể trả về kiểu con trỏ
Đối số kiểu con trỏ
Trang 30Hết bài 7
Khái niệm con trỏ
Khái quát các ứng dụng của con trỏ