40
CƠ SỞ LẬP TRÌNH CON TRỎ

CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

  • Upload
    others

  • View
    6

  • Download
    0

Embed Size (px)

Citation preview

Page 1: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

CƠ SỞ LẬP TRÌNH

CON TRỎ

Page 2: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Nội dung1. Khái niệm2. Khai báo3. Thao tác với con trỏ4. Các phép toán với con trỏ5. Con trỏ và mảng6. Mảng con trỏ7. Con trỏ hàm

2

Page 3: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Khái niệm Con trỏ (Pointer): Là biến lưu trữ địa chỉ vùng nhớ của một đối tượng (biến,

hàm). Có tác dụng như truyền tham chiếu. Có liên quan chặt chẽ đến mảng và chuỗi.

Biến con trỏ (Pointer variable) Chứa địa chỉ vùng nhớ thay vì chứa giá trị. Thông thường, biến chứa giá trị (tham chiếu trực tiếp). Con trỏ chứa địa chỉ của biến mang giá trị cụ thể (tham

chiếu gián tiếp). Cần phân biệt nội dung của vùng nhớ và địa chỉ của nó.Ví dụ:

3

paFF0024AF0536

a18

FF0024

Page 4: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Khai báo<type> * <tên biến trỏ>;Trong đó, <type> là kiểu dữ liệu của biến mà con

trỏ đang trỏ đến.Ví dụ:int * a;float * b;char * c;

Con trỏ có cùng kiểu dữ liệu với kiểu dữ liệu củabiến mà nó trỏ tới.

Kích thước của một biến con trỏ tùy thuộc vàoOS.

4

Page 5: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Thao tác với con trỏToán tử lấy địa chỉ &: Trả về địa chỉ vùng nhớ củabiến.&x địa chỉ của biến xVí dụ: int x = 10;

• Sử dụng con trỏ để truy xuất địa chỉ của biến x.• Biến x có một địa chỉ cụ thể trong vùng nhớ.• Để lấy địa chỉ biến x: sử dụng toán tử & trước x

(&x).

5

Page 6: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Thao tác với con trỏvoid main()

