Trong các bài học trước, chúng ta đã nói rất nhiều về mảng cố định và mảng động. Mặc dù cả hai đều được tích hợp ngay vào ngôn ngữ C++, nhưng cả hai đều có nhược điểm: Mảng cố định bị phân rã thành con trỏ dẫn đến mất thông tin về độ dài mảng, còn mảng động phải chú ý về vấn đề giải phóng bộ nhớ và thay đổi kích thước mảng động mà không gặp lỗi.
Để giải quyết những vấn đề này, thư viện standard của ngôn ngữ C++ đã bổ sung thêm một số tính năng giúp cho việc quản lý mảng dễ dàng hơn, đó là std::array và std::vector. Chúng ta sẽ nói về std::array trong bài học này, và std::vector trong bài học tiếp theo.
Nội dung chính
1. Giới thiệu về std::array
Được giới thiệu trong C++ 11, std::array cung cấp tính năng mảng cố định mà sẽ không bị phân rã khi được truyền vào trong một hàm. std::array được định nghĩa trong header <array>, bên trong namespace std.
Khai báo một biến mảng std::array rất dễ dàng:
#include <array>
std::array<int, 3> myArray; // declare an integer array with length 3
Giống như cài đặt gốc (native implementation) của mảng cố định, độ dài của một mảng std::array cũng phải được biết tại thời điểm biên dịch.
std::array có thể được khởi tạo bằng danh sách khởi tạo (initializer lists) hoặc khởi tạo đồng đều (uniform initialization):
std::array<int, 5> myArray = { 9, 7, 5, 3, 1 }; // initializer list
std::array<int, 5> myArray2 { 9, 7, 5, 3, 1 }; // uniform initialization
Không giống như mảng cố định được tích hợp sẵn trong C++, bạn không thể bỏ qua độ dài mảng khi khai báo std::array:
std::array<int, > myArray { 9, 7, 5, 3, 1 }; // illegal, array length must be provided
std::array<int> myArray { 9, 7, 5, 3, 1 }; // illegal, array length must be provided
Tuy nhiên, kể từ C++ 17, việc bỏ qua kiểu dữ liệu và kích thước mảng khi khai báo mảng std::array đã được cho phép. Tuy nhiên, chúng chỉ có thể được bỏ qua cùng với nhau, chứ không thể bỏ qua một trong hai cái, và chỉ khi mảng được khởi tạo rõ ràng.
std::array myArray { 9, 7, 5, 3, 1 }; // The type is deduced to std::array<int, 5>
std::array myArray { 9.7, 7.31 }; // The type is deduced to std::array<double, 2>
Chúng tôi thích cú pháp này hơn là phải gõ ra kiểu dữ liệu và kích thước khi khai báo. Nếu trình biên dịch của bạn không có khả năng của C++ 17, bạn cần sử dụng cú pháp khai báo rõ ràng cả kiểu dữ liệu và kích thước mảng std::array.
/**
* 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/
*/
// std::array myArray { 9, 7, 5, 3, 1 };
std::array<int, 5> myArray { 9, 7, 5, 3, 1 };
// std::array myArray { 9.7, 7.31 };
std::array<double, 2> myArray { 9.7, 7.31 };
Bạn cũng có thể gán các giá trị cho mảng, sử dụng một danh sách khởi tạo (initializer list):
std::array<int, 5> myArray;
myArray = { 0, 1, 2, 3, 4 }; // okay
myArray = { 9, 8, 7 }; // okay, elements 3 and 4 are set to zero!
myArray = { 0, 1, 2, 3, 4, 5 }; // not allowed, too many elements in initializer list!
Việc truy cập tới các giá trị của mảng std::array, sử dụng chỉ số mảng (index) sẽ giống với mảng cố định bình thường:
std::cout << myArray[1] << '\n';
myArray[2] = 6;
Cũng giống như các mảng cố định bình thường, toán tử subscript chỉ định chỉ số phần tử mảng để truy cập sẽ không thực hiện bất kỳ phép kiểm tra giới hạn mảng nào. Nếu cung cấp một chỉ số mảng không hợp lệ, chương trình của bạn sẽ xảy ra lỗi.
Mảng std::array hỗ trợ một dạng truy cập phần tử mảng thứ hai (hàm at()), có bao gồm kiểm tra giới hạn:
std::array myArray { 9, 7, 5, 3, 1 };
myArray.at(1) = 6; // array element 1 valid, sets array element 1 to value 6
myArray.at(9) = 10; // array element 9 is invalid, will throw an error
Trong ví dụ trên, lời gọi hàm array.at(1) sẽ kiểm tra để đảm bảo rằng phần tử mảng tại index 1 là hợp lệ, và nó hợp lệ thật, nên một tham chiếu đến phần tử mảng tại index 1 sẽ được trả về. Sau đó, chúng ta gán giá trị 6 cho phần tử này. Tuy nhiên, lời gọi hàm array.at(9) sẽ không thành công do phần tử mảng tại index 9 đã nằm ngoài giới hạn của mảng. Lúc này, thay vì trả về một tham chiếu, hàm at() sẽ ném ra một lỗi làm chấm dứt chương trình (lưu ý rằng: Nó thực sự sẽ ném ra một ngoại lệ có kiểu std::out_of_range – chúng ta sẽ đề cập đến các ngoại lệ trong chương 15). Do hỗ trợ việc kiểm tra giới hạn, nên hàm at() sẽ chậm hơn (nhưng an toàn hơn) so với toán tử truy cập phần tử mảng [].
Mảng std::array sẽ tự động được dọn dẹp (giải phóng bộ nhớ) sau khi nó nằm ngoài phạm vi của đoạn code mà chương trình đang chạy, do đó, không cần phải thực hiện bất kỳ hình thức dọn dẹp thủ công nào.
2. Kích thước mảng và sắp xếp mảng std::array
Hàm size() có thể được sử dụng để lấy về độ dài của mảng std::array:
std::array myArray { 9.0, 7.2, 5.4, 3.6, 1.8 };
std::cout << "length: " << myArray.size() << '\n';
Kết quả in ra:
length: 5
Bởi vì mảng std::array sẽ không bị phân rã thành một con trỏ khi được truyền cho một hàm nào đó, nên hàm size() sẽ hoạt động ngay cả khi bạn gọi nó từ bên trong một hàm:
/**
* 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 <array>
#include <iostream>
void printLength(const std::array<double, 5> &myArray)
{
std::cout << "length: " << myArray.size() << '\n';
}
int main()
{
std::array myArray { 9.0, 7.2, 5.4, 3.6, 1.8 };
printLength(myArray);
return 0;
}
Đoạn code trên sẽ in ra:
length: 5
Lưu ý rằng thư viện standard sử dụng thuật ngữ “size” để nói đến độ dài mảng – đừng nhầm lẫn với kết quả của hàm sizeof() trên một mảng cố định được tích hợp sẵn trong C++, cái mà trả về kích thước thực sự của mảng trong bộ nhớ (được tính bằng kích thước của một phần tử mảng nhân với độ dài của mảng). Đây là một sự không nhất quán trong cách đặt tên của C++.
Cũng cần lưu ý rằng hàm std::array sẽ được truyền theo kiểu tham chiếu (hằng). Điều này để ngăn trình biên dịch tạo ra một bản sao của mảng std::array khi mảng std::array được truyền vào hàm (nhằm cải thiện hiệu năng của chương trình).
Quy tắc: Luôn luôn truyền mảng std::array theo kiểu tham chiếu (pass by reference) hoặc tham chiếu hằng (pass by const reference).
Do luôn biết được độ dài của mảng std::array, ta có thể sử dụng vòng lặp for dựa trên phạm vi với mảng std::array:
std::array myArray{ 9, 7, 5, 3, 1 };
for (int element : myArray)
std::cout << element << ' ';
Bạn có thể sắp xếp std::array
bằng std::sort
:
/**
* 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 <algorithm> // for std::sort
#include <array>
#include <iostream>
int main()
{
std::array myArray { 7, 3, 1, 9, 5 };
std::sort(myArray.begin(), myArray.end()); // sort the array forwards
// std::sort(myArray.rbegin(), myArray.rend()); // sort the array backwards
for (int element : myArray)
std::cout << element << ' ';
std::cout << '\n';
return 0;
}
Kết quả in ra:
1 3 5 7 9
Hàm std::sort() sử dụng các iterators để sắp xếp mảng std::array, đây là một khái niệm mà hiện nay chúng ta chưa đề cập đến, vì thế bây giờ bạn có thể chưa cần quan tâm tới các tham số truyền vào hàm std::sort(). Chúng tôi sẽ giải thích khái niệm iterator này sau.
3. Đánh chỉ số thủ công cho mảng std::array thông qua size_type
Bạn hãy thử tìm xem, đoạn code sau có điều gì chưa đú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>
#include <array>
int main()
{
std::array myArray { 7, 3, 1, 9, 5 };
// Iterate through the array and print the value of the elements
for (int i{ 0 }; i < myArray.size(); ++i)
std::cout << myArray[i] << ' ';
std::cout << '\n';
return 0;
}
Câu trả lời là đoạn code trên tiềm ẩn khả năng về sự không khớp giữa việc sử dụng giá trị có dấu (signed value) và giá trị không có dấu (unsigned value)! Do một quyết định kỳ lạ, hàm size() và tham số chỉ số mảng truyền vào cho lệnh truy cập phần tử mảng operator[] sử dụng một kiểu gọi là size_type, được định nghĩa là một kiểu số nguyên không dấu trong thư viện standard của C++. Biến đếm vòng lặp/index của phần tử mảng (biến i) có kiểu signed int (kiểu số nguyên có dấu). Do đó, tồn tại sự không khớp/không phù hợp về kiểu dữ liệu được sử dụng giữa biến i để mô tả chỉ số của phẩn tử mảng với phép so sánh i < myArray.size() và phép truy cập giá trị phần tử mảng myArray[i].
Thật thú vị, size_type không phải là một kiểu dữ liệu toàn cục – global type (như kiểu int hoặc kiểu std::size_t). Thay vào đó, nó được định nghĩa bên trong phần định nghĩa của mảng std::array (C++ cho phép các kiểu dữ liệu lồng nhau – nested types). Điều này có nghĩa là khi muốn sử dụng kiểu size_type, chúng ta phải đặt tiền tố là kiểu dữ liệu mảng đầy đủ (full array type) (có thể nghĩ rằng, std::array sẽ đóng vai trò như một namespace trong trường hợp này). Trong ví dụ trên của chúng ta, kiểu dữ liệu “size_type” đã được đặt tiền tố đầy đủ sẽ là std::array<int, 5>::size_type.
Do đó, cách chính xác để viết đúng đoạn code trên sẽ là:
#include <array>
#include <iostream>
int main()
{
std::array myArray { 7, 3, 1, 9, 5 };
// std::array<int, 5>::size_type is the return type of size()!
for (std::array<int, 5>::size_type i{ 0 }; i < myArray.size(); ++i)
std::cout << myArray[i] << ' ';
std::cout << '\n';
return 0;
}
Có vẻ đoạn code trên vẫn hơi khó đọc, hãy sử dụng cú pháp type alias (tên thay thế) trong C++:
/**
* 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>
#include <array>
int main()
{
std::array myArray { 7, 3, 1, 9, 5 };
using index_t = std::array<int, 5>::size_type;
for (index_t i{ 0 }; i < myArray.size(); ++i)
std::cout << myArray[i] << ' ';
std::cout << '\n';
return 0;
}
Code của chúng ta có vẻ đã ổn hơn một chút, và giải pháp này có thể đã đạt được sự kết hợp tốt nhất giữa tính chính xác về mặt kỹ thuật và khả năng dễ đọc.
Trong tất cả các cài đặt của std::array, size_type là một bí danh dành cho std::size_t. Vì vậy, việc nhìn thấy các developers sử dụng std::size_t cho ngắn gọn là khá phổ biến.
#include <array>
#include <iostream>
int main()
{
std::array myArray { 7, 3, 1, 9, 5 };
for (std::size_t i{ 0 }; i < myArray.size(); ++i)
std::cout << myArray[i] << ' ';
std::cout << '\n';
return 0;
}
Có một giải pháp tốt hơn, đó là tránh việc đánh chỉ số mảng (indexing) std::array thủ công, ở vị trí đầu tiên trong vòng lặp for. Thay vào đó, hãy sử dụng các vòng lặp for dựa trên phạm vi (range-based for loops) (hoặc các iterators) nếu có thể.
Hãy nhớ rằng, các số nguyên không dấu sẽ bị wrap around khi bạn đạt đến giới hạn của chúng (giải thích một chút, số nguyên mà bị wrap around nghĩa là sao?? Wrap around có nghĩa là nếu chúng ta thực hiện tăng giá trị số nguyên lớn nhất có thể, nó sẽ tiếp tục từ giá trị số nguyên nhỏ nhất có thể, và ngược lại, nếu chúng ta giảm giá trị số nguyên nhỏ nhất có thể, nó sẽ tiếp tục từ giá trị số nguyên lớn nhất có thể). Một lỗi phổ biến hay mắc phải đó là index đã là 0 rồi nhưng developer vẫn giảm index, gây ra lỗi wrap-around khiến index trở thành giá trị số nguyên lớn nhất có thể. Bạn đã thấy điều này trong bài học về các vòng lặp for, nhưng chúng ta nhắc lại một chút để nhớ kỹ hơn.
#include <array>
#include <iostream>
int main()
{
std::array myArray { 7, 3, 1, 9, 5 };
// Print the array in reverse order.
// We can use auto, because we're not initializing i with 0.
// Bad:
for (auto i{ myArray.size() - 1 }; i >= 0; --i)
std::cout << myArray[i] << ' ';
std::cout << '\n';
return 0;
}
Đây là một vòng lặp vô hạn, lỗi hành vi không xác định sẽ xảy ra một khi giá trị của biến i bị wraps around. Có hai vấn đề ở đây. Nếu mảng myArrray trống, tức là hàm size() trả về 0 (điều này là khả thi đối với mảng std::array), thì phép myArray.size() – 1 sẽ bị wraps around. Vấn đề thứ hai là dù có bao nhiêu phần tử trong mảng thì phép i >= 0 sẽ luôn luôn đúng, vì số nguyên không dấu (unsigned integers) không thể nhỏ hơn 0.
Dưới đây là đoạn code đúng dùng để in mảng theo chiều ngược lại, trong đó biến i (biến chỉ số phần tử mảng) là một số nguyên không dấu (unsigned integer):
#include <array>
#include <iostream>
int main()
{
std::array myArray { 7, 3, 1, 9, 5 };
// Print the array in reverse order.
for (auto i{ myArray.size() }; i-- > 0; )
std::cout << myArray[i] << ' ';
std::cout << '\n';
return 0;
}
Đột nhiên, chúng ta giảm index trong phần điều kiện lặp của vòng for bằng toán từ — được đặt ở phần hậu tố của biến i. Câu lệnh kiểm tra điều kiện sẽ chạy trước mỗi lần lặp, bao gồm cả lần lặp đầu tiên. Trong lần lặp đầu tiên, biến i có giá trị là myArray.size() – 1, bởi vì i đã bị giảm trong phần kiểm tra điều kiện của vòng for. Khi i bằng 0 và chuẩn bị bị wrap around, điều kiện lặp của vòng for sẽ không còn đúng nữa và vòng lặp sẽ dừng lại. i thực sự đã bị wraps around khi chúng ta thực hiện phép i– trong lần cuối, nhưng sau đó nó đã không được sử dụng (vì vòng lặp for đã dừng do điều kiện lặp không còn đúng nữa), do đó đoạn chương trình trên chạy ổn.
4. Mảng các struct
Tất nhiên các phần tử của mảng std::array sẽ không chỉ giới hạn trong các kiểu dữ liệu số. Mọi kiểu dữ liệu có thể được sử dụng trong một mảng thông thường thì đều có thể được sử dụng trong mảng std::array.
/**
* 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 <array>
#include <iostream>
struct House
{
int number{};
int stories{};
int roomsPerStory{};
};
int main()
{
std::array<House, 3> houses{};
houses[0] = { 13, 4, 30 };
houses[1] = { 14, 3, 10 };
houses[2] = { 15, 3, 40 };
for (const auto& house : houses)
{
std::cout << "House number " << house.number
<< " has " << (house.stories * house.roomsPerStory)
<< " rooms\n";
}
return 0;
}
Kết quả in ra:
House number 13 has 120 rooms
House number 14 has 30 rooms
House number 15 has 120 rooms
Tuy nhiên, mọi thứ sẽ trở nên hơi kỳ cục khi chúng ta thử khởi tạo mảng.
// Doesn't work.
std::array<House, 3> houses{
{ 13, 4, 30 },
{ 14, 3, 10 },
{ 15, 3, 40 }
};
Mặc dù chúng ta có thể khởi tạo mảng std::array giống như đoạn code trên nếu các phần tử của nó thuộc những kiểu dữ liệu đơn giản, như int hoặc std::string, nhưng nó sẽ không hoạt động với các kiểu dữ liệu cần nhiều giá trị được tạo. Chúng ta cùng xem tại sao lại như vậy.
std::array là một kiểu dữ liệu tổng hợp, giống như House ở đoạn code trên. Không có hàm đặc biệt nào dành cho việc tạo mảng std::array, mảng bên trong của std::array (its internal array) được khởi tạo như thể nó là một biến thành viên của một struct. Để cho dễ hiểu hơn, chúng ta sẽ tự cài đặt một kiểu dữ liệu mảng đơn giản.
Cho đến hiện tại, chúng ta không thể làm điều này mà không phải truy cập vào biến (mảng) thành viên value của struct Array. Bạn sẽ tìm hiểu cách xử lý điều này sau, vì nó không ảnh hưởng đến vấn đề mà chúng ta đang xét.
struct Array
{
int value[3]{};
};
int main()
{
Array array{
11,
12,
13
};
return 0;
}
Đoạn code trên hoạt động như mong đợi. Mảng std::array cũng sẽ hoạt động ổn thế này nếu chúng ta sử dụng nó với các phần tử kiểu int. Khi thể hiện (instantiate) một struct, chúng ta có thể khởi tạo tất cả các biến thành viên của nó. Nếu chúng ta cố gắng tạo ra một Array của các HouseS, lỗi sẽ xuất hiện.
/**
* 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/
*/
struct House
{
int number{};
int stories{};
int roomsPerStory{};
};
struct Array
{
// This is now an array of House
House value[3]{};
};
int main()
{
// If we try to initialize the array, we get an error.
Array houses{
{ 13, 4, 30 },
{ 14, 3, 10 },
{ 15, 3, 40 }
};
return 0;
}
Khi chúng ta sử dụng cặp dấu ngoặc nhọn {} bên trong phần code khởi tạo của struct, trình biên dịch sẽ cố gắng khởi tạo từng biến thành viên của struct, cho mỗi cặp dấu ngoặc nhọn {}. Thay vì khởi tạo mảng Array như thế này (cách này sai):
// This is wrong
Array houses{
{ 13, 4, 30 }, // value[0]
{ 14, 3, 10 }, // value[1]
{ 15, 3, 40 } // value[2]
};
Thì trình biên dịch sẽ cố gắng khởi tạo mảng Array như sau:
Array houses{
{ 13, 4, 30 }, // value
{ 14, 3, 10 }, // ???
{ 15, 3, 40 } // ???
};
Cặp dấu ngoặc nhọn đầu tiên bên trong phần khởi tạo của mảng Array houses sẽ khởi tạo mảng value, bởi vì mảng value là biến thành viên thứ nhất của mảng Array. Nếu không có hai cặp dấu ngoặc ngọn còn lại, ta sẽ khởi tạo được một ngôi nhà (one house) với số nhà 13, 4 tầng (4 stories), và 30 phòng mỗi tầng (30 rooms per story).
Lưu ý: Có thể bỏ qua cặp dấu ngoặc nhọn {} ở ngoài cùng, khi tiến hành khởi tạo tổng hợp (aggregate initialization):
struct S
{
int arr[3]{};
int i{};
};
// These two do the same
S s1{ { 1, 2, 3}, 4 };
S s2{ 1, 2, 3, 4 };
Để khởi tạo tất cả các căn nhà, ở bên trong cặp dấu ngoặc nhọn {} đầu tiên, chúng ta cần làm như sau:
Array houses{
{ 13, 4, 30, 14, 3, 10, 15, 3, 40 }, // value
};
Đoạn code trên sẽ hoạt động, nhưng nó thật sự rất khó hiểu. Khó hiểu đến nỗi trình biên dịch có thể sẽ cảnh báo bạn về nó. Nếu chúng ta thêm các cặp dấu ngoặc nhọn {} vào xung quanh mỗi phần tử của mảng, thì phần code khởi tạo mạng sẽ rất dễ đọc.
/**
* 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>
struct House
{
int number{};
int stories{};
int roomsPerStory{};
};
struct Array
{
House value[3]{};
};
int main()
{
// With braces, this works.
Array houses{
{ { 13, 4, 30 }, { 14, 3, 10 }, { 15, 3, 40 } }
};
for (const auto& house : houses.value)
{
std::cout << "House number " << house.number
<< " has " << (house.stories * house.roomsPerStory)
<< " rooms\n";
}
return 0;
}
Đây chính là lý do tại sao bạn sẽ thấy thêm một cặp dấu ngoặc nhọn {} trong phần code khởi tạo của mảng std::array.
5. Tổng kết
std::array là một sự thay thế tuyệt vời cho các mảng cố định đã được tích hợp sẵn trong C++. Nó hiệu quả ở chỗ nó không sử dụng nhiều bộ nhớ hơn các mảng cố định. Nhược điểm thực sự duy nhất của mảng std::array so với mảng cố định được tích hợp sẵn là cú pháp có phần rắc rối hơn một chút, bạn phải chỉ định rõ độ dài mảng (trình biên dịch sẽ không tự động tính toán nó cho bạn trong phần code khởi tạo của mảng std::array, trừ khi bạn phải bỏ qua luôn cả kiểu dữ liệu của phần tử mảng std::array – điều mà không phải lúc nào cũng khả thi), và tồn tại vấn đề về không khớp trong việc sử dụng các kiểu dữ liệu số có dấu và không dấu đối với kích thước mảng std::array và biến đánh chỉ số phần tử mảng std::array. Nhưng đó là những vấn đề tương đối nhỏ – chúng tôi khuyên bạn nên sử dụng mảng std::array hơn là mảng cố định được tích hợp sẵn trong C++, cho những trường hợp cần sử dụng mảng theo kiểu nâng cao hơn, không tầm thường/đơn giản như chúng ta vẫn thường làm với mảng cố định.