Bỏ qua để đến nội dung

Toán tử & và *

Bài trước đã giới thiệu nhanh hai toán tử &*. Bài này đi sâu hơn vào cách chúng hoạt động và những điểm dễ gây nhầm lẫn.

Khi đứng trước một biến, & trả về địa chỉ bộ nhớ của biến đó:

#include <iostream>
int main() {
int x = 10;
std::cout << x << std::endl; // 10 - giá trị của x
std::cout << &x << std::endl; // Địa chỉ của x, ví dụ 0x7ffd8e2f3a1c
return 0;
}

Đây là điểm gây nhầm lẫn phổ biến nhất: dấu *hai vai trò khác nhau tùy vào ngữ cảnh.

* trong khai báo: nghĩa là “đây là con trỏ”

Phần tiêu đề “* trong khai báo: nghĩa là “đây là con trỏ””
int* ptr; // Khai báo ptr là MỘT CON TRỎ trỏ đến kiểu int

* khi sử dụng: nghĩa là “dereference” - truy cập giá trị tại địa chỉ

Phần tiêu đề “* khi sử dụng: nghĩa là “dereference” - truy cập giá trị tại địa chỉ”
int x = 10;
int* ptr = &x;
std::cout << *ptr << std::endl; // 10 - "đi tới" địa chỉ ptr đang trỏ, lấy giá trị

Cách phân biệt: nếu * xuất hiện ngay sau một kiểu dữ liệu (int*, double*…) thì đó là khai báo con trỏ; nếu * xuất hiện trước một biến đã tồn tại (*ptr) thì đó là dereference.

int x = 10;
int* ptr = &x; // Lấy địa chỉ của x, gán cho ptr
int y = *ptr; // Lấy giá trị tại địa chỉ ptr đang trỏ, gán cho y
std::cout << y << std::endl; // 10
// *&x tương đương với x
std::cout << *&x << std::endl; // 10
#include <iostream>
int main() {
int score = 50;
int* ptr = &score;
std::cout << score << std::endl; // 50
*ptr = 100; // Thay đổi giá trị TẠI địa chỉ mà ptr đang trỏ
std::cout << score << std::endl; // 100 (score đã đổi!)
return 0;
}

Bản thân con trỏ cũng là một biến, nên nó cũng được lưu ở một địa chỉ nào đó trong bộ nhớ:

int x = 10;
int* ptr = &x;
std::cout << ptr << std::endl; // Địa chỉ của x (giá trị mà ptr đang lưu)
std::cout << &ptr << std::endl; // Địa chỉ của CHÍNH ptr (khác với địa chỉ của x)

Điều này dẫn đến khái niệm “con trỏ trỏ đến con trỏ” - sẽ được trình bày ở bài riêng.

Lỗi thường gặp: quên toán tử & hoặc *

Phần tiêu đề “Lỗi thường gặp: quên toán tử & hoặc *”
int x = 10;
int* ptr;
// ptr = x; // LỖI BIÊN DỊCH! x là giá trị int, không phải địa chỉ
ptr = &x; // ĐÚNG - gán địa chỉ của x cho ptr
// int y = ptr; // LỖI BIÊN DỊCH! ptr là địa chỉ, không thể gán trực tiếp cho biến int
int y = *ptr; // ĐÚNG - lấy giá trị tại địa chỉ ptr đang trỏ
  • &bien: lấy địa chỉ của một biến đã tồn tại
  • KieuDuLieu* ten: khai báo một con trỏ (dấu * đi cùng kiểu dữ liệu)
  • *con_tro: truy cập/thay đổi giá trị tại địa chỉ mà con trỏ đang trỏ đến (dereference)
  • &* là hai phép toán ngược nhau: *&x tương đương x