Tác giả phạm hồng thái bài giảng ngôn ngữ LẬp trình c/C++



tải về 1.98 Mb.
trang21/55
Chuyển đổi dữ liệu07.07.2016
Kích1.98 Mb.
1   ...   17   18   19   20   21   22   23   24   ...   55

Con trỏ

Ý nghĩa


Con trỏ là một biến chứa địa chỉ của biến khác. Nếu p là con trỏ chứa địa chỉ của biến x ta gọi p trỏ tới x và x được trỏ bởi p. Thông qua con trỏ ta có thể làm việc được với nội dung của những ô nhớ mà p trỏ đến.

Để con trỏ p trỏ tới x ta phải gán địa chỉ của x cho p.

Để làm việc với địa chỉ của các biến cần phải thông qua các biến con trỏ trỏ đến biến đó.

Khai báo biến con trỏ


<*tên biến> ;

Địa chỉ của một biến là địa chỉ byte nhớ đầu tiên của biến đó. Vì vậy để lấy được nội dung của biến, con trỏ phải biết được số byte của biến, tức kiểu của biến mà con trỏ sẽ trỏ tới. Kiểu này cũng được gọi là kiểu của con trỏ. Như vậy khai báo biến con trỏ cũng giống như khai báo một biến thường ngoại trừ cần thêm dấu * trước tên biến (hoặc sau tên kiểu). Ví dụ:

int *p ; // khai báo biến p là biến con trỏ trỏ đến kiểu dữ liệu nguyên.

float *q, *r ; // hai con trỏ thực q và r.


Sử dụng con trỏ, phép toán *


Để con trỏ p trỏ đến biến x ta phải dùng phép gán p = địa chỉ của x.

Nếu x không phải là mảng ta viết: p = &x.

Nếu x là mảng ta viết: p = x hoặc p = &x[0].

Không gán p cho một hằng địa chỉ cụ thể. Ví dụ viết p = 200 là sai.

Phép toán * cho phép lấy nội dung nơi p trỏ đến, ví dụ để gán nội dung nơi p trỏ đến cho biến f ta viết f = *p.

& và * là 2 phép toán ngược nhau. Cụ thể nếu p = &x thì x = *p. Từ đó nếu p trỏ đến x thì bất kỳ nơi nào xuất hiện x đều có thể thay được bởi *p và ngược lại.

:

int i, j ; // khai báo 2 biến nguyên i, j



int *p, *q ; // khai báo 2 con trỏ nguyên p, q

p = &i; // cho p trỏ tới i

q = &j; // cho q trỏ tới j

cout << &i ; // hỏi địa chỉ biến i

cout << q ; // hỏi địa chỉ biến j (thông qua q)

i = 2; // gán i bằng 2

*q = 5; // gán j bằng 5 (thông qua q)

i++ ; cout << i ; // tăng i và hỏi i, i = 3

(*q)++ ; cout << j ; // tăng j (thông qua q) và hỏi j, j = 6

(*p) = (*q) * 2 + 1; // gán lại i (thông qua p)

cout << i ; // 13

Qua ví dụ trên ta thấy mọi thao tác với i là tương đương với *p, với j là tương đương với *q và ngược lại.


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


Trên đây ta đã trình bày về 2 phép toán một ngôi liên quan đến địa chỉ và con trỏ là & và *. Phần này chúng ta tiếp tục xét với các phép toán khác làm việc với con trỏ.

Phép toán gán


Gán con trỏ với địa chỉ một biến: p = &x ;

Gán con trỏ với con trỏ khác: p = q ; (sau phép toán gán này p, q chứa cùng một địa chỉ, cùng trỏ đến một nơi).

:

int i = 10 ; // khai báo và khởi tạo biến i = 10



int *p, *q, *r ; // khai báo 3 con trỏ nguyên p, q, r

p = q = r = &i ; // cùng trỏ tới i

*p = q**q + 2**r + 1 ; // i = 10*10 + 2*10 + 1

cout << i ; // 121


Phép toán tăng giảm địa chỉ


p ± n: con trỏ trỏ đến thành phần thứ n sau (trước) p.

Một đơn vị tăng giảm của con trỏ bằng kích thước của biến được trỏ. Ví dụ giả sử p là con trỏ nguyên (2 byte) đang trỏ đến địa chỉ 200 thì p+1 là con trỏ trỏ đến địa chỉ 202. Tương tự, p + 5 là con trỏ trỏ đến địa chỉ 210. p - 3 chứa địa chỉ 194.



194

195

196

197

198

199

200

201

202203204205206207208209210211_p-3p-2p-1pp+1p+2p+4p+4p+5


p - 3








































p




p + 1



Như vậy, phép toán tăng, giảm con trỏ cho phép làm việc thuận lợi trên mảng. Nếu con trỏ đang trỏ đến mảng (tức đang chứa địa chỉ đầu tiên của mảng), việc tăng con trỏ lên 1 đơn vị sẽ dịch chuyển con trỏ trỏ đến phần tử thứ hai, … Từ đó ta có thể cho con trỏ chạy từ đầu đến cuối mảng bằng cách tăng con trỏ lên từng đơn vị như trong câu lệnh for dưới đây.

:

int a[100] = { 1, 2, 3, 4, 5, 6, 7 }, *p, *q;



p = a; cout << *p ; // cho p trỏ đến mảng a, *p = a[0] = 1

p += 5; cout << *p ; // *p = a[5] = 6 ;

q = p - 4 ; cout << *q ; // q = a[1] = 2 ;

for (int i=0; i<100; i++) cout << *(p+i) ; // in toàn bộ mảng a


Phép toán tự tăng giảm


p++, p--, ++p, --p: tương tự p+1 và p-1, có chú ý đến tăng (giảm) trước, sau.

: Ví dụ sau minh hoạ kết quả kết hợp phép tự tăng giảm với lấy giá trị nơi con trỏ trỏ đến. a là một mảng gồm 2 số, p là con trỏ trỏ đến mảng a. Các lệnh dưới đây được qui ước là độc lập với nhau (tức lệnh sau không bị ảnh hưởng bởi lệnh trước, đối với mỗi lệnh p luôn luôn trỏ đến phần tử đầu (a[0]) của a.

int a[2] = {3, 7}, *p = a;

(*p)++ ; // tăng (sau) giá trị nơi p trỏ º tăng a[0] thành 4

++(*p) ; // tăng (trước) giá trị nơi p trỏ º tăng a[0] thành 4

*(p++) ; // lấy giá trị nơi p trỏ (3) và tăng trỏ p (tăng sau), p ® a[1]

*(++p) ; // tăng trỏ p (tăng trước), p ® a[1] và lấy giá trị nơi p trỏ (7)

Chú ý:

Phân biệt p+1 và p++ (hoặc ++p):

p+1 được xem như một con trỏ khác với p. p+1 trỏ đến phần tử sau p.

p++ là con trỏ p nhưng trỏ đến phần tử khác. p++ trỏ đến phần tử đứng sau phần tử p trỏ đến ban đầu.

Phân biệt *(p++) và *(++p):

Các phép toán tự tăng giảm cũng là một ngôi, mức ưu tiên của chúng là cao hơn các phép toán hai ngôi khác và cao hơn phép lấy giá trị (*). Cụ thể:

*p++ º *(p++)

*++p º *(++p)

++*p º ++(*p)

Cũng giống các biến nguyên việc kết hợp các phép toán này với nhau rất dễ gây nhầm lẫn, do vậy cần sử dụng cặp dấu ngoặc để qui định trình tự tính toán.


Hiệu của 2 con trỏ


Phép toán này chỉ thực hiện được khi p và q là 2 con trỏ cùng trỏ đến các phần tử của một dãy dữ liệu nào đó trong bộ nhớ (ví dụ cùng trỏ đến 1 mảng dữ liệu). Khi đó hiệu p - q là số thành phần giữa p và q (chú ý p - q không phải là hiệu của 2 địa chỉ mà là số thành phần giữa p và q).

Ví dụ: giả sử p và q là 2 con trỏ nguyên, p có địa chỉ 200 và q có địa chỉ 208. Khi đó p - q = -4 và q - p = 4 (4 là số thành phần nguyên từ địa chỉ 200 đến 208).


Phép toán so sánh


Các phép toán so sánh cũng được áp dụng đối với con trỏ, thực chất là so sánh giữa địa chỉ của hai nơi được trỏ bởi các con trỏ này. Thông thường các phép so sánh <, <=, >, >= chỉ áp dụng cho hai con trỏ trỏ đến phần tử của cùng một mảng dữ liệu nào đó. Thực chất của phép so sánh này chính là so sánh chỉ số của 2 phần tử được trỏ bởi 2 con trỏ đó.

:

float a[100], *p, *q ;



p = a ; // p trỏ đến mảng (tức p trỏ đến a[0])

q = &a[3] ; // q trỏ đến phần tử thứ 3 (a[3]) của mảng

cout << (p < q) ; // 1

cout << (p + 3 == q) ; // 1

cout << (p > q - 1) ; // 0

cout << (p >= q - 2) ; // 0

for (p=a ; p < a+100; p++) cout << *p ; // in toàn bộ mảng a

1   ...   17   18   19   20   21   22   23   24   ...   55


Cơ sở dữ liệu được bảo vệ bởi bản quyền ©hocday.com 2016
được sử dụng cho việc quản lý

    Quê hương