요약
이 수업은 강좌의 6단원, 곧 복합 데이터를 다루는 단원을 연다. 단원이 무엇을 위한 것인지 설명한다. 데이터의 컬렉션(고정 크기와 가변 크기의 시퀀스, 표, 레코드)을 표현하고, 직접 작성하거나 라이브러리에서 가져온 기본 알고리즘으로 그것을 처리하며, 그 비용을 추정하는 법을 가르치는 것이다. 단원의 여섯 수업과 그것들을 잇는 실을 소개한다. 단원이 이미 안다고 전제하는 것, 특히 불변식을 갖춘 반복문과 상수 참조 전달을 되짚는다. 단원이 해결하는 문제를 제시한다. 개수를 모르는 데이터를 저장해야 하는데, 낱낱의 변수로 작성하면 남는 데이터를 말없이 흘려버리는 프로그램의 문제이다. 알고리즘을 재는 두 번째 척도로서 정확성과 나란히 놓이는 비용을 미리 소개한다. 그리고 단원이 뒤의 단원들과 C++ 프로그래밍 II 강좌에서 어디로 이어지는지 보여 준다.
학습 목표
- 단원의 내용 순서를 설명하고, 각 수업이 뒤이은 수업에 무엇을 보태는지 설명한다.
- 반복문과 그 불변식, 매개변수 전달(값 전달, 참조 전달, 상수 참조 전달)을 떠올리고, 필요하면 복습한다.
- 예를 통해 알고리즘의 비용을, 정확성과 나란히 놓이는 품질의 두 번째 척도로서 미리 익힌다.
- 단원을 마쳤을 때 할 줄 알아야 하는 것이 무엇인지 알고, 그것을 뒤의 단원들에서 배울 것과 구별한다.
입문 평가
단원을 시작하기 전에 출발점을 재어 두면 좋다. 이 평가는 자기 평가이며 점수를 매기지 않는다. 목적은 오직 학습의 방향을 잡는 데 있다. 처음 네 문항은 단원이 이미 안다고 전제하는 것을 확인한다. 최댓값을 찾는 반복문의 불변식, 큰 매개변수의 전달 방식, 이중 중첩 반복문의 회차 수, 그리고 부호 없는 산술이다. 그 뒤의 여섯 문항은 수업 순서대로 단원의 핵심 개념을 다룬다. 시퀀스의 유효한 인덱스, 벡터의 생성, 어떤 크기를 절반으로 나눌 수 있는 횟수, 정렬된 데이터에서의 탐색, 표의 이중 인덱스, 그리고 같은 개체의 데이터를 서로 떨어진 컬렉션에 저장하는 위험이다. 아직 맞힐 의무는 누구에게도 없으며, 그중 몇 문항에서는 “아직 모르겠다”가 정직한 답이다. 맞든 틀리든 모든 답이 요점을 설명하고 그 요점을 다루는 수업을 알려 주기 때문이다. 같은 능력은 단원 끝의 종합 수업에서 다시 측정한다.
단원을 시작하기 전에 출발점을 확인해 보세요. 짧은 자기 평가입니다. 점수가 매겨지지 않으며 결과는 브라우저에만 저장됩니다.
문제 1이전 단원에서
int largest{0}; int t{}; while (std::cin >> t) { if (t > largest) { largest = t; } }코드 조각은largest변수에 읽은 기온 가운데 가장 높은 것을 남겨야 한다. 어떤 입력에서 실패하는가?첫 회차는 데이터를 0과 비교하여 양수이면 저장한다. 양수 데이터가 하나뿐이면 결과는 옳다. 답하기 전에 추적해 보는 것이 좋다.>연산자로는 최댓값과 같은 데이터가 최댓값을 대신하지 않지만, 최댓값은 같은 수이다. 반복은 결과를 바꾸지 않는다.초기화를 불변식으로 점검하지 않았음을 드러낸다. 올바른 반복문은 첫 회차 전에 불변식이 성립할 것을 요구한다.바라는 불변식 “largest변수는 지금까지 읽은 것의 최댓값이다”는 시작할 때 성립하지 않는다. 아무것도 읽기 전에는 최댓값이 없고, 초깃값 0이 모든 음수 데이터를 이기기 때문이다. 첫 데이터가 있는지 확인한 뒤 그 데이터로 초기화하면 고쳐진다. 벡터 수업은 저장된 시퀀스에서 같은 일을 하며, 라이브러리는 이를std::ranges::max함수로 표현한다. 그 사전 조건은 시퀀스가 비어 있지 않은 것이다.복습: 올바른 반복문 설계
문제 2이전 단원에서어떤 함수가
std::string객체에 저장된 백만 자짜리 텍스트에 모음이 몇 개 있는지 세고, 텍스트는 수정하지 않는다. 그 매개변수는 어떻게 선언하는 것이 좋은가?값 전달은 호출할 때마다 백만 개의 문자를 복사한다. 옳기는 하지만 아무것도 보태지 않는 비용을 치른다.복사하지는 않지만 필요 없이 텍스트의 수정을 허용하고, 리터럴도 임시 객체도 받아들이지 않는다. “복사하지 않는다”와 “수정을 허용한다”를 혼동한다.전달 방식은 인터페이스의 일부이다. 값 전달은 함수에 자기만의 복사본을 약속하며, 프로그램은 그 복사본을 만들어야 한다.상수 참조는 텍스트를 복사하지 않고, 수정할 권한도 없이 읽으며, 게다가 임시 객체도 받아들인다. 6단원에서는 이 선택이 규칙이 된다. 백만 개의 데이터로 된 벡터를 읽는 함수는 그것을const std::vector<int>&형태로 받으며, 불필요한 복사는 가장 흔한 비용 오류가 된다.복습: 매개변수와 유효 범위
문제 3이전 단원에서
++count문장은for (int i{0}; i < 6; ++i) { for (int j{0}; j < i; ++j) { ++count; } }코드에서 몇 번 실행되는가?문제 4이전 단원에서
std::println("{}", 0u - 1);문장은unsigned int타입이 32비트인 시스템에서 무엇을 출력하는가?부호 없는 타입이 음수를 받아들인다고 생각한다.뺄셈이 타입의 최솟값에서 멈춘다고 생각한다. 부호 없는 산술은 한 바퀴 돌 뿐 포화되지 않는다.있을 수 있는 경고를 오류와 혼동한다. 이 식은 유효하며 그 값은 정의되어 있다.문제 5C++처럼 번호를 매긴 개 원소의 시퀀스에서, 원소를 가리키는 인덱스 는 어느 것인가?
일상 언어에서처럼 1부터 번호를 매긴다. C++에서a[n]원소는 이미 시퀀스 밖에 있다.가장 흔한 하나 차이 오류이다. 인덱스를 개 세지만 원소는 개뿐이다.정직한 답이다. 6.1강에서 0부터 세는 이유와 함께 설명한다.문제 6
v벡터에는std::vector<int> v(3, 7);문장 뒤에 무엇이 들어 있는가?소괄호를 중괄호인 것처럼 읽는다.두 수의 역할을 뒤바꾼다.정직한 답이다. 6.2강에서 벡터와 그 생성 방법을 소개한다.문제 7
for (int m{1024}; m > 1; m /= 2) { ... }반복문은 몇 회 도는가?문제 8어떤 전화번호부에 이름 백만 개가 가나다순으로 실려 있다. 하나를 찾으려고 전화번호부를 한가운데에서 펼치고, 이름이 있을 수 없는 절반을 버린 뒤, 나머지 절반으로 같은 일을 되풀이한다. 최대 몇 번 펼쳐야 하는가?
이름을 하나씩 살피는 탐색의 횟수이다. 순서를 이용하지 않으면 평균적으로 전화번호부의 절반을 훑게 된다.백만의 제곱근이다. 부분을 절반으로 나누는 것과 여러 블록으로 나누는 것을 혼동한다.상용로그를 쓴다. 펼칠 때마다 버리는 것은 절반이지 10분의 9가 아니다.문제 9
std::vector<std::vector<int>> t{{1, 2, 3}, {4, 5, 6}};문장이 있을 때t[1][0]값과t.size()값은 얼마인가?첫 번째 인덱스를 열로 읽고, 행 대신 열을 센다.size()함수가 표의 모든 칸을 센다고 생각한다.정직한 답이다. 6.5강에서 표를 벡터의 벡터로 소개한다.문제 10어떤 프로그램이 한 집단의 이름은 한 벡터에, 그들의 성적은 다른 벡터에 저장하여, 성적
grades[i]원소가names[i]원소의 성적이 되게 한다. 성적 벡터만 정렬하면 어떻게 되는가?컴파일러에게 두 벡터는 서로 독립적이다. 관계는 프로그램을 쓴 사람의 머릿속에만 있다.이 알고리즘은 벡터 하나만 받으며, 다른 벡터의 존재를 알지 못한다.모든 접근이 유효하고 모든 결과가 정의되어 있다. 결함은 논리의 문제이며, 그래서 강좌의 두 새니타이저(메모리 오류 새니타이저 AddressSanitizer와 정의되지 않은 동작 새니타이저 UndefinedBehaviorSanitizer) 가운데 어느 것도 이를 알리지 않는다.
콘텐츠를 불러오는 중…