Lập Trình C++ B5: Con Trỏ, Cấp phát vùng nhớ động
Chào mừng các bạn đến với chủ đề lập trình c++ cơ bản.
Hôm nay chúng ta sẽ tìm hiểu về con trỏ trong c++.
Con trỏ trong c++ là một kỹ thuật cấp phát vùng nhớ động.
Nó giúp bạn tối ưu và linh hoạt trong việc sử dụng vùng nhớ.
Bạn hãy tưởng tượng, bạn khai báo một mảng dữ liệu có 10 phần tử. Nhưng khi sử dụng bạn chỉ dùng có 4 phần tử.
Như vậy 6 phần tử còn lại được ram cấp cho vùng nhớ là dư thừa, không sử dụng đến.
Nhưng nó vẫn bị chiếm dụng trong suốt thời gian thực hiện.
Và điều đó gây lên hiện tượng: một chương trình nhỏ mà ngốn rất nhiều phần ram sử dụng trên máy tính.
Do đó để cải thiện cho những hạn chế của việc cấp phát vùng nhớ tĩnh như mảng.
=> Con trỏ được sử dụng, nó giúp cho việc cấp phát vùng nhớ động được tốt hơn.
I. Lý Thuyết Cơ Bản Về Con trỏ.
1. Khai báo và sử dụng con trỏ trong c++
– Con trỏ là một biến bình thường như các biến khác.
– Con trỏ không lưu giá trị mà nó lưu địa chỉ của một biến khác.
– Cú pháp khai báo : Kiểu dữ liệu * tên_con trỏ.
Hãy xem hình ví dụ dưới đây.
#include<iostream.h> void main() { int* pInt; // Con trỏ dành cho các biến kiểu int float* pFloat; // Con trỏ dành cho các biến kiểu float std::string* pStr; // Con trỏ dành cho các biến chuỗi ký tự }
– Để sử dụng được con trỏ, ta phải dùng phép gán để gán địa chỉ của biến cho con trỏ.
ví dụ: int* p;
int a = 5;
p = &a; // gán địa chỉ của biến cho con trỏ. con trỏ p sẽ trỏ vào vùng nhớ có địa chỉ của biến a.
– Để truy xuất được giá trị mà con trỏ trỏ đến, sử dụng phép *.
Ví dụ cout << *p // chương trình sẽ in giá giá trị là 5.
– Không gán con trỏ p = một hằng địa chỉ cụ thể. Ví dụ chúng ta viết int *p; p = 0x3242 // Sai.
– Tương tác giữa mảng và con trỏ.
Giả sử tôi có mảng int a [5];
một con trỏ : int * p;
C++ cho phép gán : p= a; // Điều này hợp lệ.
Lúc này con trỏ p sẽ được trỏ đến vùng nhớ của mảng a.
Và địa chỉ nó lưu chính là địa chỉ biến đầu tiên trong mảng a[0], và ta cũng có thể viết p = &a[0];
Lúc đó để truy xuất sang các phần tử tiếp theo của mảng, chúng ta sẽ phải dùng đến các phép toán cho con trỏ.
2. Các phép toán cho con trỏ
– Phép gán : & là phép gán con trỏ tới một biến.
– Phép toán * là ngược lại với & cho phép lấy giá trị (nội dung) của biến mà con trỏ trỏ đến.
– Phép toán tăng giảm: p + 1, p +2….
Phép toán này cho phép con trỏ nhảy một bước mà khoảng cách của bước = với kích thước của kiểu dữ liệu nó trỏ đến.
Và điều này được áp dụng cho việc truy xuất các phần tử trong mảng.
– Phép toán tự tăng giảm: ++ và –. Cũng được áp dụng cho biến con trỏ.
Và nó cũng tăng giảm địa chỉ con trỏ theo bước bằng với kích thước của dữ liệu nó trỏ đến.
3. Vấn đề quản lý cấp phát vùng nhớ với con trỏ.
Trong các lý thuyết ở trên, chúng ta mới chỉ nói đến việc khai báo một con trỏ,
và gán con trỏ vào một vùng nhớ đã xác định trước.
Vậy khi khai báo một con trỏ và cấp phát mới cho nó thì sao.
– Toán tử new được sử dụng để cấp phát vùng nhớ, toán tử delete được sử dụng để xóa vùng nhớ cấp phát.
– Con trỏ khác với các biến khác ở chỗ, khi nó được cấp phát, thì nó phải được delete thì vùng nhớ mới được giải phóng.
Còn với các biến thông thưởng, và mảng.
Khi hàm kết thúc, thì các vùng nhớ tự động được giải phóng.
– Điều này cho thấy sự linh động với con trỏ.
Đó là khi cần thì bạn khái báo cấp phát, dùng xong, bạn có thể xóa đi để giải phóng ngay vùng nhớ,
mà không phải đợi đến khi kết thúc hàm.
Tuy nhiên nếu bạn quên delete, thì điều này lại trở nên tệ hại.
Xem ví dụ như hình sau đây.
4. Những lưu ý khi sử dụng con trỏ:
– p2 = p+1; // p2 là một trỏ mới khác p và nó trỏ sang vùng nhớ kế tiếp bên cạnh p
– p++; // là chính con trỏ p, nhưng nó đã không trỏ ở vị trí cũ mà trỏ sang vị trí bên cạnh.
– Phép toán ++ và — có độ ưu tiên cao nhất so với các phép toán 2 ngôi và phép *
– Đã có new là phải nhớ đến việc delete con trỏ, có thể delete ko nằm cùng khu vực với new,
nhưng phải luôn nhớ là phải có chỗ để delete vùng nhớ đã cấp phát ra.
– Con trỏ là một biến như bình thường, nên suy ra sẽ có những kỹ thuật đặc biệt như:
Mảng con trỏ, con trỏ hàm, con trỏ xâu ký tự, con trỏ hằng, hằng con trỏ.
Và đó là những vấn đề nâng cao, sẽ được nói riêng trong một bài.
II. Thực hành con trỏ trong c++
Viết một chương trình thực hiện các yêu cầu sau.
1. Nhập vào số lượng cho phép của một danh sách dữ liệu
2. Nhập vào một danh sách các số thực với số lượng được nhập ở trên.
3. Danh sách được quản lý bởi một con trỏ p với kỹ thuật cấp phát vùng nhớ.
4. Tìm ra phần tử có giá trị lớn nhất, nhỏ nhất.
Code sample
// Phattrienphanmem123az.com #include <iostream> #include <conio.h> int fMin(int *p, int num) { int min = *p; for (int i = 0; i < num; i++) { int val = *(p + i); if (min > val) { min = val; } } return min; } int fMax(int *p, int num) { int max = *p; for (int i = 0; i < num; i++) { int val = *(p + i); if (max < val) { max = val; } } return max; } int main() { int number = 0; std::cout << "Input number = "; std::cin >> number; std::cout << "Input value" << std::endl; int *p = new int[number]; for (int i = 0; i < number; i++) { std::cout << "a[" << i << "]= "; std::cin >> *(p + i); } int vMin = fMin(p, number); int vMax = fMax(p, number); std::cout << "max = " << vMax << std::endl; std::cout << "min = " << vMin << std::endl; delete[] p; _getch(); return 0; }
Đó là một ví dụ cơ bản về cách sử dụng con trỏ trong c++
Ok.