Nói về con trỏ [P. 3]

Các bài viết trong loạt bài “Nói về con trỏ”

Mảng 2 chiều

Trước khi bắt đầu mới khai báo mảng 2 chiều (hay còn gọi là ma trận) bằng con trỏ, cũng nên nhắc lại một chút về cách khai báo truyền thống chứ nhỉ

int a[10][10];

Ngắn gọn và dễ hiểu phải không nào?

Thế còn khai báo bằng con trỏ thì thế nào nhỉ? Thế này đây:

int **a;

Nhìn qua thì có vẻ ngắn gọn nhưng có gọn thật không thì hãy chờ đã.

Khoan giải thích ý nghĩa của câu khai báo int **a, hãy xem qua đoạn code sau:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int b[10][10];
    int **a;

    printf("%p\n", &b);
    printf("%p\n", a);

    return 0;
}

Output:

0x7fff5fbff800
0x0

Như đã đề cập ở phần trước đó printf(“%p\n”, a); sẽ xuất ra địa chỉ của vùng nhớ lưu giá trị thực sự của con trỏ a. Tuy nhiên, trong trường hợp này, kết quả là 0x0 có nghĩa là a chưa trỏ đến đâu cả và chưa có một vùng nhớ lưu giá trị thực sự của a. Điều này đồng nghĩa với việc khai báo mảng 2 chiều chưa hoàn chỉnh và chưa thể sử dụng được nó.

Vậy int **a; có ý nghĩa thế nào?

Quay trở lại phần trước một chút. Bạn còn nhớ câu này chứ “(chú ý chỗ này nhé, chút nữa chúng ta sẽ cần đến nó đấy)“? Hãy tìm và đọc lại chỗ đó rồi chúng ta sẽ đi tiếp.

Bạn đã nhớ lại chưa nào? Mình đã nói rằng int *a có nghĩa là con trỏ a trỏ đến một vùng nhớ có kiểu là int phải không nào?

Vậy trong trường hợp này int **a, phải dịch câu lệnh đó và hiểu thế nào? Cũng không khó đâu bạn ạ.

Nó có nghĩa thế này, cũng là con trỏ a trỏ đến một vùng nhớ, nhưng kiểu dữ liệu của vùng nhớ đó không còn là int nữa mà lại là một con trỏ khác và con trỏ này mới thực sự trỏ đến một vùng nhớ có kiểu dữ liệu là int.

Có thể hơi khó hiểu một chút đấy, để mình giải thích thêm nhé:

int cols = 2; //Số cột của ma trận
a = (int**)malloc(cols * sizeof(int*));

Sau int **a, a chưa trỏ đến một vùng nhớ nào, nên câu lệnh trên sẽ cấp phát một vùng mới và trỏ a đến đó. sizeof(int*) sẽ trả về kích thước của kiểu dữ liệu int* (con trỏ đến vùng nhớ có kiểu dữ liệu int). Lấy kết quả đó nhân với cols (số cột của ma trận), ta sẽ được kích thước vùng nhớ cần thiết cho số cột của ma trận. Còn int**, để không làm bạn khó hiểu hơn, bạn chỉ cần nhớ, do a có kiểu dữ liệu là int** nên ép về kiểu int** cho hợp lệ thôi.

Đến đây, bạn đã có được các cột của ma trận. Tuy nhiên, vẫn chưa có các dòng và vẫn chưa thể lưu dữ liệu được. Việc tiếp theo cần làm là cấp phát vùng nhớ cho mỗi cột và những vùng nhớ này mới thực sự là nơi lưu giá trị. Để thực hiện việc đó, ta làm như sau:

int rows = 2; //Số cột
for (int i = 0; i < rows; i++)
        //a[i] = (int*)malloc(rows * sizeof(int));
        *(a + i) = (int*)malloc(rows * sizeof(int));

Việc cấp phát vùng nhớ lúc này, giống như mảng 1 chiều. Chỉ khác một chút là, ở mảng 1 chiều, mình viết là a = (int*)malloc(sizeof(int[10]));.

Khác biệt thứ nhất nằm ở chỗ sizeof(int[10])rows * sizeof(int). Nói là khác biệt chứ 2 lệnh đó là như nhau, bạn có thể dùng cách nào tuỳ thích.

Khác biệt thứ hai là a*(a + i), đây mới là khác biệt cần lưu ý. Nếu là (a + i) thì có đúng không? Câu trả lời này, xin nhường lại cho bạn vì mình tin rằng nếu bạn theo dõi từ bài đầu tiên đến giờ, bạn có thể dễ dàng trả lời được câu hỏi này.

Và đây là một đoạn hoàn chỉnh cho việc khai báo và khởi tạo mảng 2 chiều dùng con trỏ:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int cols, rows;
    cols = rows = 2;

    //Khai báo
    int ** a = NULL;
    a = (int**)malloc(cols * sizeof(int*));
    for (int i = 0; i < rows; i++)
        *(a + i) = (int*)malloc(rows * sizeof(int));

    //Khởi tạo
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < cols; j++)
            a[i][j] = 0;
    ...    
    return 0;
}

Stop

Bạn đã biết mảng động là gì, khai báo, khởi tạo ra sao. Tuy nhiên, vẫn còn một câu hỏi quan trọng. Đó là sử dụng chúng ra sao, khác gì so với mảng bình thường và hơn hết là tại sao lại sử dụng chúng thay vì mảng bình thường.

Bạn sẽ tìm thấy câu trả lời trong bài viết kế tiếp.

(còn tiếp…)

Tags: , , , ,

About ninjapro

It is better to feel by yourself about me

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: