Tải bản đầy đủ (.pptx) (55 trang)

6. Tuan 9-10 Con tro va mang dong ppsx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (230.18 KB, 55 trang )

BÀI GIẢNG
KỸ THUẬT LẬP TRÌNH

Nguyễn Duy Đỉnh
1
Tuần 9 - 10 – Con trỏ

Dữ liệu kiểu con trỏ

Các phép toán với con trỏ

Các lệnh cấp phát bộ nhớ

Mảng động

Truyền tham số mảng
2
Dữ liệu kiểu con trỏ

Bộ nhớ RAM là mỗi chuỗi các ô nhớ có địa chỉ xác định được đánh số từ
0x0000

Địa chỉ của một ô nhớ là vị trí của nó trong bộ nhớ RAM

Địa chỉ tối đa phụ thuộc vào dung lượng nhớ. Ví dụ

RAM 256 Bytes có dải địa chỉ từ 0x00 đến 0xFF

RAM 1M có dải địa chỉ từ 0x0 0000 đến 0xF FFFF

RAM 1G có dải địa chỉ từ 0x0000 0000 đến 0x3FFF FFFF





Bản đồ nhớ là cách bố trí các ô nhớ trong bộ nhớ RAM
3

Ví dụ, bản đồ nhớ của RAM 256 Bytes

Địa chỉ ô nhớ màu vàng - 0x0B; đỏ - 0x14; xanh – 0x29

Mỗi ô nhớ chiếm 1 Byte bộ nhớ
Dữ liệu kiểu con trỏ
0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00
0x10
0x20

0xF0
4
Dữ liệu kiểu con trỏ

Các biến khi được khai báo sẽ chiếm một ví trí nào đó trong bộ nhớ RAM

Tên biến chính là cách mà ta gọi tên của ô nhớ

Chẳng hạn, ô nhớ màu vàng, màu đỏ, màu xanh là cách mà ta gọi tên các
ô nhớ 0x0B, 0x14, 0x28

Ví dụ: Bộ nhớ RAM có 256Bytes, nếu ta khai báo:
short int a, b;

a = 0x1234;
b = a << 1;
5
Do biến a, b kiểu short int là kiểu dữ liệu 2 bytes nên mỗi biến a, b
sẽ chiếm 2 bytes (2 ô) trong bản đồ nhớ của bộ nhớ RAM

Chẳng hạn, giả sử bộ nhớ cấp cho a bắt đầu từ 0x0A

bộ nhớ cấp cho b bắt đầu từ 0x12
Dữ liệu kiểu con trỏ
0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00
0x10
0x20

0xF0
6

Do biến a, b kiểu short int là kiểu dữ liệu 2 bytes nên mỗi biến
a, b sẽ chiếm 2 bytes (2 ô) trong bản đồ nhớ của bộ nhớ RAM

Chẳng hạn, giả sử bộ nhớ cấp cho a bắt đầu từ 0x0A

bộ nhớ cấp cho b bắt đầu từ 0x12
Dữ liệu kiểu con trỏ
0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00
0x10
0x20


0xF0
7

Gán a = 0x1234, nghĩa là ghi các giá trị 12 và 34 vào các ô
nhớ tương ứng: [0x0A] = 12 và [0x0B] = 34

Tương tự, gán b = a<<1; //b=2468, nghĩa là ghi các giá trị 24
và 68 vào các ô nhớ [0x12] = 24 và [0x13] = 68
Dữ liệu kiểu con trỏ
0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00 34 12
0x10 68 24
0x20

0xF0
8

Trên thực tế, khi máy tính thực hiện chương trình, nó chỉ quan
tâm đến địa chỉ ô nhớ và làm việc với địa chỉ ô nhớ

a, b chẳng qua chỉ là tên mà người lập trình gọi ô nhớ sao cho
thuận tiện khi lập trình

Thông thường, ta chỉ quan tâm đến giá trị của các biến a, b mà
không cần biết nó ở vị trí nào trong bản đồ nhớ
Dữ liệu kiểu con trỏ
9

Tuy nhiên, trong rất nhiều trường hợp, địa chỉ và vùng bộ nhớ
chiếm giữ của các biến này cũng rất quan trọng, giúp cho việc

thực hiện thuật toán nhanh hơn, thuận tiện hơn, linh hoạt hơn

Chẳng hạn,

khi muốn thay đổi giá trị của tham số truyền vào một hàm

