8. 배열, 문자열, 그리고 여러 데이터를 다루는 법

하나보다 여러 개가 더 현실적이다
지금까지는 숫자 하나, 문자 하나, 참/거짓 하나처럼 값 하나를 중심으로 배웠다. 하지만 실제 프로그램은 대개 여러 값을 한꺼번에 다룬다. 학생 30명의 점수, 한 줄 문장, 게임 캐릭터 이름 목록, 일주일 매출처럼 말이다. 그래서 이번 장의 핵심은 아주 단순하다. 값을 하나씩 따로 들고 있지 말고, 묶어서 다루는 법을 익히는 것이다.
C++에서 가장 먼저 만나게 되는 묶음 도구는 배열이고, 글자를 다룰 때는 std::string이 사실상 기본 선택지다. 배열은 같은 타입의 값을 여러 개 담는 상자 묶음이고, std::string은 문자열을 안전하고 편하게 다루도록 만든 C++ 표준 라이브러리의 타입이다.12
배열: 같은 종류의 값을 줄 세워 저장하기
배열은 같은 자료형의 값을 고정된 개수만큼 순서대로 저장하는 자료구조다. 원소는 0부터 시작하는 인덱스로 접근한다.1
#include <iostream>
using namespace std;
int main() {
int scores[5] = {90, 85, 100, 76, 88};
cout << scores[0] << 'n'; // 90
cout << scores[3] << 'n'; // 76
scores[1] = 95;
cout << scores[1] << 'n'; // 95
}scores[0]은 첫 번째 원소이고, scores[4]가 마지막 원소다. 원소가 5개라면 쓸 수 있는 인덱스는 0부터 4까지다. 초보자가 가장 자주 하는 실수는 scores[5]처럼 범위를 벗어나는 접근이다. 이런 코드는 겉으로는 멀쩡해 보여도 이상한 결과를 만들 수 있다.
배열의 중요한 특징은 크기가 처음부터 정해진다는 점이다. int scores[5];라고 적었다면, 이 배열은 원소를 5개 담는 구조로 끝이다. 나중에 6개, 10개로 저절로 커지지 않는다. 이 점은 불편해 보이지만, 구조가 단순해서 배우기 좋다. 나중에 더 유연한 vector를 배우면 이 답답함이 왜 생겼는지 자연스럽게 이해하게 된다.
배열을 순회한다는 뜻
배열을 배웠다면 곧바로 익혀야 할 감각이 하나 있다. 바로 순회다. 순회는 처음부터 끝까지 원소를 하나씩 방문하는 것을 말한다. 여러 데이터를 다루는 일의 대부분은 결국 순회다.
#include <iostream>
using namespace std;
int main() {
const int COUNT = 5;
int scores[COUNT] = {90, 85, 100, 76, 88};
int sum = 0;
for (int i = 0; i < COUNT; i++) {
sum += scores[i];
}
double average = static_cast<double>(sum) / COUNT;
cout << "총점: " << sum << 'n';
cout << "평균: " << average << 'n';
}배열을 순회할 수 있게 되면 할 수 있는 일이 확 늘어난다.
모든 원소의 합 구하기
가장 큰 값이나 가장 작은 값 찾기
원하는 값이 있는지 검사하기
각 원소를 바꾸거나 가공해서 출력하기
즉, 배열은 데이터를 모아 두는 도구이고, 반복문은 그 데이터를 움직이게 하는 엔진이라고 생각하면 된다.
2차원 배열: 표처럼 생각하면 쉽다
배열 안에 배열이 들어가면 2차원 배열이 된다. 말이 어려워 보여도, 사실 표나 좌석 배치표를 떠올리면 쉽다. 행과 열이 있는 구조다.1
#include <iostream>
using namespace std;
int main() {
int seats[2][3] = {
{1, 0, 1},
{0, 1, 1}
};
for (int row = 0; row < 2; row++) {
for (int col = 0; col < 3; col++) {
cout << seats[row][col] << ' ';
}
cout << 'n';
}
}seats[0][2]는 0번째 행의 2번째 열을 뜻한다. 격자판 게임, 시간표, 작은 표 형태의 데이터는 이런 방식으로 표현할 수 있다. 처음에는 대괄호가 두 번 나온다는 사실에 익숙해지는 것만으로도 충분하다.
문자열은 문자들의 묶음이지만, 배열과는 다르다
문자 하나는 char로 저장한다. 그렇다면 글자 여러 개, 즉 문자열은 어떻게 저장할까? 여기서 두 길이 갈린다.
하나는 오래된 방식인 C 스타일 문자열이고, 다른 하나는 C++에서 기본처럼 쓰는 std::string이다. C 스타일 문자열은 char 배열을 바탕으로 하며 끝을 나타내는 특수 문자 '0'가 필요하다. 반면 std::string은 길이와 메모리를 스스로 관리해 더 안전하고 다루기 쉽다.23
| 구분 | C 스타일 문자열 | std::string |
|---|---|---|
| 기본 형태 | char name[] = "Alice"; | std::string name = "Alice"; |
| 문자열 끝 표시 | '0' 필요 | 내부적으로 관리 |
| 길이 다루기 | 별도 방식 필요 | .size() 또는 .length() 사용 |
| 붙이기/비교 | 함수 사용이 많음 | +, +=, == 등 사용 가능 |
| 보통 추천 | 특별한 이유가 있을 때 | 보통 이쪽이 기본 |
초보자라면 원칙 하나만 기억해도 충분하다. 특별한 이유가 없다면 문자열은 std::string부터 쓰면 된다.24
왜 std::string이 더 편한가
std::string은 그냥 문자 여러 개가 아니다. 문자열을 다루기 좋게 만들어진 클래스다. 그래서 길이를 쉽게 확인할 수 있고, 문자열끼리 이어 붙일 수 있고, 원하는 부분을 찾아낼 수도 있다.34
#include <iostream>
#include <string>
using namespace std;
int main() {
string first = "C++";
string second = "학습";
string title = first + " " + second;
cout << title << 'n';
cout << "길이: " << title.size() << 'n';
}만약 오래된 C 라이브러리나 C 방식 API와 연결해야 한다면 std::string을 C 스타일 문자열 형태로 꺼낼 수도 있다. 그럴 때 쓰는 것이 .c_str()이다.34 다만 지금 단계에서는 필요할 때 연결도 가능하다는 정도만 알고 넘어가면 충분하다.
std::string 기본 조작 익히기
문자열은 통째로 다뤄도 좋지만, 문자 하나씩 살펴보거나 일부만 잘라 쓸 수도 있다.
#include <iostream>
#include <string>
using namespace std;
int main() {
string text = "banana";
cout << text[0] << 'n'; // b
text[0] = 'B';
cout << text << 'n'; // Banana
cout << text.substr(0, 3) << 'n'; // Ban
if (text.find("nan") != string::npos) {
cout << "\"nan\"을 찾았습니다.n";
}
}여기서 자주 쓰는 기능을 짚어 보자.
text[index]는 특정 위치의 문자에 접근한다.text.size()는 문자열 길이를 돌려준다.text.substr(시작, 길이)는 부분 문자열을 만든다.text.find(찾을내용)은 처음 발견된 위치를 찾고, 없으면string::npos를 돌려준다.3
한 가지 더 중요한 점이 있다. operator[]는 편하지만 범위를 검사하지 않는다. 예를 들어 길이가 6인 문자열에서 text[100]에 접근하면 위험하다. 더 안전하게 접근하고 싶다면 text.at(index)를 쓸 수 있다. at()은 잘못된 위치에 접근할 때 예외를 던져 문제를 빨리 드러내 준다.35
공백이 있는 문자열 입력받기
문자열 입력에서도 한 번쯤 꼭 걸리는 함정이 있다. cin >> name;은 공백 전까지만 읽는다. 그래서 Hong Gil Dong을 입력하면 Hong까지만 들어간다. 문장 전체를 읽고 싶다면 getline()을 사용해야 한다.3
#include <iostream>
#include <string>
using namespace std;
int main() {
string sentence;
cout << "좋아하는 문장을 입력하세요: ";
getline(cin, sentence);
cout << "입력한 문장: " << sentence << 'n';
cout << "문장 길이: " << sentence.size() << 'n';
}이 차이를 모르면 분명 입력했는데 뒤가 사라지는 일이 생긴다. 프로그램이 말을 안 듣는 게 아니라, 우리가 다른 입력 방식을 쓴 것이다.
문자열도 순회할 수 있다
문자열도 결국 문자의 모임이므로 배열처럼 하나씩 볼 수 있다.3
#include <iostream>
#include <string>
using namespace std;
int main() {
string word = "robot";
for (int i = 0; i < word.size(); i++) {
cout << i << "번 위치: " << word[i] << 'n';
}
}이 감각은 의외로 중요하다. 나중에 대소문자 바꾸기, 모음 개수 세기, 특정 문자 제거하기 같은 문자열 문제는 거의 모두 문자열 순회로 풀게 된다.
배열과 문자열을 함께 써 보는 작은 예제
이제 여러 문자열을 배열에 넣어 보자. 배열은 같은 타입의 값을 여러 개 저장하므로, std::string 여러 개도 담을 수 있다.
#include <iostream>
#include <string>
using namespace std;
int main() {
const int COUNT = 5;
string words[COUNT] = {"cat", "banana", "sky", "computer", "book"};
int longestIndex = 0;
int totalLength = 0;
for (int i = 0; i < COUNT; i++) {
totalLength += words[i].size();
if (words[i].size() > words[longestIndex].size()) {
longestIndex = i;
}
}
cout << "가장 긴 단어: " << words[longestIndex] << 'n';
cout << "전체 글자 수: " << totalLength << 'n';
}이 짧은 예제 안에 중요한 감각이 꽤 많이 들어 있다. string 배열로 여러 단어를 저장했고, 반복문으로 전체를 순회했고, 각 문자열의 길이를 구했고, 비교를 통해 가장 긴 단어를 찾아냈다. 실전 프로그램은 이런 작은 동작들을 여러 번 조합해서 만들어진다.
자주 하는 실수
처음 배열과 문자열을 배우면 비슷해 보여서 헷갈리기 쉽다. 특히 아래 실수는 정말 자주 나온다.
배열 인덱스를 범위 밖까지 사용하기
배열은 자동으로 크기가 늘어날 거라고 기대하기
char배열과std::string을 완전히 같은 것으로 생각하기공백이 있는 문자열에서
cin >>와getline()의 차이를 놓치기
여기에 한 가지를 더 보태자면, 문자열도 결국 데이터 여러 개의 묶음이라는 사실을 자꾸 잊기 쉽다. 문자열 문제를 어려워하는 이유 중 하나는 문자열을 특별한 마법 상자로 보기 때문이다. 하지만 실제로는 문자 여러 개를 순서대로 저장한 구조다. 이 생각만 잡혀도 문자열이 훨씬 덜 낯설어진다.
이번 장에서 꼭 기억할 것
배열과 문자열은 앞으로 거의 모든 챕터에서 다시 만난다. 지금 단계에서는 아래 네 가지만 단단히 기억하면 충분하다.
배열은 같은 자료형의 값을 여러 개 저장하는 고정 크기 구조다.
배열 원소 접근은 0부터 시작하며, 범위를 벗어나면 위험하다.
문자열은 초보자 기준으로
std::string을 기본으로 쓰는 것이 좋다.여러 데이터를 다루는 핵심 기술은 결국 반복문을 이용한 순회다.
값 하나를 다루는 법을 배웠다면, 이제부터는 값 여러 개를 자연스럽게 바라보는 눈을 길러야 한다. 그 눈이 생기면 문제를 푸는 방식도 달라진다. 변수 하나 더 만들까가 아니라, 이건 묶어서 저장해야겠네라고 생각하게 된다. 바로 그 순간부터 코드가 조금 더 프로그래머답게 변한다.
참고
1Chapter 6: Arrays and the C++ String Class
2Modern C++ : string vs std::string vs std::array<char, N>
3std::basic_string - cppreference.com
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.