{

int x = 10;

int *px;

px = &x;

printf("Memory address: %d\n",px);//1024printf("Value of x: %d\n",*px); //10*px += 10;

printf("Value of x: %d\n",x); //20}

6

Page 7: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Thao tác với con trỏvoid main(){int x = 10, y = 15;int *px, *py;px = &x; //px = địa chỉ của xpy = &y; //py = địa chỉ của y*py = 20; //giá trị trỏ bởi py = 20*px = *py;//gtrị trỏ bởi px= gtrị trỏ bởi pypx = py; //phép gán con trỏ*px = 30; //giá trị trỏ bởi px = 30printf(“x = %d\n”,x);printf(“y = %d\n”,y);printf("px = %d, *px = %d\n",px, *px);printf("py = %d, *py = %d\n",py, *py);

} 7

Page 8: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Thao tác với con trỏCần phân biệt

*px = *py; //gán giá trị của vùng nhớ mà py đang trỏ đến cho vùng nhớ mà px đang trỏ đến

Vớipx = py; //sau lệnh này px và py cùng trỏ đến 1 vùng nhớ

8

Page 9: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Thao tác với con trỏ Con trỏ NULL:

• Con trỏ không chứa địa chỉ của bất kỳ vùng nhớ nào.• Nên khởi tạo giá trị NULL hoặc địa chỉ của vùng nhớ nào đó cho biến

trỏ lúc khai báo.Ví dụ: int * p = NULL; Con trỏ void *:

• Tương thích với mọi kiểu dữ liệu mà một biến trỏ trỏ đến, có thể gángiá trị của con trỏ thuộc một kiểu bất kỳ nào đó cho con trỏ void *.

• Không thể thực hiện các phép tính số học.• Không thể (dùng *) lấy dữ liệu.

VD: void *px,*py;int x=1; float y=0.1;px=&x;py=&y;px+=2; //Error

9

Page 10: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Các phép toán với con trỏTăng, giảm con trỏ (++ hoặc --)Cộng, trừ 1 số nguyên với 1 con trỏ.Con trỏ có thể trừ lẫn nhau (cộng, trừ với con trỏlà vô nghĩa trừ khi dùng cho con trỏ mảng).Ví dụ: Mảng p có 5 phần tử kiểu int (4 byte)

p trỏ đến phần tử đầu tiên p[0], tại địa chỉ 5000.p+=3; //trỏ đến phần tử thứ 4 p[3], tại địa chỉ5012p--; //trỏ đến phần tử thứ 3 p[2], tại địa chỉ 5008

10

Page 11: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Các phép toán với con trỏGán con trỏ (Pointer assignment):

• Một con trỏ có thể được gán cho con trỏ khác nếu cả 2cùng kiểu.

• Nếu không cùng kiểu thì phải ép kiểu (cast).So sánh con trỏ (Pointer comparison):

• Sử dụng các toán tử quan hệ để so sánh địa chỉ chứatrong con trỏ.

• Thường dùng để xác định khi con trỏ = NULL.

11

Page 12: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Truyền tham số con trỏ cho hàmTrong phần khai báo và định nghĩa hàm, khai báokiểu dữ liệu con trỏ là <type> *.Trong lời gọi hàm, ta phải cung cấp biểu thức có trịlà địa chỉ của vùng nhớ cùng kiểu với kiểu của thamsố biến trỏ tương ứng.Ví dụ: hàm swap

void swap(int *px, int *py){int temp = *px;*px = *py;*py = temp;

}Gọi hàm: swap (&a, &b);

12

Page 13: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Con trỏ và mảngCon trỏ và mảng có mối quan hệ chặt chẽ:Tên mảng cũng như hằng con trỏ(constant pointer).Có thể dùng chỉ số đối với các con trỏ.

13

Page 14: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Con trỏ và mảngVí dụ:int a[10];int *pa;pa = &a[0];//pa chứa địa chỉ của a[0]…int x = *pa;//copy content of a[0] to xint y = *(pa+1);//copy content of a[1] to y

14

Page 15: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Con trỏ và mảnga là địa chỉ của a[0] pa = &a[0]; có thể viết: pa = a; *(a+i) == *(pa+i)

15

Page 16: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Con trỏ và mảngSử dụng con trỏ để truyền mảng:1. #include <stdio.h>2. #define SIZE 53. void getArray(int *a, int size);4. main() {5. int an_array[SIZE];6. getArray(an_array, SIZE);7. return 0;8. }9. void getArray(int *a, int size){10. for(int i=0; i<size; i++) {11. printf(“a[%d]=“, i);12. scanf(“%d”, &a[i]);13. }14. } 16

Page 17: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Mảng con trỏ Kiểu phần tử của biến mảng có thể là

kiểu con trỏ. Các biến có địa chỉ chứatrong các phần tử mảng con trỏ là mộtmảng, nhưng có vùng nhớ không liên tục. Thường dùng để lưu mảng của chuỗi. Ví dụ: char *s[5] = {“Orange”, “Mango”,

“Coconut”, “Longan”, “Banana”}; Mỗi phần tử của s trỏ đến char * (1 chuỗi) Mảng không chứa chuỗi, chỉ trỏ đến

chuỗi.17

Page 18: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Con trỏ và mảng 2 chiều Để truy xuất các phần tử trong mảng 2 chiều, có

thể sử dụng con trỏ như sau:float *pa, a[4][6];pa = (float *)a;

Khi đó:p trỏ đến a[0][0]p+1 trỏ đến a[0][1]p+2 trỏ đến a[0][2]p+3 trỏ đến a[1][0]p+4 trỏ đến a[1][1]p+5 trỏ đến a[1][2]…

18

Page 19: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Con trỏ và mảng 2 chiều Chú ý: a (trong a[m][n]) là một hằng con trỏ trỏ

đến các dòng của ma trận, khi đó:a trỏ đến dòng đầu tiên.a+1 trỏ đến dòng thứ 2.a+2 trỏ đến dòng thứ 3.…

Sử dụng (float *)a để trỏ đến a[0][0].Địa chỉ của a[i][j]: (float *)a + i*n + j;

19

Page 20: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Con trỏ đa cấp Bản thân biến trỏ cũng có địa chỉ, do đó, có thể

chứa địa chỉ của nó trong 1 biến trỏ khác, gọi làcon trỏ trỏ đến con trỏ, hay con trỏ 2 cấp. Số lượng dấu “*” xác định cấp của 1 biến trỏ. Con trỏ 2 cấp có liên quan mật thiết với mảng 2

chiều. Ví dụ:

int a[2][3];int **p = new int *[2];p[0] = a[0];p[1] = a[1];

20

Page 21: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Cấp phát động Cấp phát tĩnh: Cấp phát vùng nhớ lúc biên dịch

chương trình. Cấp phát động: Cấp phát vùng nhớ lúc thực

hiện chương trình. Vùng nhớ của các đối tượng (biến) cấp phát

động sẽ được đặt tại HEAP. Trong C, dùng các hàm malloc, calloc, realloc,

… được khai báo trong thư viện <alloc.h>,<stdlib.h> để cấp phát.

21

Page 22: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Cấp phát động Hàm malloc:

void * malloc(size_t size);Trong đó: size_t: kích thước vùng nhớ cần cấp phát, size_t là

kiểu dữ liệu định nghĩa trong <stdlib.h>, tương đương với một unsigned int.

void *: có thể cấp phát vùng nhớ cho kiểu dữ liệu bất kỳ.

VD:int* pi;int size = 5;pi = (int*)malloc(size * sizeof(int));

Chú ý: malloc trả về NULL nếu không thể cấp phát vùng nhớ hoặc size = 0. 22

Page 23: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Cấp phát động Hàm calloc:

void *calloc(size_t num, size_t size);

Hàm realloc:void *realloc(void *ptr, size_tnewsize);

23

Page 24: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Cấp phát động Trong C++, dùng toán tử new: Để cấp phát vùng nhớ trên HEAP có kích thước =

sizeof(<type>)<biến> = new <type>; Để cấp phát vùng nhớ trên HEAP có kích thước =

sizeof(<type>) * n<biến> = new <type> [n]; Ví dụ:

int *x = new int;int *a = new int [5];

24

Page 25: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Cấp phát động Phải thu hồi các vùng nhớ đã cấp phát khi

không còn sử dụng để sử dụng vào việc khác. Nếu không thu hồi, bộ nhớ sẽ nhanh chóng cạn

kiệt. Trong C, sử dụng hàm free(ptr), với ptr là biến

con trỏ chỉ đến vùng nhớ đã được cấp phát độngbàng các hàm malloc, calloc, realloc, …void free(void *ptr);

25

Page 26: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Cấp phát động Trong C++, dùng toán tử delete:

delete <biến>;delete [] <biến>; Ví dụ:

free (pi);delete x;delete [] a;

26

Page 27: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Ví dụ cấp phát động mảng 1 chiều#include <iostream>#include <iomanip>#include <stdlib.h>#include <malloc.h>using namespace std;void randomInit(int* a, int n);void output(int* a, int n);

27

Page 28: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Ví dụ cấp phát động mảng 1 chiềuvoid main(){

int* a;int n;cout << "Cho biet so phan tu ? : ";cin >> n;// a = (int*) calloc(n, sizeof(int));a = new int [n];randomInit(a, n);output(a, n);// free(a);delete [] a;

}28

Page 29: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Ví dụ cấp phát động mảng 1 chiềuvoid randomInit(int* a, int n){for (int i = 0; i < n; i++)

a[i] = rand()% 100;}void output(int* a, int n){for (int i = 0; i < n; i++ )

cout << setw(4) << a[i];cout << endl;

}29

Page 30: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Ví dụ cấp phát động mảng 2 chiều#include <iostream>#include <iomanip>#include <stdlib.h>#include <malloc.h>using namespace std;void randomInit(int** a, int m, int n);void output(int** a, int m, int n);

30

Page 31: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Ví dụ cấp phát động mảng 2 chiềuvoid main(){

int** a;int m, n;cout << "Cho biet so dong ? : "; cin >> m;cout << "Cho biet so cot ? : "; cin >> n;a = new int * [m];for (int i = 0; i < m; i++)

a[i] = new int [n];randomInit(a, m, n);output(a, m, n);for (int i = 0; i < m; i++)

delete [] a[i];delete [] a;

} 31

Page 32: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Ví dụ cấp phát động mảng 2 chiềuvoid randomInit(int** a, int m, int n){

for (int i = 0; i < m; i++)for (int j = 0; j < n; j++)

a[i][j] = rand()% 100;}void output(int** a, int m, int n){

for (int i = 0; i < m; i++){

for (int j = 0; j < n; j++)cout << setw(4) << a[i][j];

cout << endl;}

} 32

Page 33: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Con trỏ hàm Trong C/C++, tên hàm là địa chỉ vùng nhớ của

chỉ thị đầu tiên của hàm, do đó ta có thể gán giátrị địa chỉ này cho 1 biến trỏ có kiểu cùng kiểuvới kiểu giá trị trả về của hàm. Đó gọi là con trỏhàm.

Có thể gọi thực hiện một cách gián tiếp một hàmnào đó thông qua con trỏ hàm.

Hàm có thể được dùng làm tham số cho mộthàm khác nhờ vào con trỏ hàm.

Khai báo con trỏ hàm:<type> (*<tên con trỏ hàm>)([Danh sách các tham số]);

33

Page 34: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Con trỏ hàmVí dụ: Giả sử có các khai báo:

int a, b;void swap(int * px, int * py);void (* pf)(int *, int *);

Có thể gọi hàm swap gián tiếp như sau:pf = swap();pf (&a, &b);

34

Page 35: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Bài tập1. Cho trước mảng nguyên. Viết các hàm sau: (sử

dụng con trỏ)- Nhập mảng.- Xuất mảng.- Tìm kiếm giá trị x trong mảng.- Tìm phần tử lớn nhất, nhỏ nhất trong mảng.- Thêm 1 phần tử vào mảng.- Xóa 1 phần tử khỏi mảng.- Kiểm tra mảng có thứ tự tăng? Giảm? Hay

không có thứ tự?- Đảo ngược thứ tự các phần tử trong mảng.

35

Page 36: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Bài tập- Tính tổng các phần tử trong mảng.- Tính giá trị trung bình của mảng.- Sắp xếp mảng theo thứ tự tăng dần, giảm dần.- Sắp xếp mảng theo thứ tự tăng dần và loại bỏ

các phần tử trùng nhau.- Liệt kê các phần tử là số chẵn, số lẻ, số âm, số

dương, số nguyên tố.- Đếm số lượng các phần tử là số chẵn, số lẻ,

số âm, số dương, số nguyên tố.Viết chương trình áp dụng các hàm trên.

36

Page 37: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Bài tập2. Viết chương trình nhập vào 2 tập hợp A

và B. Xây dựng hàm:- Hợp của 2 tập hợp.- Giao của 2 tập hợp.- Hiệu của 2 tập hợp.3. Viết chương trình nhập vào 2 mảng A và

B, ghép 2 mảng A và B đưa vào mảng C.

37

Page 38: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Bài tập4. Cho mảng 2 chiều không giới hạn số

lượng phần tử. Viết hàm:- Nhập mảng.- Xuất mảng.- Tìm phần tử nhỏ nhất, lớn nhất trong

mảng.- Tính tổng các phần tử trong mảng.- Tính tổng từng dòng trong ma trận.- Tính tổng, hiệu, tích 2 ma trận.

38

Page 39: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Bài tập5. Viết chương trình xử lý chuỗi ký tự gồm các chức

năng sau: (Dùng con trỏ để cài đặt, không dùnghàm thư viện)

- Tính chiều dài của chuỗi nhập vào.- Sao chép 2 chuỗi với nhau.- So sánh 2 chuỗi với nhau.- Tìm 1 ký tự trong chuỗi nhập.- Tìm chuỗi con trong chuỗi nhập.- Thêm chuỗi con vào chuỗi nhập tại vị trí k.- Xóa chuỗi con trong chuỗi nhập.- Loại bỏ các khoảng trắng thừa (ký tự Space, Tab)

trong chuỗi nhập.

39

Page 40: CON TRỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ cần cấp phát, size_t là ... nào đóthông qua con trỏhàm. Hàm có thể

Bài tập- Đảo ngược chuỗi nhập.- Kiểm tra 2 chuỗi nhập có bằng nhau?- Kiểm tra chuỗi nhập có đối xứng?- Đếm tần số xuất hiện của các ký tự trong chuỗi

nhập.- Đếm số từ trong chuỗi nhập.6. Viết chương trình nhập vào danh sách các từ

tiếng Anh, sắp xếp các từ theo thứ tự tăngdần.

40