Double Pointer
Bài viết mình sẽ giới thiệu về Double Pointer
, đại loại là sử dụng con trỏ kép **
.
Bài đọc tham khảo: Double Pointer
Tổng quan
Cú pháp
type_data **name_variable;
Ví dụ
#include <stdio.h>
#include <stdlib.h>
int main()
{
int var = 2;
int *p;
int **double_p;
// gán địa chỉ
p = &var;
double_p = &p;
printf("Địa chỉ của var: %p\n", &var);
printf("Địa chỉ mà con trỏ p đang ở tới: %p\n", p);
printf("Địa chỉ mà con trỏ double_p đang trỏ: %p\n", double_p);
printf("Dịa chỉ của con trỏ double_p: %p\n", &double_p);
}
Ouput
Địa chỉ của var: 0x7ffc7b94f114
Địa chỉ mà con trỏ p đang ở tới: 0x7ffc7b94f114
Địa chỉ mà con trỏ double_p đang trỏ: 0x7ffc7b94f118
Dịa chỉ của con trỏ double_p: 0x7ffc7b94f120
Có thể hình dung dễ dàng hơn qua hình ảnh sau
Thông qua hình ảnh có thể thấy được double_p
nắm giữ địa chỉ của p
, và p
nắm giữ địa chỉ của var
.
Câu hỏi đặt ra: "Việc sử dụng double pointer
nhằm mục đích gì ?"
- Hãy xem mỗi mũi tên là đường dẫn đến giá trị mà mk cần tìm.
- Lúc này nếu ta muốn
double_p
trỏ tới một giá trị khác thì chỉ việc là thay đổi địa chỉ là dược.Giúp quản lí linh hoạt các biến và con trỏ.
Ứng dụng
1. Tạo mảng 2 chiều
#include <stdio.h>
#include <stdlib.h>
int main() {
int rows = 3; // Số hàng
int cols = 4; // Số cột
// Khai báo một con trỏ kép
int **arr;
// Cấp phát bộ nhớ cho mảng chính (chứa các con trỏ hàng)
// arr bây giờ là một mảng có 3 phần tử, mỗi phần tử là một con trỏ kiểu int*
arr = (int **)malloc(rows * sizeof(int *));
// Kiểm tra xem việc cấp phát có thành công không
if (arr == NULL) {
printf("Loi: Khong the cap phat bo nho.");
return 1;
}
// Cấp phát bộ nhớ cho từng hàng
// Mỗi phần tử của mảng arr (tức là mỗi con trỏ hàng) sẽ trỏ tới một mảng số nguyên
for (int i = 0; i < rows; i++) {
arr[i] = (int *)malloc(cols * sizeof(int));
if (arr[i] == NULL) {
printf("Loi: Khong the cap phat bo nho.");
// Giải phóng bộ nhớ đã cấp phát trước đó để tránh rò rỉ
for (int j = 0; j < i; j++) {
free(arr[j]);
}
free(arr);
return 1;
}
}
// Gán giá trị vào mảng
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
arr[i][j] = i * cols + j + 1;
}
}
// In mảng ra màn hình
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
// Giải phóng bộ nhớ đã cấp phát
// Phải giải phóng từng hàng trước
for (int i = 0; i < rows; i++) {
free(arr[i]);
}
// Sau đó giải phóng mảng chính
free(arr);
return 0;
}
Output
1 2 3 4
5 6 7 8
9 10 11 12
2. Quản lí đa dạng các kiểu dữ liệu
Tạo 1 mảng là các con trỏ. Và sử dụng các con trỏ này có thể giúp ta quản lí linh hoạt các cấu trúc dữ liệu và biến trong chương trình.
a. Mảng động các con trỏ
Sử dụng malloc
để cấp phát vùng để lưu trữ các con trỏ.
#include <stdio.h>
#include <stdlib.h>
typedef struct Student {
int age;
int mssv;
} student_t;
int main()
{
student_t **double_p;
double_p = (student_t **)malloc(sizeof(student_t*) * 2);
student_t A = {
.age = 20,
.mssv = 1
};
student_t B = {
.age = 21,
.mssv = 2
};
double_p[0] = &A;
double_p[1] = &B;
// In ra thông tin
for (int i = 0; i < 2; i++) {
printf("Student %d: age = %d, mssv = %d\n",
i,
double_p[i]->age,
double_p[i]->mssv);
}
free(double_p); // giải phóng bộ nhớ động cho mảng con trỏ
return 0;
}
Output
Student 0: age = 20, mssv = 1
Student 1: age = 21, mssv = 2
b. Mảng tĩnh các con trỏ
Nếu xác định được số lương con trỏ ngày từ đầu thì ta có thể cấp phát trực tiếp vùng nhớ để lưu trữ các con trỏ
Cú pháp:
<kiểu dữ liệu con trỏ> *name[SỐ_PHẦN_TỬ];
Ở đây tôi sẽ lấy một ví dụ: Trong ví dụ này, tôi sẽ tạo con trỏ để quản lí 2 chuỗi kí tự và truyền nó vào trong hàm print
.
#include <stdio.h>
void print(char **src)
{
printf("Chuỗi được lưu trữ: %s %s\n", src[0], src[1]);
}
int main()
{
char *list_str[2];
list_str[0] = "hello";
list_str[1] = "world";
print(list_str);
return 0;
}
Output
Chuỗi được lưu trữ: hello world
Nếu không làm như tôi, bạn sử dụng phương pháp gì để truyền 2 chuỗi vào hàm mà tối ưu hiệu suất, hãy bình luận bên dưới bài viết.
All rights reserved