1.Nhắc lại kiến thức mảng một chiều

Mảng một chiều là một lưu trữ chứa những giá trị có cùng kiểu với nhau. Các phần tử của mảng được lưu trữ trong các vị trí liền kề nhau.

Giả sử tôi có một mảng a gồm 5 phần tử sẽ là:

#include <stdio.h>
int main()
{
    int a[5] = {0,1,2,3,4};
}

Ta tưởng tượng các phần tử được lưu trữ trong mảng như sau:

Dòng trên cùng chính là phần truy cập vào vị trí trong mảng, dòng ở giữa chính là giá trị tại các vị trí mà dòng trên đang truy cập, dòng cuối cùng chính là địa chỉ của từng vị trí.

Nhận xét: Địa chỉ của các phần tử trong mảng là liền kề nhau. Ban đầu là địa chỉ 1000 khi truy cập vào phần tử sau đó (phần tử thứ 2) là 1004, truy cập vào phần tử thứ 3 là 1008 …. Truy cập vào phần tử cuối là 1016.

Vì kiểu int có kích thước là 4 byte nên mỗi lần truy cập vào địa chỉ phần tử liền kề trong mảng thì vị trí tăng lên 4 đơn vị (4 byte).

Như vậy ta có thể kết luận rằng các phần tử của một mảng được lưu trữ trong các vị trí bộ nhớ liền nhau.

2.Sử dụng con trỏ để thao tác với các phần tử trong mảng một chiều

2.1 Địa chỉ ô nhớ các phần tử mảng một chiều

Mảng một chiều, thực chất cũng tồn tại là một con trỏ. Trong đó, tên mảng chứa chính địa chỉ của mảng đó. Con trỏ đến phần tử đầu tiên của mảng cũng mang lại chính địa chỉ của mảng đó.

Để làm rõ hơn về điều này tôi có một ví dụ dưới đây, tôi khai báo một mảng a gồm 5 phần tử, tôi sẽ kiểm tra địa chỉ của mảng tại tên mảng và địa chỉ của con trỏ phần tử đầu tiên của mảng như sau:

#include <stdio.h>
int main()
{
    //khai bao mang a gom 5 phan tu
    int a[5] = {0,1,2,3,4};
    //khai bao con tro p va gan p bang dia chi cua phan tu dau tien trong mang
    int *p;
    p = &a[0];
    //hien thi ket qua
    printf("dia chi ten mang  %x \n",&a);
    printf("dia chi cua con tro phan tu dau tien %x",p);
}
dia chi ten mang 62fe00

dia chi cua con tro phan tu dau tien 62fe00

Chú ý: Địa chỉ ô nhớ trên mỗi máy tính khi thực thi chương trình trên là khác nhau. Kết quả 62fe00 chỉ là kết quả minh họa.

Thực thi chương trình ta nhận thấy rằng, địa chỉ của tên mảng và địa chỉ của con trỏ phần tử đầu tiên trong mảng là như nhau.

Hiểu được địa chỉ ô nhớ của mảng để dễ dàng sử dụng con trỏ tương tác với mảng hơn.

2.2 Sử dụng con trỏ truy cập giá trị và địa chỉ của phần tử mảng một chiều

Sử dụng khái niệm đỉa chỉ ô nhớ của mảng một chiều ở phần trên, bây giờ ta có thể thao tác dễ dàng để truy cập vào giá trị và địa chỉ của mảng bằng các phép toán con trỏ.

Địa chỉ của một phần tử mảng có thể được biểu diễn theo hai cách:

  1. Sử dụng & trước phần tử mảng, ví dụ &a[i]
  2. Sử dụng biểu thức (a+i)

Xét vị dụ dưới đây tôi có mảng a gồm 5 phần tử, tôi sẽ lấy địa chỉ của từng phần tử trong mảng bằng &a[i] và vòng lặp for:

#include <stdio.h>
int main()
{
    //khai bao mang a gom 5 phan tu
    int a[5] = {0,1,2,3,4};
    //lay dia chi cua tung phan tu
    for(int i = 0; i < 5; i++){
        printf("Dia chi cua a[%d]: %x \n", i, &a[i]);
    }
}
Dia chi cua a[0]: 62fe00

Dia chi cua a[1]: 62fe04

Dia chi cua a[2]: 62fe08

Dia chi cua a[3]: 62fe0c

Dia chi cua a[4]: 62fe10

