Transcript P+i

Chương 5
CON TRỎ
(Pointer)
1
1. Khái niệm
 Một con trỏ là một biến chứa địa chỉ ô nhớ. Địa
chỉ này là vị trí của một đối tượng khác trong bộ
nhớ.
 Ví du:
float x; //x là một biến có kiểu float
x=12.5; //x có giá trị là 12.5
Và lúc này, biến x chiếm 4 ô nhớ liên tiếp, giả sử
mỗi ô nhớ có kích thước 1 byte.
2
Địa chỉ của ô nhớ
 Địa chỉ của ô nhớ là số thứ tự của byte đầu tiên trong
một dãy các byte liên tiếp. Địa chỉ này được tính ở hệ đếm
16.
 Ví dụ :
int a[4];
Mỗi biến kiểu int chiếm 2 bytes, như vậy bộ nhớ sẽ
dành 8 ô nhớ cho biến a.
Và cách đánh địa chỉ như sau: giả sử địa chỉ của biến
a[0] là ffee
3
2. Con trỏ
 Con trỏ là một kiểu dữ liệu đặt biệt dùng để quản lý địa chỉ
của các ô nhớ.
 Con trỏ kiểu <T> chỉ được dùng để chứa địa chỉ của biến
kiểu <T>; nghĩa là con trỏ kiểu int chỉ được dùng để chứa
địa chỉ của biến kiểu int, con trỏ kiểu float chỉ được dùng
để chứa địa chỉ của biến kiểu float.
 Cách khai báo như sau:
int
<type>* <PointerName>;
Ví dụ:
int *pi;
px
 Có thể khai báo con trỏ nhiều cấp, ví dụ:
int * *ppi;
float * * *pppf;
4
Phép lấy địa chỉ của một biến
 Kí hiệu: &
 Cách sử dụng:&<VarName>
 Ví dụ: int *pi=&a; // pi giữ địa chỉ của các biến nguyên a
Lưu ý:
Trong trường hợp trên, pi là một địa chỉ có giá trị địa chỉ là
&a, và &pi cũng là một địa chỉ nhưng giá trị địa chỉ này
không phải là &a mà là nơi ghi nhận giá trị &a, khi đó:
int **ppi=&pi; // con trỏ 2 cấp
5
Phép toán lấy giá trị tại một địa chỉ mà một con trỏ
đang trỏ tới
 Kí hiệu: *
 Cách sử dụng: *<PointerName>
 Ví dụ:
int a= 10; // biến a có giá trị 10
int *pi;
pi =&a; //pi giữ địa chỉ của biến a
 *pi là giá trị của a bà bằng 10
6
Ví dụ 1
int a=25, x;
int *y;
x=a;
y=&a;
a
y
x
25
y
7
Ví dụ 2
int a,
*p;
a=25;
p=&a;
m=*p ;
Cho biết Giá trị của m ?
8
Phép toán lấy thành phần của cấu trúc với con trỏ
 Kí hiệu: ->
 Cách sử dụng:
<Struct_Pointer_Var_Name> -> <Field_Name>
Ví dụ:
T là con trỏ struct gồm 2 thành phần a và b
thì T->a và T->b là giá trị các thành phần a,b tại địa
chỉ mà T trỏ vào.
Ghi chú: phép toán này chúng ta sẽ nói thêm trong
chương cấu trúc
9
Chú ý
 Cần chú ý rằng việc thay đổi giá trị của pi sẽ làm
cho giá trị của a thay đổi theo và ngược lại.
 Khi ta khai báo:
int a=10;
int *pi=&a; // pi giữ địa chỉ biến a
*pi=*pi +2;
 sẽ làm cho biến a có giá trị là 12
Lưu ý: Việc sử dụng và thao tác trên giá trị tại địa
chỉ của con trỏ chỉ được thực hiện sau khi con trỏ
đã có địa chỉ
10
Các thao tác trên con trỏ
 Lệnh gán con trỏ
Có thể dùng phép gán để gán giá trị của một con trỏ cho
một con trỏ khác có cùng kiểu
Khi ta khai báo:
int x;
int *p1, *p2;
p1 = &x; // p1 giữ địa chỉ biến a
p2 = p1;
cả hai p1 và p2 cùng trỏ đến biến x.
11
Các thao tác trên con trỏ
 Phép toán số học trên con trỏ
o Chỉ có 2 phép toán sử dụng trên con trỏ là phép cộng
và trừ
o Khi cộng (+) hoặc trừ (-) 1 con trỏ với 1 số nguyên
N; kết quả trả về là 1 con trỏ. Con trỏ này trỏ đến
vùng nhớ cách vùng nhớ của con trỏ hiện tại một số
nguyên lần kích thước của kiểu dữ liệu của nó.
12
Ví dụ:
char *a;
short *b;
long *c;
 Khi đó:
a = a + 1; // trỏ vào 1 byte tiếp theo
b = b + 1; // trỏ vào 2 bytes tiếp theo
c = c + 1; // trỏ vào 4 bytes tiếp theo
13
Minh họa
14
Ví dụ
#include <iostream.h>
#include<conio.h>
void main ()
{
int a = 20, b = 15, *pa, *pb, temp;
pa = &a; // con trỏ pa chứa địa chỉ của a
pb = &b; // con trỏ pb chứa địa chỉ của b
temp = *pa;
// kết quả xuất ra
màn hình ?
*pa = *pb;
*pb = temp;
a = 15
cout << "a = " << a << endl;
b = 20
cout << “b = ” << b;
}
15
3. Cấp phát bộ nhớ động
Ý nghĩa của việc cấp phát động bộ nhớ
 Biến toàn cục (global variables) được cấp phát bộ nhớ vào
lúc biên dịch. Biến cục bộ (local variables) dùng stack. Tuy
nhiên, biến toàn cục hay cục bộ không thể được tạo thêm
trong khi thực thi chương trình. Một số chương trình cần
thêm bộ nhớ khi thực thi giải pháp cho vấn đề này là cấp
phát động.
 Cấp phát động là phương tiện nhờ đó một chương trình có
thể dành được thêm bộ nhớ trong khi đang thực thi.
 Con trỏ cung cấp sự hổ trợ cho cấp phát bộ nhớ động trong
C/C++.
16
3.1. Cấp phát bộ nhớ động bởi C++
C++ cung cấp hai toán tử cấp phát bộ nhớ động:
new và delete.
−Toán tử new cấp phát bộ nhớ và trả về một con trỏ đến
byte đầu tiên của vùng nhớ được cấp phát.
−Toán tử delete thu hồi vùng nhớ được cấp phát trước
đó bởi toán tử new.
Cú pháp:
<pointer_var_name>=new <Type>;
Hoặc
<pointer_var_name>=new <Type>[N_blocks];
delete <pointer_var_name>;
17
Lưu ý:
 Cần kiểm tra xem việc cấp phát bộ nhớ có thành công hay
không. Nếu thành công, con trỏ trỏ đến địa chỉ đầu khối
nhớ được cấp phát và lúc này chúng ta mới có thể thực
hiện các thao tác trên con trỏ này.
 Khi ta khai báo:
int *pi;
pi= new int;
if (pi==NULL)
{
cout<<"khong du bo nho";
exit(0); /* kết thúc CTr, trả về mã lỗi Code (0)
cho hệ thống*/
}
18
Ví dụ:
#include <iostream.h>
int main()
{
int *p;
p = new int;
if (p==NULL)
{
cout<<"khong du bo nho";
exit(0);
}
*p = 100;
cout << "At " << p << " ";
cout << "is the value " << *p << "\n";
delete p;
return 0;
}
19
4. Cấp phát bộ nhớ động bởi C
Ngoài ra, C còn cung cấp một hàm cấp phát và giải
phóng bộ nhớ động như malloc(), calloc(), free()…
Cú pháp:
 cấp phát bộ nhớ
void * malloc (unsigned N_bytes);
void * calloc(unsigned N_Blocks,unsigned N_bytes);
 Hủy bộ nhớ
void free (<pointer_var_name>) // hủy bộ nhớ
(xem giáo trình)
20
Từ khóa sizeof
 Cú pháp:
sizeof(<type>)
Hoặc sizeof(<expression>)
 Tác dụng: Cho biết kích thước của kiểu dữ liệu
hoặc của biểu thức
 Khi ta khai báo:
