1. 함수 리턴값으로 벡터 이용하기
#include <iostream>
#include <vector>
#include <iomanip>
using namespace std;
bool is_prime(int n) {
if (n <= 1)
return false;
for (int i = 2; i < n; i++)
if (n % i == 0)
return false;
return true;
}
vector<int> primes(int low, int high) {
vector<int> vec;
for (int i = low; i <= high; i++) {
if (is_prime(i))
vec.push_back(i);
}
return vec;
}
void print(const vector<int> &v) {
for (int elem : v)
cout << setw(4) << elem;
cout << endl;
}
int main() {
int low, high;
cin >> low >> high;
vector<int> vec = primes(low, high); //[low, high] 모든 소수
print(vec);
return 0;
}
- print함수는 벡터의 원소를 수정하지 않음 → const, &를 걸어줘야 메모리 복사가 일어나지 않고 값도 변경되지 않음!
- vector<int>는 무거운 데이터 타입, 객체니까 가급적이면 메모리 복사를 피하기 위해 &를 걸어준다!
- &를 걸어줬으므로 const로 의도치 x 변하지 않게!
- is_prime 함수: 입력 파라미타 n이 소수이면 true, 그렇지 않으면 false 반환
- primes 함수: 입력 파라미타 low와 high 사이의 수 중에서 소수이면(if is_prime함수가 true이면) 1차원 벡터에 추가(push_back)하여 벡터를 반환 (리턴타입이 vector<int>)
- print 함수: 입력 파라미타인 1차원 벡터 v의 모든 원소들을 모니터에 출력, 이 때 4칸 띄워 우측정렬함
2. 2차원 벡터 생성하기
vector<vector<int>> v(2, vector<int>(3));
- vector<vector<'열'에 해당하는 데이터 타입>> 벡터이름(행의 개수, vector<데이터 타입>(열의 개수));
- 위의 예제같은 경우 2행 3열, 원소가 2 * 3 = 6개인 2차원 벡터! (matrix, 행렬, table...)
for(vector<int>& row : vec) {
for(int elem : row)
cout << elem;
cout << endl;
}
- 2차원 벡터의 '행'의 원소는 1차원 벡터!
- vector<int>& row : vec → row(행)의 각 원소가 1차원 벡터이므로 & 걸어주기!!
- 인덱싱 : [0][0], [0][1], [0][2], [1][0], [1][1], [1][2]
3. 다차원 벡터 선언 단순화하기
1. using 구문
using Matrix = vector<vector<double>>;
using: 'Matrix'라는 새로운 타입의 이름은 정의한다. 이를 이용하면 2차원 벡터 중에서도 double형의 원소들을 열에 가지는 벡터를 간편하게 선언할 수 있다.
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
using Matrix = vector<vector<double>>;
void print(const Matrix &m) {
for (const vector<double>& row : v)
for (double elem : row)
cout << setw(5) << elem;
cout << endl;
}
*)교수님께서는 별로 추천하지 않으셨다.
2. auto 만능키
- 컴파일러가 데이터 타입을 명확히 알 수 있을 때 타입을 auto로 치환할 수 있다.
#include <iostream>
#include <vector>
#include <iomanip>
using namespace std;
void print(vector<vector<int>> &v) {
//벡터 v는 각각의 elem(여기선 row)마다 1차원 벡터를 가짐
//그 elem(row)안에 다시 정수형 element가 들어있음
//vector<int>는 &로 메모리 복사 피하기, const로 수정 x
//int는 메모리 복사 하나 안하나 상관x, &안걸어줘도 됨
for (const auto &row : v) { //const vector<int> 와 동일
for (auto elem : row) //int와 동일
cout << setw(4) << elem;
cout << endl;
}
}
int main() {
vector<vector<int>> v(2, vector<int>(3)); //2x3 matrix, elem 3개인 1차원 벡터가 2개
// v[0][0] = 1; v[0][1] = 2; v[0][2] = 3;
// v[1][0] = 4; v[1][1] = 5; v[1][2] = 6;
vector<vector<int>> v{ {1, 2, 3},
{4, 5, 6} };
print(v);
//초기화 할 때 값의 type이 명확하니까 auto 가능
auto a = 10; //int와 동일
auto b = 'c'; //char과 동일
auto c = 10.0; //double고 동일
return 0;
}
4. 배열
1. 배열이란?
- 벡터(vector)와 배열(array)은 같은 형태와 기능은 유사하나, 배열은 built-in(벡터보다 더 오래된 데이터 타입, 컨테이너)이라 벡터처럼 헤더를 include할 필요가 없다.
- 배열은 객체가 아니기 때문에 멤버 데이터와 멤버 함수(메소드)도 없다.
- 특정 메모리와 그에 맞는 값에 즉각적으로 매핑된다.
- 배열의 크기는 프로그램 실행 중에 바뀔 수도 있고(dynamic array-동적 배열), 그렇지 않을 수도 있다.(static array-정적 배열)
2. 배열의 선언
int list[4] = {1, 2, 3, 4};
'배열의 원소 데이터 타입' '배열의 이름'[배열의 크기] = {원소1, 원소2, 원소3, ...};
*)이 때 int list[]로 크기를 비워놔도 컴파일러는 허용한다! → 배열의 원소들로 크기를 유추할 수 있기 때문이다.
3. 배열의 시작 주솟값
- 배열의 이름 == 배열의 첫 번째 인덱스[0]의 시작 주솟값 == 즉, 배열의 시작 주솟값 이다!!
- 포인터 변수로 임시로 타입이 변환된다. (배열의 이름이 포인터 변수 그 자체는 아님!)
- *연산자로 참조 시 [0]데이터를 가리킨다. ( ex) *list == list[0] )
4. 배열의 크기
위에서 언급했듯, 배열은 객체가 아니기 때문에 멤버 데이터, 메소드도 없다. 벡터의 메소드 중에서 자주 사용되는 .size()함수 역시 배열은 사용하지 못한다. 배열의 크기(size)가 배열의 멤버 데이터가 아니기 떄문이다. → 따라서 배열의 크기를 사용해야하는 경우, ①함수를 호출할 때 직접 입력 파라미터로 보내주는 방법과 ②sizeof(배열이름) 이라는 글로벌 함수(global function)을 사용하는 방법 등이 있다.
*) 방법 ①의 example
#include <iostream>
void print(int a[], int size) { //size: 배열의 크기
for (int i = 0; i < size; i++)
cout << a[i] << " ";
cout << endl;
}
int sum(int a[], int size) {
int result = 0;
for (int i = 0; i < size; i++)
result += a[i];
return result;
}
int main() {
int list[4] = { 2, 4, 6, 8 };
print(list, 4); //배열의 크기 4를 함수의 입력 파라미타로 보내주기
cout << sum(list, 4) << endl;
//list의 모든 원소를 0으로 만들기
for (int i = 0; i < 4; i++)
list[i] = 0;
print(list, 4);
cout << sum(list, 4) << endl;
}
- 함수의 입력 파라미타로 배열을 받을 때: int a[] (매개변수 a는 배열의 이름, 배열의 원소 타입은 int)
5. 포인터와 배열
- 앞서 배열의 이름은 (다른 연산자와 쓰이지 않을 때) 배열의 첫 번째 원소[0]를 가리키는 = 배열의 첫 번째 원소[0]의 시작 주솟값을 가지는 포인터 변수로 잠시 임시로 타입 변환된다고 했다.
- 배열의 첫 번째 원소[0]의 시작 주솟값: &a[0] 으로 표현
- int형 변수를 가리키는 포인터 p가 있을 때 → p = &a[0]; 과 p = a; 는 같은 표현!! (a는 원소가 int형인 배열)
- 배열의 시작, 끝 주솟값만 알면 배열의 모든 원소에 접근할 수 있다!
*)포인터 연산 & 주솟값의 연산
앞서 증감 연산자에 대해 알아볼 때, a++/++a 와 같이 전위, 후위에서 1을 증가시키는 연산자에 대해 알아보았다.
그런데, 이 연산자들이 포인터 변수에 대해서도 적용 가능하다! → p++/p-- 와 같은 연산이 가능하다는 뜻이다!
int형 원소들로 이루어진 배열 arr와, int형 변수를 가리키는 int*형 포인터 p가 있다고 하자.
p는 arr의 첫 번째 원소 arr[0]을 가리키고 있다. (p = arr; 혹은 p = &arr[0])
이 때 p++; (p = p + 1) 연산을 실행하면 p의 원래 주솟값에 16진수로 4만큼 증가하여 다음 원소(arr[1])를 가리키게 된다.
→ int는 4byte크기의 데이터 타입이기 때문에 메모리 상에서 변수가 차지하는 방 한 칸의 크기가 4이기 때문이다. 따라서 실제 16진수로 이루어진 주솟값은 4만큼 증가했을지라도, 배열에서는 그 다음(+1) 원소인 arr[1]을 가리키는 것이다! →여기서 우리는 arr[0]와 arr[1]이 배열 상에서는 1칸 거리를 가지지만, 메모리 공간 상에서는 4칸(즉, 4byte)만큼의 거리를 가지고 있다는 사실을 알 수 있다. (double이면 8byte! char이면 1byte!)
*) 4~5 example
#include <iostream>
#include <iomanip>
using namespace std;
int sum(int* list, int size) { //int* list == int list[]
// sizeof(list)쓰면 array크기 계산 가능
int result = 0;
for (int i = 0; i < size; i++)
result += list[i];
return result;
}
void print(int *begin, int *end) {
int *curr = begin; //curr: 배열 원소의 주솟값을 계속 업데이트하는 포인터
while (curr != end) {
cout << setw(4) << *curr;
curr++; //curr의 주솟값에 16진수로 4 증가 (int는 4byte니까 방 한칸 크기 4)
}
/* 위의 while문과 동일한 for문
for (int *curr = begin; curr != end; curr++) {
cout << setw(4) << *curr;
} */
cout << endl;
}
int main() {
int list[3] = { 10, 20, 30 };
//list를 함수로 보낼 때 크기도 함께 보내줘야함
cout << sum(list, 3) << endl;
//[0]의 주솟값+1 == [1]의 주솟값 (주솟값의 덧셈에서 1증가->16진수로 4증가)
cout << list << '\t' << *list << endl;
cout << (list + 1) << '\t' << *(list + 1) << endl;
cout << (list + 2) << '\t' << *(list + 2) << endl;
int *begin = list;
int *end = list + 3;
print(begin, end);
return 0;
}
- 함수의 입력 파라미타로 배열을 보낼 때, int arr[]와 int* arr는 동일한 표현이다!
- 위의 예제에서 사실 end포인터는 list[2]+1의 주솟값을 저장하고 있기 때문에 '배열의 마지막 원소의 주솟값'이라기 보다는 엄밀히 따지자면 '마지막 원소+1의 주솟값'이다.
*)sample codes: https://github.com/halterman/CppBook-SourceCode
'코딩 > 객체지향프로그래밍' 카테고리의 다른 글
[C++] 클래스와 객체 (1) (0) | 2020.08.02 |
---|---|
[C++] 포인터, 벡터, 배열 (3) (0) | 2020.07.30 |
[C++] 포인터, 벡터, 배열 (1) (0) | 2020.07.27 |
[C++] String, File - 문자열과 파일 (2) (0) | 2020.07.25 |
[C++] String, File - 문자열과 파일 (1) (0) | 2020.07.23 |