Đối với hầu hết những nhà phát triển dự án trong C++ thì mảng và con trỏ luôn là tiêu điểm mà họ thường xuyên đau đầu trong việc vận hành. Chính vì vậy, chúng tôi sẽ hỗ trợ bạn tìm hiểu về mảng và con trỏ trong C++ qua những thông tin bổ ích bên dưới bài viết.
Sơ lược về mảng trong C++
Mảng là một kiểu dữ liệu có thể được dùng để phục vụ cho việc lưu một tập hợp những phần tử với trên cùng một kiểu dữ liệu. Thông thường, chúng ta có thể khai báo một tên mảng để định nghĩa nó kèm theo tùy chọn kích thước của nó. Từ đó, bạn sẽ truy cập những phần tử trên mảng tương ứng với chỉ số của chúng.
Ví dụ: Chúng ta muốn khai báo một mảng với 5 số nguyên thì dùng cú pháp như bên dưới
int arr[5]; “arr” là mảng được khai báo từ code trên với năm phần tử kiểu số nguyên. Do đó, để truy cập phần tử thứ i bất kỳ thì dùng cú pháp: arr[i];
Một điểm quan trọng cần phải nhớ là chỉ số của mảng sẽ chạy từ 0. Nói cách khác, phần tử đầu tiên của mảng sẽ mang chỉ số là 0, và chỉ số phần tử thứ hai là 1. Các bạn cứ thực hiện như vậy cho đến phần tử cuối cùng có chỉ số (kích thước mảng -1)
Trên thực tế, C++ hỗ trợ đa dạng những đặc tính và hàm để thao tác trên mảng từ đó hỗ trợ người dùng truy cập và biến đổi những phần tử trong mảng. Không những vậy, chúng ta còn có thể xử lý nhiều tác vụ khác trên mảng.
Con trỏ trong C++
Con trỏ (pointer) trong C++ được ví như biến để lưu trữ địa chỉ bộ nhớ cùng với một biến khác. Khi khai báo một biến, con trỏ sẽ lưu trữ tại một địa chỉ nằm ở trong bộ nhớ, và từ đó việc dùng con trỏ để thực thi và xử lý giá trị ứng với biến đó tương ứng qua địa chỉ của nó. Bên cạnh đó, khi khai báo con trỏ thì chúng ta dùng ký hiệu “*” sau kiểu dữ liệu con trỏ và phía trước tên của nó.
Ví dụ 1: Hãy dùng cú pháp bên dưới để khai báo con trỏ kiểu số nguyên.
int *ptr;
Dòng code này có chức năng khai báo một con trỏ với tên là “ptr” ứng với kiểu số nguyên. Do đó, hãy dùng toán tử “&” để lấy địa chỉ trên một biến nhằm gán giá trị trên con trỏ.
Ví dụ 2:
int x = 10; ptr = &x;
Khi đã gán xong giá trị trên con trỏ thông qua địa chỉ biến “x” thì người dùng hãy sử dụng toán tử “*” để tiếp cận giá trị biến này thông qua con trỏ.
Ví dụ 3: Dùng cú pháp bên dưới để in giá trị biến “x” thông qua con trỏ “ptr”.
cout << *ptr;
Toán tử “*” có thể được dùng cho việc giải tham chiếu con trỏ tức là lấy giá trị của biến từ con trỏ hướng đến.
Phân rã mảng trong C++ là gì?
Phân rã mảng trong ngôn ngữ lập trình C++ là phương pháp chia một mảng thành những phần tử con để giúp bạn giải quyết vấn đề hiệu quả và nhanh chóng. Lúc này, quá trình sẽ hỗ trợ tối ưu hóa không gian và thời gian liên quan đến những thuật toán phức tạp.
Sau đây, chúng tôi xin đưa ra ví dụ liên quan đến phân rã mảng (thuật toán truy tìm nhị phân). Nó hỗ trợ việc truy tìm một phần tử trong một mảng đã sắp xếp thông qua dùng phương pháp phân rã mảng.
Ví dụ:
int binarySearch(int arr[], int left, int right, int x) { if (right >= left) { int mid = left + (right - left) / 2; // Nếu phần tử tại giữa là phần tử cần tìm, trả về chỉ số của phần tử đó if (arr[mid] == x) return mid; // Nếu phần tử tại giữa lớn hơn x, tìm kiếm trong nửa đầu tiên của mảng if (arr[mid] > x) return binarySearch(arr, left, mid - 1, x); // Nếu phần tử tại giữa nhỏ hơn x, tìm kiếm trong nửa thứ hai của mảng return binarySearch(arr, mid + 1, right, x); } // Nếu không tìm thấy phần tử, trả về -1 return -1; }
Từ ví dụ này có thể thấy được hàm binarySearch được dùng để phân rã mảng nhằm truy tìm một phần tử trong mảng đã sắp xếp từ trước. Hàm trong ví dụ này đã dùng phương pháp đệ quy để truy tìm trong các nữa của mảng và cho ra kết quả là chỉ số của phần tử muốn tìm. Hàm sẽ trả về giá trị là -1 khi phần tử không tồn tại trong mảng.
Sự khác biệt giữa mảng và con trỏ trong C++
Mảng cố định hay con trỏ đều là những yếu tố quan trọng mà người lập trình phải quan tâm trước khi triển khai những dự án lớn sau này. Sau đây là những khác biệt chính của con trỏ và mảng cố định trong C++:
1. Khai báo
– Con trỏ thường được khai báo thông qua phương pháp chỉ định kiểu dữ liệu mà nó trỏ đến.
– Mảng cố định sẽ được khai báo thông qua việc chỉ định kích thước trên mảng cũng như kiểu dữ liệu trên những phần tử.
Ví dụ:
int arr[5]; // Khai báo mảng cố định int* ptr; // Khai báo con trỏ
2. Vùng nhớ
– Con trỏ sẽ hướng đến một địa chỉ cụ thể nằm trong bộ nhớ.
– Mảng cố định được biết đến như một khối liên tục những phần tử có thể được sử dụng để lưu trữ trong bộ nhớ.
Ví dụ:
int arr[5]; // Khai báo mảng cố định int* ptr; // Khai báo con trỏ // In địa chỉ của mảng cố định và con trỏ cout << "Địa chỉ của mảng cố định: " << arr << endl; cout << "Địa chỉ của con trỏ: " << ptr << endl;
3. Kích thước
– Kích thước con trỏ thường bị ảnh hưởng chủ yếu vào kiểu dữ liệu mà nó trỏ đến và sẽ bị thay đổi khi bạn chạy chương trình.
– Kích thước của mảng cố định là tuyệt đối không đổi trong quá trình chạy chương trình.
Ví dụ:
int arr[5]; // Khai báo mảng cố định int* ptr; // Khai báo con trỏ cout << "Kích thước của mảng cố định: " << sizeof(arr) << endl; cout << "Kích thước của con trỏ: " << sizeof(ptr) << endl;
4. Khả năng truy cập phần tử
– Những phần tử trong con trỏ thường được dùng để truy cập thông qua con trỏ cũng như toán tử trỏ đến.
– Các phần tử trong mảng cố định thì truy cập qua chỉ số của mảng.
Ví dụ:
int arr[5] = {1, 2, 3, 4, 5}; // Khai báo mảng cố định int* ptr = arr; // Con trỏ trỏ tới địa chỉ của mảng // In các phần tử của mảng cố định và con trỏ for (int i = 0; i < 5; i++) { cout << "Phần tử của mảng cố định tại chỉ số " << i << ": " << arr[i] << endl; cout << "Phần tử của con trỏ tại chỉ
Đánh giá quá trình truyền mảng cố định cho các hàm
Khi bàn về quá trình truyền mảng cố định cho các hàm trong C++, chúng ta cần phải quan tâm một số yếu tố sau:
1. Khai báo đổi số
– Đối số là một mảng cố định thường được dùng để khai báo thông qua phương pháp đặt kiểu dữ liệu cũng như tên đối số. Tiếp đến, hãy đặt cặp dấu ngoặc [] và gán kích thước trên mảng (nếu cần thiết).
– Nếu mảng không thể cố định kích thước, các bạn hãy dùng từ khóa const để gán kích thước cho nó.
Ví dụ:
// Hàm tính trung bình các phần tử của một mảng cố định double average(const int arr[], int size) { double sum = 0; for (int i = 0; i < size; i++) { sum += arr[i]; } return sum / size; }
2. Truy cập những phần tử trên mảng
– Những phần tử trên mảng cố định sẽ có thể được truy cập bằng chỉ số của mảng. Sau đây, chúng tôi xin trình bày ví dụ minh họa trong trường hợp này.
Ví dụ:
// Gọi hàm tính trung bình các phần tử của một mảng cố định int arr[] = {1, 2, 3, 4, 5}; double avg = average(arr, 5);
3. Phương pháp dùng mảng trong hàm
– Trong hàm, mảng cố định được ví như một con trỏ tới kiểu dữ liệu tương ứng với phần tử của mảng.
– Giá trị trên mảng cố định sẽ không bị ảnh hưởng khi bạn dùng hàm.
Ví dụ:
// Hàm sắp xếp các phần tử của một mảng cố định void sort(const int arr[], int size) { // Không thể thay đổi các giá trị của mảng cố định }
Truyền địa chỉ trong C++
Trong C++, việc truyền địa chỉ là cực kỳ quan trọng trong các dự án lớn. Nói cách khác, truyền địa chỉ được hiểu là phương pháp truyền tham số vào hàm trong C++ thông qua việc dùng địa chỉ từ biến đó hơn là truyền giá trị trực tiếp trên biến đó.
Mặt khác, chúng ta hoàn toàn có thể biến đổi giá trị trên biến đó nằm trong hàm và những thay đổi sẽ không bị ảnh hưởng sau khi hàm kết thúc trong quá trình truyền địa chỉ từ một biến vào hàm. Hơn thế nữa, truyền địa chỉ sẽ tối ưu bộ nhớ khi so sánh với phương pháp truyền giá trị.
Lúc này, các bạn sẽ dùng toán tử & (ampersand) trước tên biến đó để truyền địa chỉ từ một biến vào hàm.
Ví dụ:
void increment(int* numPtr) { (*numPtr)++; // tăng giá trị của biến tại địa chỉ numPtr lên 1 } int main() { int num = 10; increment(&num); // truyền địa chỉ của biến num vào hàm increment cout << num << endl; // in ra giá trị của biến num (11) return 0; }
Từ ví dụ trên có thể thấy rằng người dùng định nghĩa một hàm increment nhận vào con trỏ kiểu số nguyên và tăng giá trị trên biến tại địa chỉ đó lên 1. Nó sẽ không trả về bất cứ giá trị nào. Khi xét đến hàm main, người dùng tạo biến num và gán giá trị là 10.
Tiếp theo, truyền địa chỉ của biến vào hàm increment để in giá trị của biến num ra màn hình. Cuối cùng, kết quả hiển thị là: 11 vì giá trị của biến num đã tăng lên 1 trong hàm increment.
Tại sao mảng nằm trong class và struct không bị phân rã?
Mảng nằm trong class và struct sẽ không bị phân rã trong C++ khi bạn truyền vào hàm hay khi chuyển đổi qua lại những đối tượng. Ngoài ra, lý do quan trọng là đảm bảo dữ liệu trên mảng không bị mất đi khi truyền qua những hàm hoặc biến đổi trên những đối tượng như struct/class.
Ví dụ:
class MyArray { private: int arr[5]; public: void setArray(int index, int value) { arr[index] = value; } int getArray(int index) { return arr[index]; } }; int main() { MyArray myArray; myArray.setArray(0, 10); myArray.setArray(1, 20); cout << myArray.getArray(0) << endl; // in ra giá trị của phần tử đầu tiên của mảng arr trong đối tượng MyArray return 0; }
Đoạn code trên được định nghĩa thông qua một class MyArray kèm theo một mảng arr với độ dài là 5. Lúc này tại hàm main, người dùng sẽ khởi tạo một đối tượng myArray thuộc kiểu MyArray và gọi những phương thức setArray hỗ trợ việc set giá trị cho những phần tử trong mảng arr.
Từ đó, gọi phương thức getArray cho việc lấy giá trị phần tử đầu tiên trong mảng arr. Sau đó, chạy code và in kết quả ra màn hình. Nó trả giá trị là 10 và đặc biệt không có việc mảng bị phân rã khi các bạn chuyển đối tượng MyArray qua những phương thức hay truyền vào hàm.
Lời kết
Mảng và con trỏ trong C++ thật sự không hề đơn giản và dễ dàng chinh phục. Tuy nhiên, chúng tôi tin rằng bài viết này đã cởi bỏ được những áp lực hay khúc mắc của người lập trình trong việc tiếp cận đến chúng.
Cuối cùng, nếu các bạn thấy bài viết hay và bổ ích, đừng quên chia sẻ để nâng cao tính tương tác trong cộng đồng C++ ở Việt Nam nhé.