cout<<sizeof(char);
// kết quả xuất ra
màn hình ?
cout<<sizeof(int);
cout<<sizeof(float);
1 2 4 8 10
cout<<sizeof(double);
cout<<sizeof(long double);
21
6. Con trỏ void
• Là một lọai con trỏ đặc biệt mà có thể trỏ đến bất
kỳ kiểu dữ liệu nào.
• Cú pháp:
void *PointerName;
• Ví dụ:
void *p;
p = &a; // p trỏ đến biến nguyên a
p = &f; //p trỏ đến biến thực f
22
Tương thích và không tương thích kiểu
 Kiểu dữ liệu khi khai báo biến con trỏ chính là kiểu dữ
liệu của ô nhớ mà con trỏ có thể trỏ đến.
 Địa chỉ đặt vào biến con trỏ phải cùng kiểu với kiểu của
con trỏ. Tuy nhiên, ta cũng có thể ép kiểu con trỏ về đúng
kiểu tương ứng khi dùng trong các biểu thức.
 Ví dụ:
int a;
float f;
int *pa; float *pf;
pa = &a; pf = &f; // hợp lệ
pa = &f; pf = &a; //không hợp lệ
23
7. Con trỏ null
 Khi một con trỏ chưa trỏ tới bất kỳ một địa chỉ nào
cả thì con trỏ đó là con trỏ NULL
#include <iostream.h>
void main()
{
int *p;
// p la con trỏ NULL
cout <<“Gia tri con tro p tro den la: “<< *p;
}
NULL POINTER ASSIGNMENT
24
8. Con trỏ và mảng
Giữa mảng và con trỏ có một sự liên hệ rất chặt chẽ:
– Những phần tử của mảng được xác định bằng chỉ số trong
mảng và cũng có thể được xác định qua biến con trỏ.
– Tên của một mảng tương đương với địa chỉ phần tử đầu
tiên của nó, tương tự một con trỏ tương đương với địa chỉ
của phần tử đầu tiên mà nó trỏ tới.
Kiểu mảng
Ví dụ
Kiểu Con trỏ
&<Tên mảng>[0]
&A[0] hoặc <Tên con trỏ >
A
&<Tên mảng> [<Vị trí>]
&A[i]
<Tên con trỏ> + <Vị trí>
<Tên mảng>[<Vị trí>]
A[i]
*(< Tên con trỏ > + <Vị trí>)
25
Ví dụ
p
P+i
*(p+i)
Ví dụ 1:
char ch[10], *p;
p = ch;
 p được gán địa chỉ của phần tử đầu tiên của mảng ch.
p = ch;
 Để tham chiếu phần tử thứ 3 trong mảng ch, ta dùng một
trong 2 cách sau:
ch[2] hoặc *(p+2).
26
Ví dụ:
int Numbers[5];
int *p;
Number
s
p
10
p
p= Numbers;
*p = 10;
p++; *p = 20;
p = &numbers[2];
*p = 30;
p = numbers + 3;
*p = 40;
p = numbers;
*(p+4) = 50;
20
p
20
27
30
p
40
p
50
9. Mảng con trỏ
 Mỗi biến con trỏ là một biến đơn. Ta có thể tạo
mảng của các con trỏ với mỗi phần tử của mảng là
một con trỏ.
 Cú pháp:
<type> *pointerArray[elements];
o type: kiểu dữ liệu mà các con trỏ phần tử trỏ đến.
o pointerArray: tên mảng con trỏ.
o elements: số phần tử của mảng con trỏ.
28
Ví dụ:
int *p[5];
int a=6;
p[0] = &a;
p[2] = p[0];
int b;
b = *p[0];
P[0]
P[1]
p 100
a
6
100
b
29
P[2]
P[3]
P[4]
Tìm số lớn nhất của một dãy số nguyên n phần tử nhập từ bàn phím?
int main()
{
int *px;
int n;
cin>>n;
px=new int[n];
for (int i=0; i<n; i++)
cin>>*(px+i);
int max=*px;
for (int i=1;i<n;i++)
if (*(px+i)>max)
max=*(px+i);
cout<<max;
return 0;
}
30
Thực hiện
bài tập chương 5
31