Khám Phá Con Trỏ và Cấp Phát Động Trong C++

Chuyên khảo phân tích Bai12 contro trong c p2, đánh giá các khía cạnh quan trọng, đề xuất hướng nghiên cứu tiếp theo., phục vụ nghiên cứu và ứng dụng thực tiễn

Trường đại học

Trường Đại Học

Chuyên ngành

Công Nghệ Thông Tin

Người đăng

Ẩn danh

Thể loại

bài giảng

2023

63
3
0

Phí lưu trữ

30 Point

Mục lục chi tiết

1. Cấp phát động

1.1. Cấp phát bộ nhớ tĩnh (static memory allocation)

1.2. Cấp phát động (dynamic memory allocation)

2. Cấu trúc một CT C++ trong bộ nhớ

3. Cấp phát bộ nhớ

3.1. Hàm malloc, calloc, realloc trong C

3.2. Toán tử new trong C++

3.3. Giải phóng bộ nhớ: free và delete

4. Biến cấp phát động và Biến tự động

5. Toán tử new

6. Kiểm tra việc cấp phát có thành công không

7. Khởi tạo giá trị trong cấp phát động

8. Ví dụ 1

9. Toán tử delete

10. Từ khóa typedef

11. Định nghĩa kiểu dữ liệu con trỏ

12. Con trỏ và hàm

12.1. Con trỏ là kiểu dữ liệu hoàn chỉnh

12.2. Ví dụ typedef int *IntPointer

13. Bài tập

14. Bài giải

15. Cấp phát động và mảng 1 chiều

16. Nhắc lại về mảng và con trỏ

17. Hạn chế của mảng chuẩn

18. Tạo mảng động bằng toán tử new

19. Xóa mảng động

20. Bài tập

21. Lời giải

22. Hàm trả về kiểu mảng

23. Bài tập

24. Lời giải

25. Mảng động 2 chiều

26. Bài tập

27. Lời giải

28. Con trỏ và hàm số

28.1. Tham số của hàm là 1 biến con trỏ - Trường hợp thay đổi giá trị đối số

28.2. Tham số của hàm là 1 biến con trỏ - Trường hợp không thay đổi giá trị đối số

28.3. Kiểu trả về của hàm là 1 con trỏ

29. Con trỏ và cấu trúc

29.1. Truy xuất các thuộc tính dùng con trỏ

29.2. Cấu trúc đệ quy (tự trỏ)

30. Bài tập

30.1. Bài 1: Tại sao cần phải giải phóng khối nhớ được cấp phát động?

30.2. Bài 2: Điều gì xảy ra nếu nối thêm ký tự vào chuỗi cấp phát động mà không cấp phát lại bộ nhớ?

30.3. Bài 3: Ta thường dùng phép ép kiểu trong những trường hợp nào?

30.4. Bài 4: Xác định kiểu của các biểu thức

30.5. Bài 5: Việc cấp phát động nghĩa là gì?

30.6. Bài 6: Sự khác nhau giữa malloc và calloc

30.7. Bài 7: Câu lệnh sử dụng hàm malloc cấp phát 1000 số kiểu long

30.8. Bài 8: Dùng calloc cấp phát bộ nhớ

30.9. Bài 9: Kiểm tra kết quả hàm func

30.10. Bài 10: Kiểm tra lỗi trong hàm main

30.11. Bài 11: Kết quả chương trình và giải thích

30.12. Bài 12: Kết quả chương trình, ý nghĩa khai báo, tìm lỗi sai

31. Viết chương trình nhập dãy số hữu tỉ, xuất số nhỏ hơn 1, tính tổng và tích

32. Viết chương trình khai báo mảng hai chiều 12x12 kiểu char, gán ký tự 'X', in lưới

33. Bài tập bắt buộc (2/2)

33.1. Viết chương trình khai báo mảng 10 con trỏ kiểu float, nhập, sắp xếp, in

33.2. Chương trình nhập dòng văn bản, sắp xếp alphabet, hiển thị

33.3. Làm lại các bài tập về ma trận dùng con trỏ

34. Vấn đề mở rộng

34.1. Các thao tác trên khối nhớ

34.2. Tham khảo cấp phát động bằng hàm malloc

Tóm tắt

I. Tổng Quan Về Con Trỏ và Cấp Phát Động Trong C

Con trỏ và cấp phát động là hai khái niệm quan trọng trong lập trình C++. Chúng cho phép quản lý bộ nhớ một cách linh hoạt và hiệu quả. Con trỏ giúp tham chiếu đến các vùng nhớ, trong khi cấp phát động cho phép cấp phát bộ nhớ khi chương trình đang chạy. Việc hiểu rõ về chúng là cần thiết để tối ưu hóa hiệu suất và tránh lỗi trong quá trình lập trình.

1.1. Khái Niệm Về Con Trỏ Trong C

Con trỏ trong C++ là một biến lưu trữ địa chỉ của một biến khác. Nó cho phép truy cập và thao tác trực tiếp với bộ nhớ, giúp tối ưu hóa hiệu suất chương trình.

1.2. Cấp Phát Động Là Gì

Cấp phát động là quá trình cấp phát bộ nhớ trong thời gian chạy của chương trình. Điều này cho phép lập trình viên chỉ định kích thước bộ nhớ cần thiết mà không cần biết trước.

II. Vấn Đề Thách Thức Khi Sử Dụng Con Trỏ và Cấp Phát Động

Mặc dù con trỏ và cấp phát động mang lại nhiều lợi ích, nhưng cũng tồn tại nhiều thách thức. Việc quản lý bộ nhớ không đúng cách có thể dẫn đến rò rỉ bộ nhớ hoặc lỗi truy cập bộ nhớ. Điều này đòi hỏi lập trình viên phải cẩn thận trong việc cấp phát và giải phóng bộ nhớ.

2.1. Rò Rỉ Bộ Nhớ Là Gì

Rò rỉ bộ nhớ xảy ra khi bộ nhớ được cấp phát nhưng không được giải phóng sau khi không còn sử dụng. Điều này có thể làm giảm hiệu suất của chương trình.

2.2. Lỗi Truy Cập Bộ Nhớ

Lỗi truy cập bộ nhớ xảy ra khi chương trình cố gắng truy cập vào vùng nhớ không hợp lệ. Điều này có thể dẫn đến sự cố hoặc hành vi không mong muốn.

III. Phương Pháp Cấp Phát Động Trong C

C++ cung cấp nhiều phương pháp để thực hiện cấp phát động, bao gồm toán tử newdelete. Việc sử dụng đúng các toán tử này là rất quan trọng để quản lý bộ nhớ hiệu quả.

3.1. Sử Dụng Toán Tử New

Toán tử new được sử dụng để cấp phát bộ nhớ cho một biến hoặc mảng. Cú pháp đơn giản giúp lập trình viên dễ dàng thực hiện cấp phát động.

3.2. Giải Phóng Bộ Nhớ Với Toán Tử Delete

Toán tử delete được sử dụng để giải phóng bộ nhớ đã được cấp phát bằng new. Việc này giúp tránh rò rỉ bộ nhớ và đảm bảo hiệu suất chương trình.

IV. Ứng Dụng Thực Tiễn Của Con Trỏ và Cấp Phát Động

Con trỏ và cấp phát động có nhiều ứng dụng trong lập trình thực tế, từ việc quản lý mảng động đến việc xây dựng các cấu trúc dữ liệu phức tạp như danh sách liên kết và cây.

4.1. Quản Lý Mảng Động

Mảng động cho phép lập trình viên tạo ra các mảng có kích thước thay đổi trong thời gian chạy, giúp tiết kiệm bộ nhớ và tối ưu hóa hiệu suất.

4.2. Xây Dựng Cấu Trúc Dữ Liệu

Con trỏ là công cụ quan trọng trong việc xây dựng các cấu trúc dữ liệu như danh sách liên kết, cây và đồ thị, cho phép thao tác linh hoạt với dữ liệu.

V. Kết Luận Về Con Trỏ và Cấp Phát Động Trong C

Hiểu biết về con trỏ và cấp phát động là rất quan trọng trong lập trình C++. Chúng không chỉ giúp tối ưu hóa hiệu suất mà còn giúp lập trình viên quản lý bộ nhớ một cách hiệu quả. Việc nắm vững các khái niệm này sẽ giúp cải thiện kỹ năng lập trình và giảm thiểu lỗi trong quá trình phát triển phần mềm.

5.1. Tương Lai Của Con Trỏ Trong Lập Trình

Với sự phát triển của các ngôn ngữ lập trình mới, con trỏ vẫn giữ vai trò quan trọng trong C++. Việc hiểu rõ về chúng sẽ giúp lập trình viên thích ứng với các công nghệ mới.

5.2. Lời Khuyên Khi Sử Dụng Con Trỏ

Lập trình viên nên cẩn thận trong việc cấp phát và giải phóng bộ nhớ. Sử dụng các công cụ kiểm tra bộ nhớ có thể giúp phát hiện và khắc phục các vấn đề liên quan đến bộ nhớ.

11/07/2025

Trích đoạn nội dung tài liệu

CON TRỎ VÀ CẤP PHÁT ĐỘNG CĐR buổi học • Sau khi học xong buổi học, sinh viên có khả năng: • Hiểu được về con trỏ và cấp phát động. • Áp dụng con trỏ trong cấp phát mảng. • Áp dụng con trỏ và tham số của hàm. • Áp dụng con trỏ và cấu trúc.

Cấp phát động 2. Cấp phát động mảng 1 chiều 3. Cấp phát động mảng 2 chiều 4. Con trỏ và hàm số 5.

Con trỏ và cấu trúc 6. Một số vấn đề mở rộng NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 3 1. Cấp phát động • Cấp phát bộ nhớ tĩnh (static memory allocation) • Khai báo biến, cấu trúc, mảng, … • Bắt buộc phải biết trước cần bao nhiều bộ nhớ lưu trữ è tốn bộ nhớ, không thay đổi được kích thước, … • Cấp phát động (dynamic memory allocation) • Cần bao nhiêu cấp phát bấy nhiêu. • Có thể giải phóng nếu không cần sử dụng.

• Sử dụng vùng nhớ ngoài chương trình (cả bộ nhớ ảo virtual memory). NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 4 Cấu trúc một CT C++ trong bộ nhớ • Toàn bộ tập tin chương trình sẽ được nạp vào bộ nhớ tại vùng nhớ còn trống, gồm 4 phần: STACK Lưu đối tượng cục bộ Last-In First-Out khi thực hiện hàm Vùng nhớ trống Vùng cấp phát động HEAP (RAM trống và bộ nhớ ảo) Đối tượng toàn cục Vùng cấp phát tĩnh & tĩnh (kích thước cố định) Gồm các lệnh và hằng Mã chương trình (kích thước cố định) NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 5 Cấp phát bộ nhớ • Cấp phát bộ nhớ • Trong C: Hàm malloc, calloc, realloc (<stdlib.h>) • Trong C++: Toán tử new • Giải phóng bộ nhớ • Trong C: Hàm free • Trong C++: Toán tử delete NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 6 Biến cấp phát động và Biến tự động • Biến cục bộ • Khai báo bên trong định nghĩa hàm • Sinh ra khi hàm được gọi • Hủy đi khi hàm kết thúc • Thường gọi là biến tự động nghĩa là được trình biên dịch quản lý một cách tự động • Biến cấp phát động • Sinh ra bởi cấp phát động • Sinh ra và hủy đi khi chương trình đang chạy • Biến cấp phát động hay Biến động là biến con trỏ trước khi sử dụng được cấp phát bộ nhớ. NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 7 Toán tử new • Vì con trỏ có thể tham chiếu tới biến nhưng không thực sự cần phải có định danh cho biến đó. • Có thể cấp phát động cho biến con trỏ bằng toán tử new.

Toán tử new sẽ tạo ra biến “không tên” cho con trỏ trỏ tới. • Cú pháp: <type> *<pointerName> = new <type> Ví dụ: int *ptr = new int; o Tạo ra một biến “không tên” và gán ptr trỏ tới nó o Có thể làm việc với biến “không tên” thông qua *ptr NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 8 Kiểm tra việc cấp phát có thành công không #include <iostream> using namespace std; 0x34 3199 int main() { int *p = new int; 0x90 if (p == NULL) { ptr 0x34 cout << "Error: Khong du bo nho.\n"; exit(1); } *p = 3199; } NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 9 Khởi tạo giá trị trong cấp phát động • Cú pháp: <type> pointer = new <type> (value) • Ví dụ: #include <iostream> using namespace std; int main() { int *p; p = new int(99); // initialize with 99 cout << *p; // displays 99 return 0; } NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 10 Ví dụ 1. int *p1 = new int; p1 6. p2 = p1; p2 40 p1 p2 30 NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 11 Toán tử delete • Toán tử delete dùng để giải phóng vùng nhớ trong HEAP do con trỏ trỏ tới (con trỏ được cấp pháp bằng toán tử new).

Cú pháp: delete <pointerName>; • Ghi chú: Sau khi gọi toán tử delete thì con trỏ vẫn trỏ tới vùng nhớ trước khi gọi hàm delete. Ta gọi là “con trỏ lạc”. Ta vẫn có thể gọi tham chiếu trên con trỏ, tuy nhiên: • Kết quả không lường trước được • Thường là nguy hiểm Þ Hãy tránh con trỏ lạc bằng cách gán con trỏ bằng NULL sau khi delete. • Ví dụ: delete pointer; pointer = NULL; NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 12 Từ khóa typedef • Từ khóa typedef dùng để định nghĩa 1 tên mới hay gọi là một biệt danh (alias) cho tên kiểu dữ liệu có sẵn.

Ví dụ: typedef int SONGUYEN; Các khai báo sau tương đương: int a; SONGUYEN a; NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 13 Định nghĩa kiểu dữ liệu con trỏ • Có thể đặt tên cho kiểu dữ liệu con trỏ • Để có thể khai báo biến con trỏ như các biến khác • Loại bỏ * trong khai báo con trỏ Ví dụ: typedef int* IntPtr; - Định nghĩa một tên khác cho kiểu dữ liệu con trỏ - Các khai báo sau tương đương: IntPtr p; int *p; NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 14 Con trỏ và hàm • Con trỏ là kiểu dữ liệu hoàn chỉnh có thể dùng nó như các kiểu khác • Con trỏ có thể là tham số của hàm • Có thể là kiểu trả về của hàm Ví dụ: int* findOtherPointer(int* p); Hàm này khai báo: - Có tham số kiểu con trỏ trỏ tới int - Trả về biến con trỏ trỏ tới int NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 15 Ví dụ typedef int *IntPointer; void Input(IntPointer temp) { *temp = 20; cout << "Trong ham goi *temp = " << *temp << endl; } Truoc khi goi ham, *p = 10 int main() { Trong ham goi *temp = 20 IntPointer p = new int; Sau khi ket thuc ham, *p = 20 *p = 10; cout << "Truoc khi goi ham, *p = " << *p << endl; Input(p); cout << "Sau khi ket thuc ham, *p = " << *p << endl; } NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 16 Ví dụ 1. Trước khi gọi hàm Input 3. Thay đổi giá trị *temp p 10 p 20 temp 2. Giá trị của p sẽ được truyền 4.

Sau khi kết thúc gọi hàm Input vào temp p 10 p 20 temp NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 17 Bài tập • Viết hàm cấp phát và nhập giá trị cho 1 con trỏ theo 2 cách. NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 18 Bài giải // Cách 1 // Cách 2 #include <iostream> #include <iostream> using namespace std; using namespace std; typedef int *IntPointer; typedef int *IntPointer; IntPointer Input() { IntPointer Input(IntPointer &temp) { IntPointer temp = new int; temp = new int; *temp = 20; *temp = 20; return temp; return temp; } } int main() { int main() { IntPointer p; IntPointer p; p = Input(); Input(p); cout << "Sau khi ket thuc cout << "Sau khi ket thuc ham, ham, *p = " << *p << endl; *p = " << *p << endl; } } NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 19 2. Cấp phát động và mảng 1 chiều NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 20 Nhắc lại • Mảng lưu trong các ô nhớ liên tiếp trong bộ nhớ máy tính • Biến mảng tham chiếu tới phần tử đầu tiên • Biến mảng là một biến hằng con trỏ NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 21 Nhắc lại • Ví dụ: int a[10]; typedef int* IntPtr; IntPtr p; Þ a và p là các biến con trỏ. • Phép gán hợp lệ p = a; • p bây giờ sẽ trỏ tới nơi a trỏ, tức là tới phần tử đầu tiên của mảng a • Phép gán không hợp lệ a = p; • Bởi con trỏ mảng là con trỏ hằng.

NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 22 Hạn chế của mảng chuẩn • Hạn chế của mảng chuẩn • Bắt buộc phải biết trước cần bao nhiều bộ nhớ lưu trữ => tốn bộ nhớ, không thay đổi được kích thước, … Þ Dùng Mảng động • Mảng động • Kích thước không xác định ở thời điểm lập trình • Mà xác định khi chạy chương trình NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 23 Tạo mảng động bằng toán tử new • Cấp phát động cho biến con trỏ • Sau đó dùng con trỏ như mảng chuẩn • Cú pháp: <type> <pointer> = new <type> [<number_of_elements>] Ví dụ: typedef double * doublePtr; doublePtr d; d = new double[10]; Þ Tạo biến mảng cấp phát động d có 10 phần tử, kiểu cơ sở là double. NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 24 Xóa mảng động • Dùng toán tử delete[] để xóa mảng động. Ví dụ: double *d = new double[10]; //. Processing delete[] d; Þ Giải phóng tất cả vùng nhớ của mảng động này Þ Cặp ngoặc vuông báo hiệu có mảng Þ Nhắc lại: d vẫn trỏ tới vùng nhớ đó.

Vì vậy sau khi delete, cần gán d = NULL; NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 25 Bài tập Hãy viết chương trình tạo mảng 1 chiều có n phần tử bằng 2 cách: • Cách 1: Bằng mảng chuẩn • Cách 2: Bằng cấp phát động NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 26 Lời giải #include <iostream> #include <iostream> using namespace std; using namespace std; int main() { int main() { int arr[10]; int *arr; return 0; arr = new int[10]; } return 0; } NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 27 Hàm trả về kiểu mảng • Ta không được phép trả về kiểu mảng trong hàm. Ví dụ: int[] someFunction(); // Không hợp lệ! • Có thể thay bằng trả về con trỏ tới mảng có cùng kiểu cơ sở: int* someFunction(); // Hợp lệ! NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 28 Bài tập • Hãy viết HÀM tạo mảng 1 chiều có n phần tử bằng cấp phát động. • Viết hàm xuất mảng 1 chiều đã tạo. • Viết hàm đếm số phần tử âm trong mảng 1 chiều.

NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 29 Lời giải #include <iostream> #include <iostream> using namespace std; using namespace std; int* Input(int n){ void Input(int *&p, int n){ int *p; p = new int[n]; p = new int[n]; for (int i = 0; i < n; i++){ for (int i = 0; i < n; i++){ cin >> p[i]; cin >> p[i]; } } } return p; } int main() { int *arr, n; int main() { cout << "Nhap n: "; int *arr, n; cin >> n; cout << "Nhap n: "; Input(arr, n); cin >> n; } arr = Input(n); } NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 30 Lời giải // Hàm xuất mảng void Output(int *p, int n) { cout << "\n Xuat mang 1 chieu: "; for (int i = 0; i < n; i++) { cout << p[i] << " " ; } } NMLT - CON TRỎ VÀ CẤP PHÁT ĐỘNG 31 3.

Nội dung được bảo vệ bản quyền — Tải xuống đầy đủ

Tài liệu "Hiểu Biết Về Con Trỏ và Cấp Phát Động Trong C++" cung cấp cái nhìn sâu sắc về hai khái niệm quan trọng trong lập trình C++. Nó giải thích cách thức hoạt động của con trỏ, cách cấp phát động bộ nhớ và tầm quan trọng của chúng trong việc tối ưu hóa hiệu suất chương trình. Độc giả sẽ hiểu rõ hơn về cách sử dụng con trỏ để quản lý bộ nhớ hiệu quả, từ đó nâng cao khả năng lập trình của mình.

Để mở rộng kiến thức, bạn có thể tham khảo tài liệu Tổng hợp các bài tập môn kĩ thuật lập trình, nơi cung cấp các bài tập thực hành giúp củng cố kiến thức về con trỏ. Ngoài ra, tài liệu Bai11 contro trong c p1 sẽ giúp bạn khám phá thêm về con trỏ cơ bản trong ngôn ngữ C. Những tài liệu này sẽ là cơ hội tuyệt vời để bạn đào sâu hơn vào các khía cạnh của lập trình C++.