khi muốn làm việc với các ô nhớ liên tiếp nhau trong bộ nhớ RAM
(mảng) hoặc trên ổ cứng (tệp)

khi muốn làm việc với danh sách liên kiết

khi muốn sử dụng bộ nhớ một cách linh hoạt


Dữ liệu kiểu con trỏ
10

Từ đó, Visual C định nghĩa kiểu dữ liệu con trỏ: là kiểu dữ liệu
dùng để lưu địa chỉ của các đối tượng khác

Khai báo: thêm dấu * trước tên biến. Ví dụ
int a, *b;

Khai báo biến a kiểu nguyên, biến b là biến con trỏ kiểu
nguyên, lưu địa chỉ của ô nhớ nào đó mà giá trị của ô nhớ đó là
kiểu int

Hay nói cách khác, biến b là một con trỏ, trỏ tới các phần tử có
kiểu int
Dữ liệu kiểu con trỏ

11

Ví dụ khai báo biến kiểu con trỏ:
float *x, *y;
FILE *f;
char *s;

x, y là các con trỏ kiểu float, lưu địa chỉ của các ô nhớ mà giá trị
của các ô nhớ đó có kiểu kiểu float, hay x, y trỏ tới các phần tử
kiểu float

f con trỏ kiểu file, trỏ tới các tệp

s con trỏ kiểu char, trỏ tới các phần tử kiểu char
Dữ liệu kiểu con trỏ
12

Các biến thông thường khi được khai báo sẽ nằm trong bộ nhớ
Stack (có dung lượng mặc định là 1M Bytes)

Các biến con trỏ khi được khai báo sẽ nằm trong bộ nhớ Stack

Bản thân mỗi biến con trỏ chiếm 4 Bytes bộ nhớ

Tuy nhiên, vùng nhớ mà biến con trỏ quản lý là vùng nhớ nằm
trong bộ nhớ Heap

Bộ nhớ Heap thường lớn hơn nhiều so với bộ nhớ Stack
Dữ liệu kiểu con trỏ
13


Toán tử & và toán tử *

Toán tử &: chỉ địa chỉ của một biến trong bản đồ nhớ

Toán tử *: chỉ giá trị của ô nhớ có địa chỉ mà biến con trỏ
trỏ tới

Ví dụ:

int a, *b;

&a: địa chỉ của ô nhớ mà biến a chiếm trong bộ nhớ

*b: giá trị của ô nhớ mà con trỏ b trỏ tới

&b: địa chỉ của ô nhớ mà biến con trỏ b chiếm
Các phép toán với con trỏ
14

Ví dụ:
int a, b, *c;
a = 0x1234;
b = a << 1;

Với bản đồ nhớ dưới đây thì ta có: &a = 0x0A; &b = 0x12;

Giả sử con trỏ c đang trỏ tới ô nhớ 0x12 thì: *c = 2468;
Các phép toán với con trỏ
0 1 2 3 4 5 6 7 8 9 A B C D E F

0x00 34 12
0x10 68 24
0x20

0xF0
15

Phép gán:
short int *a, *b, c, d, *e;

a = &c; //Con trỏ a trỏ tới địa chỉ của biến c
b = &d; //Con trỏ b trỏ tới địa chỉ của biến d
*a = 0x1234; //Giá trị của ô nhớ mà a trỏ tới = 0x1234
//Tương đương với lệnh gán c = 0x1234
*b = *a; //Giá trị của ô nhớ mà b trỏ tới bằng giá
//trị ô nhớ mà a trỏ tới bằng 0x1234
//Tương đương với lệnh gán d = 0x1234
e = a;//Con trỏ e trỏ tới địa chỉ mà con trỏ a //đang trỏ
tới, tức là trỏ tới địa chỉ của //biến c
a = &d; //Con trỏ a trỏ tới địa chỉ của biến d, //không trỏ
tới địa chỉ của biến c nữa
*e = d+1; //Giá trị của ô nhớ mà con trỏ e đang trỏ
//đến tăng 1 đơn vị, tương đương với việc //tăng c 01
đơn vị = 0x1235
Các phép toán với con trỏ
16

Ví dụ: c ở địa chỉ 0x0A, d ở địa chỉ 0x12
short int *a, *b, c, d, *e;


