con trỎ · *px = 30; //giá trị trỏ bởi px = 30 ... size_t: kích thước vùng nhớ...

Post on 07-Feb-2020

6 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

CƠ SỞ LẬP TRÌNH

CON TRỎ

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

top related