Con trỏ

Con trỏ

Con trỏ là địa chỉ của ô nhớ. Ô nhớ này cũng chính là biến chúng ta sử dụng. Khai báo con trỏ như sau Kiểu dữ liệu nó trỏ tới * <tên con trỏ>;. Toán tử một ngôi & đặt trước một biến sẽ lấy địa chỉ của biến đó.
Ví dụ khai báo và gán địa chỉ cho con trỏ:

1
2
3
int a = 5;
int* pointer_a = &a; // lưu con trỏ trỏ đến địa chỉ biến a
printf("%p", pointer_a); // in địa chỉ của biến a trên bộ nhớ

Để lấy giá trị của dữ liệu con trỏ trỏ tới, ta sử dụng toán tử một ngôi *.

1
2
3
4
5
6
7
8
int a = 5;
int* pointer_a = &a; // lưu con trỏ trỏ đến địa chỉ biến a

(*pointer_a) = 10; // tương tương với a = 10;

printf("%d", a); // in 10
printf("%d", *pointer_a); // in 10
printf("%p", pointer_a); // in địa chỉ của biến a

Độ lớn của con trỏ

Giá trị của con trỏ là địa chỉ của một biến khác. Địa chỉ này thường chỉ chiếm 8 bytes với hệ máy 64 bit. Vì 8 bytes tương đương với 64bit, vậy nên hệ máy 64 bit có nhiều nhất 264 địa chỉ. Mỗi địa chỉ tương ứng với 1 byte bộ nhớ.

1
2
3
4
5
6
7
8
9
typedef LargeStruct {
  int arr[100];
} LargeStruct;

sizeof(int*); // -> 8
sizeof(void*); // -> 8
sizeof(char*); // -> 8
sizeof(long*); // -> 8
sizeof(LargeStruct*); // -> 8

Con trỏ và mảng

Con trỏ có thể trỏ vào phần tử đầu tiên của mảng.

1
2
3
int arr[] = {1, 2, 3, 4, 5};
int* pointer_arr1 = arr; // trỏ vào phần tử đầu tiên của mảng
int* pointer_arr2 = &(arr[0]); // tương đương với câu lệnh trước

Có thể sử dụng con trỏ như mảng:

1
2
3
4
int arr[] = {1, 2, 3, 4, 5};
int* pointer_arr1 = arr;
printf("%d", *pointer_arr1);  // in phần tử đầu tiên
printf("%d", pointer_arr1[0]); // tương tự câu lệnh trên

Di chuyển con trỏ

Di chuyển con trỏ tới các phần tử tiếp theo bằng cách cộng con trỏ với một số nguyên. Cũng có thể sử dụng toán tử ++ hoặc -- để dịch chuyển con trỏ.

1
2
3
4
5
6
7
8
int arr[] = {1, 2, 3, 4, 5};
int* pointer_arr = arr;

printf("%d", *(pointer_arr1)); // in phần tử đầu tiên
printf("%d", pointer_arr1[0])); // in phần tử đầu tiên, tương tự câu lệnh trước

printf("%d", *(pointer_arr1 + 1)); // in phần tử thứ 2
printf("%d", pointer_arr1[1])); // in phần tử thứ 2, tương tự câu lệnh trước

Cộng con trỏ lên một sẽ thì con trỏ sẽ trỏ tới byte cách đó bằng kích thước kiểu dữ liệu nó trỏ tới.

1
2
3
4
5
6
7
8
int arr[] = {1, 2, 3, 4, 5};
int* pointer_arr = arr;
printf("%ld\n", sizeof(int)); // in kích cỡ kiểu dữ liệu kiểu int
printf("%p\n", pointer_arr); // in địa chỉ của con trỏ 

// in địa chỉ của phần tử tiếp theo, 
// bằng với kết quả trước + sizeof(int)
printf("%p\n", pointer_arr + 1); 

Con trỏ với string

Có thể khai báo và gán luôn giá trị cho con trỏ với string. Tuy nhiên không thể gán như vậy với kiểu mảng.

1
2
3
char* str = "Hello C program";
// char* str2 = { 'a', 'b', 'c', '\0' }; // không làm được kiểu này
printf("%s", str);

Con trỏ nhiều cấp

Con trỏ cấp hai thì lưu địa chỉ của địa chỉ, tương tự với con trỏ cấp cao hơn.

1
2
3
4
5
6
7
8
int a = 10;
int* pointer = &a; // pointer lưu địa chỉ của biến a
int** pointer_lever_2 = &pointer; // pointer_lever_2 lưu địa chỉ của biến pointer

printf("%d", *pointer); // in 10
printf("%p", pointer); // in địa chỉ của a
printf("%p", *pointer_lever_2); // in địa chỉ của a, tương tự dòng lệnh trên
printf("%d", **pointer_lever_2); // in 10