+1

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 image.png

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. image.png 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

image.png

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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí