Con trỏ trong C rất dễ học và thú vị. Một số tác vụ lập trình C được thực hiện dễ dàng hơn với con trỏ và các tác vụ khác, chẳng hạn như cấp phát bộ nhớ động, không thể thực hiện được nếu không sử dụng con trỏ. Vì vậy, việc học con trỏ để trở thành một lập trình viên C hoàn hảo là điều cần thiết.

Như bạn đã biết, mọi biến có một vị trí trong bộ nhớ và mọi vị trí bộ nhớ đều có địa chỉ được xác định có thể được truy cập bằng toán tử &, toán tử này biểu diễn một địa chỉ trong bộ nhớ. Xét ví dụ sau:

#include <stdio.h>

int main () {

int nNumber;
char ch[10];

printf("Address of nNumber variable: %x\n", &nNumber);
printf("Address of ch variable: %x\n", &ch );

return 0;
}

Kết quả:

Address of nNumber variable: d045424
Address of ch variable: d04542e

1. Con trỏ là gì?

Con trỏ là một biến có giá trị là địa chỉ của một biến khác, nghĩa là địa chỉ trực tiếp của vị trí bộ nhớ. Giống như bất kỳ biến hay hằng nào, bạn phải khai báo một con trỏ trước khi sử dụng nó để lưu địa chỉ của bất kỳ biến nào. Cú pháp: type *var-name;

Trong đó: 

  • type là kiểu dữ liệu cơ sở của con trỏ, nó phải là kiểu dữ liệu C hợp lệ (ví dụ: int, double,...)
  • var-name là tên của biến con trỏ. 
  • dấu hoa thị * được sử dụng để khai báo một con trỏ, chỉ định một biến làm con trỏ. 

Hãy xem một số khai báo con trỏ hợp lệ:

int *ip; /* pointer to an integer */
double *dp; /* pointer to a double */
float *fp; /* pointer to a float */
char *ch; /* pointer to a character */

Kiểu dữ liệu thực sự của giá trị của tất cả các con trỏ, dù là số nguyên, số float, ký tự hay kiểu khác, đều giống nhau, đó là một số thập lục phân dài là biểu diễn của một địa chỉ bộ nhớ. Sự khác biệt duy nhất giữa các con trỏ thuộc các kiểu dữ liệu khác nhau là kiểu dữ liệu của biến hoặc hằng mà con trỏ trỏ tới.

2. Cách sử dụng con trỏ?

Có một số thao tác quan trọng cần được thực hiện với sự trợ giúp của con trỏ. (a) Chúng ta định nghĩa một biến con trỏ, (b) gán địa chỉ của một biến cho một con trỏ và (c) cuối cùng truy cập giá trị tại địa chỉ có sẵn trong biến con trỏ.

#include <stdio.h>

int main () {

int intNumber = 20; /* actual variable declaration */
int *ip; /* pointer variable declaration */

ip = &intNumber; /* store address of var in pointer variable*/

printf("Address of var variable: %x\n", &intNumber);

/* address stored in pointer variable */
printf("Address stored in ip variable: %x\n", ip );

/* access the value using the pointer */
printf("Value of *ip variable: %d\n", *ip );

return 0;
}

Kết quả sau khi thực thi chương trình:

Address of var variable: c623458
Address stored in ip variable: c623458
Value of *ip variable: 20

3. Con trỏ NULL

Cách tốt nhất là gán giá trị NULL cho một biến con trỏ trong trường hợp bạn không có địa chỉ chính xác để gán. Điều này được thực hiện tại thời điểm khai báo biến. Một con trỏ được gán NULL được gọi là con trỏ null. Con trỏ NULL là hằng số có giá trị bằng 0 được xác định trong một số thư viện chuẩn. 

Xét ví dụ sau:

#include <stdio.h>

int main () {

int *ptr = NULL;

printf("The value of ptr is : %x\n", ptr );
return 0;
}

Khi đoạn mã trên được biên dịch và thực thi, nó tạo ra kết quả sau: The value of ptr is 0

Trong hầu hết các hệ điều hành, các chương trình không được phép truy cập bộ nhớ tại địa chỉ 0 vì bộ nhớ đó được hệ điều hành dành riêng. Tuy nhiên, địa chỉ bộ nhớ 0 có ý nghĩa đặc biệt; nó có ý nghĩa làg con trỏ không nhằm trỏ đến một vị trí bộ nhớ có thể truy cập. Nhưng theo quy ước, nếu một con trỏ chứa giá trị null (0), thì nó được coi là không trỏ đến đâu cả.

Để kiểm tra con trỏ rỗng, bạn có thể sử dụng câu lệnh 'if' như sau -

if(ptr) /* thành công nếu p khác null */
if(!ptr) /* thành công nếu p là null */