HOWTO · C++
C++에서 벡터의 벡터 만들기
C++에서 std::vector를 중첩해 행과 열의 크기가 정해진 벡터를 만들고, 값을 채우고, 각 요소를 수정하는 방법을 알아봅니다.
이 페이지의 내용
벡터의 벡터는 바깥쪽 std::vector 안에 행을 나타내는 std::vector를 저장하는 구조입니다. 행과 열의 개수를 알고 있다면, 바깥쪽 벡터에는 행 개수를 지정하고 각 행으로 사용할 안쪽 벡터에는 열 개수와 초기값을 지정해 만들 수 있습니다. 아래 예제에서는 크기가 정해진 격자를 만들고, 예시 값으로 채운 뒤, 각 요소를 수정합니다. 행마다 벡터를 사용하고 행이나 열의 수가 프로그램 실행 중에 정해지는 경우에 유용한 방식입니다.
C++에서 크기와 초기값을 지정하는 생성자로 벡터의 벡터 만들기
크기와 초기값을 지정하는 생성자를 사용하면 두 차원과 초기 내용을 명확히 표현할 수 있습니다. 예를 들어 std::vector<std::vector<int>> grid(rows, std::vector<int>(columns, 0));는 각각 columns개의 정수가 0으로 초기화된 안쪽 벡터를 rows개 만듭니다. 안쪽 벡터 하나를 만든 다음, 그 값을 바깥쪽 벡터의 각 행을 초기화하는 데 사용합니다. 이는 크기와 초기값을 지정하는 생성 방식이며 기본 생성과는 다릅니다.
다음 예제는 정수 6개씩 들어 있는 행을 네 개 만듭니다. 벡터 인덱스는 0부터 시작하므로 [2][2]는 세 번째 행의 세 번째 열을 가리킵니다. 중첩된 .at() 호출은 네 번째 행의 네 번째 열을 선택합니다. .at()은 각 인덱스가 해당 벡터의 크기 안에 있는지 검사하고, 요소를 가리키지 않는 인덱스라면 범위를 벗어난 접근을 보고합니다. 인덱스가 유효함을 알고 있을 때는 대괄호 표기가 간결합니다.
#include <iostream>
#include <vector>
int main() {
constexpr int LENGTH = 4;
constexpr int WIDTH = 6;
std::vector<std::vector<int>> grid(LENGTH, std::vector<int>(WIDTH, 0));
grid[2][2] = 12;
std::cout << grid[2][2] << '\n';
grid.at(3).at(3) = 99;
std::cout << grid[3][3] << '\n';
return 0;
}
12
99
출력은 두 대입문으로 정해집니다. 행과 열에는 각각 해당 차원의 인덱스를 사용하세요. 바깥쪽 벡터가 행을 고르고, 그 행의 안쪽 벡터가 열을 고릅니다. 크기가 고정되어 있지 않다면 프로그램 데이터로 행의 개수와 각 행의 크기를 정할 수도 있습니다.
C++에서 rand 함수로 벡터의 벡터를 예시 값으로 채우기
격자를 만든 뒤에는 중첩 루프로 모든 정수를 순회하며 예시 값을 대입할 수 있습니다. 아래 프로그램은 현재 시각을 바탕으로 rand의 시드를 설정하고 % 100을 사용해 0부터 99까지의 값을 만듭니다. 실행할 때마다 출력 값이 달라지므로 그대로 옮겨 적을 수 있는 고정 출력은 없습니다. 예제에는 rand와 srand를 위한 <cstdlib>, std::time을 위한 <ctime> 헤더가 포함되어 있습니다.
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <vector>
int main() {
constexpr int LENGTH = 4;
constexpr int WIDTH = 6;
std::vector<std::vector<int>> grid(LENGTH, std::vector<int>(WIDTH, 0));
std::srand(static_cast<unsigned int>(std::time(nullptr)));
for (auto& row : grid) {
for (int& value : row) {
value = std::rand() % 100;
std::cout << value << ' ';
}
std::cout << '\n';
}
return 0;
}
이 예제는 시연이나 민감하지 않은 소규모 작업에 사용할 예시 데이터를 만드는 방법입니다. rand는 암호화 용도로 적합하지 않습니다. 또한 생성기가 낼 수 있는 값의 개수가 100으로 나누어떨어지지 않으면 나머지 연산 때문에 결과가 특정 값에 치우칠 수 있습니다. 시뮬레이션이나 더 나은 분포 특성이 필요한 응용 프로그램에서는 이 짧은 예제를 범용 난수 생성기로 간주하지 말고 요구 사항에 맞는 난수 기능을 선택하세요.
C++에서 중첩 범위 기반 루프로 벡터의 각 요소 수정하기
범위 기반 루프 하나가 각 행을 순회하고, 그 안의 루프가 해당 행의 정수를 하나씩 순회합니다. int& value에서 &는 value가 저장된 요소를 참조하게 하므로 대입하면 벡터 안의 값이 바뀝니다. 참조를 쓰지 않으면 루프 변수는 복사본이 되어 이를 수정해도 저장된 정수는 바뀌지 않습니다. 다음 프로그램은 각 요소에 3을 곱하기 전과 후의 격자를 출력합니다.
#include <iostream>
#include <vector>
void print(const std::vector<std::vector<int>>& grid) {
for (const auto& row : grid) {
for (int value : row) {
std::cout << value << ' ';
}
std::cout << '\n';
}
}
int main() {
std::vector<std::vector<int>> grid{{1, 2, 3}, {4, 5, 6}};
std::cout << "Before:\n";
print(grid);
for (auto& row : grid) {
for (int& value : row) {
value *= 3;
}
}
std::cout << "After:\n";
print(grid);
return 0;
}
각 안쪽 벡터는 자신의 정수 시퀀스를 관리하고, 바깥쪽 벡터는 행 벡터를 저장합니다. 한 행에 들어 있는 정수들은 연속된 메모리에 있지만, 중첩 구조 전체가 하나의 연속된 2차원 블록인 것은 아닙니다. 이는 저장 방식에 대한 설명이며 일반적인 속도 비교를 뜻하지는 않습니다. 성능은 연산과 작업량에 따라 달라집니다. 행을 서로 독립적으로 다루거나 일반적인 벡터 연산이 필요할 때 중첩 벡터를 사용할 수 있습니다. 작업에서 하나의 연속된 메모리 블록이 꼭 필요하다면 그런 배치를 지원하도록 설계된 표현을 선택하세요.