Lưu ý: Chương này sẽ khó hơn một chút so với những chương trước. Nếu
bạn cảm thấy hơi nản lòng, hãy cố gắng kiên trì. Vẫn chưa tới những phần kiến
thức hay nhất đâu!

Trong bài Structs, bạn đã biết rằng có thể sử dụng một Struct để kết hợp nhiều kiểu dữ liệu khác nhau, thành một kiểu dữ liệu mới. Điều này rất tuyệt trong những trường hợp mà chúng ta muốn mô hình hóa một đối tượng bao gồm nhiều thuộc tính khác nhau. Tuy nhiên, việc sử dụng Struct sẽ không hợp lý trong những trường hợp mà chúng ta muốn lưu lại những liên kết tham chiếu tới các thể hiện, hoặc tới một thành phần nào đó trong code.
Và may mắn thay, Struct không phải là kiểu dữ liệu kết hợp duy nhất trong C++. Một Array (mảng) là một kiểu dữ liệu kết hợp cho phép chúng ta truy cập tới nhiều biến có cùng kiểu dữ liệu, thông qua một định danh duy nhất (hay nói đơn giản là thông qua một tên duy nhất).

Cùng xem ví dụ dưới đây, trong trường hợp bạn muốn ghi lại điểm thi cho 30 học sinh trong một lớp. Nếu không có Array, bạn sẽ phải khai báo/cấp phát 30 biến gần như giống hệt nhau.

// allocate 30 integer variables (each with a different name)
int testScoreStudent1;
int testScoreStudent2;
int testScoreStudent3;
// ...
int testScoreStudent30;

Mảng cung cấp cho chúng ta một cách đơn giản hơn nhiều để làm việc này. Những câu lệnh khai báo cồng kềnh phía trên có thể được thay bằng khai báo mảng đơn giản sau:

int testScore[30]; // allocate 30 integer variables in a fixed array

Trong một khai báo biến mảng, chúng ta sử dụng cặp dấu ngoặc vuông [] để báo cho trình biên dịch rằng: Đây là một biến mảng (chứ không phải là một biến thông thường), và có bao nhiêu biến cùng kiểu dữ liệu sẽ được cấp phát (gọi là độ dài mảng).

Trong ví dụ trên, chúng ta đã khai báo một mảng cố định tên là testScore, có độ dài 30. Một mảng cố định (hay còn gọi là một mảng có độ dài cố định, hoặc mảng có kích thước cố định) là một mảng có độ dài được khai báo tường minh trước thời điểm biên dịch. Khi testScore được khởi tạo, trình biên dịch sẽ cấp phát 30 biến kiểu integer.

1. Phần tử mảng và việc subscripting

Mỗi biến trong mảng được gọi là một phần tử mảng. Các phần tử mảng sẽ không có tên riêng của chúng. Thay vào đó, để truy cập tới các phần tử riêng lẻ của một mảng, chúng ta sẽ sử dụng tên mảng, cùng với toán tử subscript (chính là cặp dấu ngoặc vuông []), và một đối số của subscript (hoặc index – chỉ số của phần tử mảng), đối số subscript này sẽ cho trình biên dịch biết phần tử mà ta muốn truy cập, nằm ở vị trí thứ mấy trong mảng. Quá trình này được gọi là thực hiện subscripting hoặc indexing trên một mảng. Có thể hiểu đơn giản rằng subscripting hay indexing chính là truy cập đến một phần tử của mảng tại một vị trí nào đó, được chỉ định (bởi chúng ta – những lập trình viên).

Trong ví dụ trên, phần tử đầu tiên trong mảng của chúng ta là testScore[0]. Phần tử thứ hai là testScore[1]. Phần tử thứ mười là testScore[9]. Phần tử cuối cùng trong mảng testScore của chúng ta là testScore[29]. Điều này thật tuyệt, bởi vì chúng ta sẽ không còn cần phải tạo ra một loạt các biến có tên khác nhau, nhưng lại liên quan đến nhau nữa. Chúng ta chỉ cần thay đổi đối số subscript để truy cập đến các phần tử mảng khác nhau.