a = &c; //a = 0x0A
b = &d; //b = 0x12
*a = 0x1234; //c = 0x1234
*b = *a; //d = 0x1234
e = a; //e = 0x0A
*e = d+1; //c = 0x1235
a = &d; //a = 0x0x12
Các phép toán với con trỏ
0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00 34 12
0x10 34 12
0x20

0xF0
17

Giả sử c ở địa chỉ 0x0A, d ở địa chỉ 0x12
short int *a, *b, c, d, *e;

a = &c; b = &d;

Dự đoán kết quả của phép toán sau?
a ; b ++;
a -= 2; b += 3;
Các phép toán với con trỏ
0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00 12 34
0x10 24 68
0x20


0xF0
18

Giả sử c ở địa chỉ 0x0A, d ở địa chỉ 0x12
short int *a, *b, c, d, *e;

a = &c; b = &d;

Dự đoán kết quả của phép toán sau?
a ; //a trỏ tới ô nhớ có địa chỉ 0x08
b ++; //b trỏ tới ô nhớ có địa chỉ 0x14
a -= 2; //a trỏ tới ô nhớ có địa chỉ 0x04
b += 3; //b trỏ tới ô nhớ có địa chỉ 0x1A

Phép tịnh tiến: a += i hoặc b -= i sẽ dịch chuyển con trỏ
a hoặc b đi (i*sizeof(*a)) ô nhớ trong bản đồ nhớ theo
hướng tiến hoặc lùi phụ thuộc vào dấu của phép tịnh tiến
Các phép toán với con trỏ
19

Lưu ý: con trỏ không có phép nhân (*) hoặc chia (/) mà chỉ có
phép gán (=), phép lấy địa chỉ (&), phép lấy giá trị (*) và phép
tịnh tiến (+ hoặc -)
Các phép toán với con trỏ
20

Ta đã gặp một vài trường hợp hàm làm thay đổi giá trị của
tham số truyền vào dòng lệnh gọi nó như:

Hàm scanf(), fscanf(),


Bằng cách dùng con trỏ, ta có thể thay đổi giá trị của tham số
truyền vào dòng lệnh gọi hàm.
Ứng dụng truyền tham số cho hàm
21

Ví dụ 9.1: Lập trình hàm đổi giá trị 2 biến bất kỳ truyền vào
tham số của hàm
void Swap(int x, int y)
{
int Tmp;
Tmp = x;
x = y;
y = Tmp;
return;
}

int a = 2, b = 5;
Swap(a, b);
Ứng dụng truyền tham số cho hàm

Hàm Swap(x, y) thực hiện việc đảo giá
trị của các biến x, y trong nội dung
hàm. Tuy nhiên, khi hàm kết thúc, giá
trị của x, y vẫn không bị thay đổi trong
toàn cục

 Nguyên nhân: Do hàm chỉ sử dụng
giá trị của các biến này mà không quan
tâm đến nó là biến nào

22

Ví dụ 9.1: Lập trình hàm đổi giá trị 2 biến bất kỳ truyền vào
tham số của hàm
void Swap2(int *x, int *y)
{
int Tmp;
Tmp = *x;
*x = *y;
*y = Tmp;
return;
}

int a = 2, b = 5;
Swap2(&a, &b);
Ứng dụng truyền tham số cho hàm

Hàm Swap2(&x, &y) không quan tâm
đến tên biến mà quan tâm đến ô
nhớ lưu giá trị của biến

Hàm Swap2(&x, &y) thực hiện việc
đảo giá trị của các ô nhớ mà con trỏ
x và y trỏ tới.
23

Note:

Việc truyền tham số sẽ làm cho hàm trở nên linh hoạt hơn,
không phụ thuộc vào biến toàn cục, do đó, có thể được

dùng đi dùng lại trong các project khác nhau mà không phụ
thuộc vào project cụ thể.

Việc truyền tham số sẽ đảm bảo tính đóng kín và tính kế
thừa cho hàm
Ứng dụng truyền tham số cho hàm
24

Ví dụ 9.2: Lập trình hàm GPTB2() giải phương trình bậc 2 với
các hệ số và các ẩn được truyền vào dưới dạng tham số
void GPTB2(float a, float b, float c, float *x1, float *x2)
{

Delta = b*b-4*a*c;
*x1 = (-b+sqrt(Delta))/(2*a);
*x2 = (-b-sqrt(Delta))/(2*a);
return;
}

float a, b, c, x1, x2;
GPTB2(a, b, c, &x1, &x2);

Ứng dụng truyền tham số cho hàm
25

×