Chú ý: Địa chỉ ô nhớ trên mỗi máy tính khi thực thi chương trình trên là khác nhau. Kết quả trên chỉ là minh họa

Ví dụ lần này tôi sẽ dùng (a+i) thay cho &a[i], kết quả thu được là tương tự:

#include <stdio.h>
int main()
{
    //khai bao mang a gom 5 phan tu
    int a[5] = {0,1,2,3,4};
    //lay dia chi cua tung phan tu
    for(int i = 0; i < 5; i++){
        printf("Dia chi cua a[%d]: %x \n", i, (a + i));
    }
}
Dia chi cua a[0]: 62fe00

Dia chi cua a[1]: 62fe04

Dia chi cua a[2]: 62fe08

Dia chi cua a[3]: 62fe0c

Dia chi cua a[4]: 62fe10

Qua 2 ví dụ trên ta đã biết cách lấy địa chỉ của từng phần tử có trong mảng, bây giờ ta có thể lấy giá trị của phần tử bằng cách khai báo con trỏ mảng và đặt biểu thức * trước địa chỉ cần lấy giá trị. (Nếu quên khái niệm này bạn đọc vui lòng đọc lại bài trước tôi đã giải thích về dấu * )

Chương trình dưới đây, tôi sẽ sử dụng con trỏ p gán bằng mảng a sau đó dùng con trỏ p để lấy ra các giá trị tại các địa chỉ của mỗi phần tử có trong mảng a

#include <stdio.h>
int main()
{
    //khai bao mang a gom 5 phan tu
    int a[5] = {0,1,2,3,4};
    //gan con tro p bang mang a
    int *p; 
    p = a; // p = &a[0] cung giong nhu p = a
    //lay gia tri va dia chi cua tung phan tu
    for(int i = 0; i < 5; i++){
        printf("Gia tri cua a[%d] = %d ", i, *(p + i));
        printf("Dia chi cua a[%d] = %x \n", i, (p + i));
    }
}
Gia tri cua a[0] = 0 Dia chi cua a[0] = 62fdf0

Gia tri cua a[1] = 1 Dia chi cua a[1] = 62fdf4

Gia tri cua a[2] = 2 Dia chi cua a[2] = 62fdf8

Gia tri cua a[3] = 3 Dia chi cua a[3] = 62fdfc

Gia tri cua a[4] = 4 Dia chi cua a[4] = 62fe00

Giải thích nhanh ví dụ trên:

  1. Gán con trỏ p = a hoặc có thể gán p = &a[0] hai điều này là giống nhau (xem lại phần Địa chỉ ô nhớ của mảng một chiều)
  2. Sử dụng toán tử *(p+i) để lấy giá trị của phần tử mảng tại vị trí thứ i
  3. Sử dụng biểu thức (p+i) để lấy địa chỉ của phần tử mảng tại vị trí i

Ví dụ tiếp theo tôi sử dụng con trỏ p để lấy ra địa chỉ các phần tử trong mảng a có giá trị là số chẵn

#include <stdio.h>
int main(){
    //khai bao mang a gom 5 phan tu
    int a[5] = {0,1,2,3,4};
    //gan con tro p bang mang a
    int *p; 
    p = a; // p = &a[0] cung giong nhu p = a
    //lay gia tri va dia chi cua tung phan tu
    printf("Gia tri va dia chi cac phan tu chan la: \n");
    for(int i = 0; i < 5; i++){
        //kiem tra dieu kien neu gia tri *(p+i) % 2 ==0 thi hien thi ra dia chi va gia tri
        if(*(p + i) % 2 == 0){
            printf("Gia tri cua a[%d] = %d ", i, *(p + i));
            printf("Dia chi cua a[%d] = %x \n", i, (p + i));
        }
    }
}
Gia tri va dia chi cua cac phan tu chan la:

Gia tri cua a[0] = 0 Dia chi cua a[0] = 62fdf0

Gia tri cua a[2] = 2 Dia chi cua a[2] = 62fdf8

Gia tri cua a[4] = 4 Dia chi cua a[4] = 62fe00

Ta nhận thấy rằng trong mảng a có các giá trị chẵn tại các chỉ số lần lượt là a[0], a[2], a[4], cách lấy giá trị theo con trỏ là *(p+0), *(p+2), *(p + 4) và cách lấy địa chỉ là (p + 0), (p +2), (p +4).