Quan trọng: Có thể bạn đã nhận ra, không giống như trong cuộc sống hằng ngày, nơi mà chúng ta thường tính/đếm bắt đầu từ số 1, trong C++, mảng luôn luôn được tính bắt đầu từ 0. Hãy chú ý điều này 🙂

Ví dụ, một mảng có độ dài N, các phần tử mảng sẽ được đanh số từ 0 đến N-1. Đây được gọi là Phạm vi của mảng.

Một ví dụ về chương trình sử dụng mảng

Dưới đây là một chương trình ví dụ, vận dụng cả việc khai báo mảng và truy cập tới các phần tử mảng:

/**
* Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
*
* @author cafedevn
* Contact: cafedevn@gmail.com
* Fanpage: https://www.facebook.com/cafedevn
* Instagram: https://instagram.com/cafedevn
* Twitter: https://twitter.com/CafedeVn
* Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

#include <iostream>
 
int main()
{
    int prime[5]; // hold the first 5 prime numbers
    prime[0] = 2; // The first element has index 0
    prime[1] = 3;
    prime[2] = 5;
    prime[3] = 7;
    prime[4] = 11; // The last element has index 4 (array length-1)
 
    std::cout << "The lowest prime number is: " << prime[0] << "\n";
    std::cout << "The sum of the first 5 primes is: " << prime[0] + prime[1] + prime[2] + prime[3] + prime[4] << "\n";
 
    return 0;
}

Đoạn chương trình trên khi chạy sẽ in ra:

The lowest prime number is: 2
The sum of the first 5 primes is: 28

2. Các kiểu dữ liệu trong mảng

Mảng có thể được tạo thành từ bất kỳ kiểu dữ liệu nào. Cùng xem ví dụ dưới đây, trong đó chúng ta sẽ khai báo một mảng kiểu double:

/**
* Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
*
* @author cafedevn
* Contact: cafedevn@gmail.com
* Fanpage: https://www.facebook.com/cafedevn
* Instagram: https://instagram.com/cafedevn
* Twitter: https://twitter.com/CafedeVn
* Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

#include <iostream>
 
int main()
{
    double array[3]; // allocate 3 doubles
    array[0] = 2.0;
    array[1] = 3.0;
    array[2] = 4.3;
 
    std::cout << "The average is " << (array[0] + array[1] + array[2]) / 3 << "\n";
 
    return 0;
}

Chương trình này tạo ra kết quả:

The average is 3.1

Array cũng có thể được tạo thành từ các structs. Xét ví dụ sau:

struct Rectangle
{
    int length;
    int width;
};
Rectangle rects[5]; // declare an array of 5 Rectangle

Để truy cập tới một biến thành viên bên trong một phần tử mảng struct của một mảng, đầu tiên hãy lấy ra phần tử mảng mà bạn muốn, sau đó sử dụng toán tử lựa chọn thành viên để chọn được biến thành viên trong struct, mà bạn muốn: 

rects[0].length = 24;

Mảng thậm chí còn có thể được tạo thành từ nhiều mảng(mảng trong mảng), chủ đề này sẽ được đề cập đến tại một bài học trong tương lai.

3. Đối số subscript trong mảng

Trong C++, đối số subscript của mảng phải luôn luôn là một kiểu số nguyên, tức là, nó có thể là một trong các kiểu dữ liệu như: char, short, int, long, long long, v.v… Và thậm chí kiểu bool cũng được (trong đó, false xác dịnh chỉ số là 0 và true xác định chỉ số là 1). Một đối số subscript của mảng có thể là một literal value (giá trị bằng chữ), một biến (có thể là biến bình thường hoặc biến hằng), hoặc là một biểu thức mà kết quả tính toán của biểu thức đó là một giá trị có kiểu nguyên. Hãy xem ví dụ bên dưới để hiểu rõ hơn

Dưới đây là một số ví dụ:

/**
* Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
*
* @author cafedevn
* Contact: cafedevn@gmail.com
* Fanpage: https://www.facebook.com/cafedevn
* Instagram: https://instagram.com/cafedevn
* Twitter: https://twitter.com/CafedeVn
* Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

int array[5]; // declare an array of length 5
 
// using a literal (constant) index:
array[1] = 7; // ok
 
// using an enum (constant) index
enum Animals
{
    ANIMAL_CAT = 2
};
array[ANIMAL_CAT] = 4; // ok
 
// using a variable (non-constant) index:
short index = 3;
array[index] = 7; // ok
 
// using an expression that evaluates to an integer index:
array[1+2] = 7; // ok

4. Khai báo mảng có kích thước cố định

Khi khai báo một mảng cố định, độ dài của mảng này (chính là giá trị nằm giữa cặp dấu ngoặc vuông []) phải là một hằng số tại thời điểm biên dịch. Điều này là bởi vì trình biên dịch cần phải biết được chiều dài của một mảng cố định tại thời điểm biên dịch (compile time). Dưới đây là một số cách khác nhau để khai báo các mảng cố định:

/**
* Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
*
* @author cafedevn
* Contact: cafedevn@gmail.com
* Fanpage: https://www.facebook.com/cafedevn
* Instagram: https://instagram.com/cafedevn
* Twitter: https://twitter.com/CafedeVn
* Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

// using a literal constant
int array[5]; // Ok
 
// using a macro symbolic constant
#define ARRAY_LENGTH 5
int array[ARRAY_LENGTH]; // Syntactically okay, but don't do this
 
// using a symbolic constant
const int arrayLength = 5;
int array[arrayLength]; // Ok
 
// using an enumerator
enum ArrayElements
{
    MAX_ARRAY_LENGTH = 5
};
int array[MAX_ARRAY_LENGTH]; // Ok

Hãy lưu ý rằng, các biến không phải là biến hằng, hoặc các biến hằng tại runtime (thời điểm chương trình đang chạy) cũng không thể được sử dụng để khai báo mảng:

// using a non-const variable
int length;
std::cin >> length;
int array[length]; // Not ok -- length is not a compile-time constant!
 
// using a runtime const variable
int temp = 5;
const int length = temp; // the value of length isn't known until runtime, so this is a runtime constant, not a compile-time constant!
int array[length]; // Not ok

Chú ý rằng, trong hai trường hợp cuối này, chương trình sẽ bị lỗi bởi vì độ dài mảng không phải là một compile-time constant (biến hằng/hằng số tại thời điểm biên dịch). Một số loại trình biên dịch có thể cho phép những mảng kiểu như thế này (ví dụ C99), nhưng chúng không hợp lệ theo tiêu chuẩn C++ và không nên được sử dụng trong các chương trình C++.

5. Lưu ý về mảng động

Bởi vì các mảng cố định có các vùng nhớ được cấp phát tại thời điểm biên dịch, điều này dẫn đến hai hạn chế là:

  •  Các mảng cố định không thể có được độ dài dựa trên đầu vào của người dùng, hoặc một số giá trị khác được tính toán tại runtime – thời điểm chương trình chạy.
  •  Các mảng cố định có độ dài cố định, không thể thay đổi được.

Trong nhiều trường hợp, những hạn chế này có thể trở thành các vấn đề. May mắn thay, C++ có hỗ trợ một loại mảng thứ hai được gọi là mảng độngdynamic array. Độ dài của mảng động có thể được thiết lập tại runtime, và độ dài của chúng có thể được thay đổi. Tuy nhiên, việc khởi tạo các mảng động thường phức tạp hơn một chút, vì vậy, chúng ta sẽ cùng thảo luận về chúng sau, trong chương này.

Bài tập C về mảng một chiều

Bài tập C về mảng hai chiều

6. Tổng kết

Mảng cố định cung cấp một cách đơn giản để cấp phát/khai báo và sử dụng nhiều biến có cùng kiểu dữ liệu, miễn là trình biên dịch có thể biết được độ dài của mảng tại compile time – thời điểm biên dịch.

Chúng ta sẽ xem xét nhiều hơn về chủ đề xung quanh mảng cố định trong các bài học tiếp theo.

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!

2 BÌNH LUẬN

Bình luận bị đóng.