Toán tử & và *
Bài trước đã giới thiệu nhanh hai toán tử & và *. 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.
Toán tử & (address-of)
Phần tiêu đề “Toán tử & (address-of)”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;}Toán tử * khi khai báo vs khi sử dụng
Phần tiêu đề “Toán tử * khi khai báo vs khi sử dụng”Đây là điểm gây nhầm lẫn phổ biến nhất: dấu * có 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.
& và * là hai phép toán ngược nhau
Phần tiêu đề “& và * là hai phép toán ngược nhau”int x = 10;int* ptr = &x; // Lấy địa chỉ của x, gán cho ptrint 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 xstd::cout << *&x << std::endl; // 10Sửa giá trị thông qua con trỏ
Phần tiêu đề “Sửa giá trị thông qua con trỏ”#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;}Con trỏ cũng có địa chỉ riêng
Phần tiêu đề “Con trỏ cũng có địa chỉ riêng”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 intint y = *ptr; // ĐÚNG - lấy giá trị tại địa chỉ ptr đang trỏTổng kết
Phần tiêu đề “Tổng kết”&bien: lấy địa chỉ của một biến đã tồn tạiKieuDuLieu* 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)&và*là hai phép toán ngược nhau:*&xtương đươngx