ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • C++ Primer CH3. Strings, Vectors and Arrays
    c++ 2024. 1. 21. 16:10

    3.1 Namespace using Declarations

    scope operator

    • ex)std::cin → std 네임스페이스에서 cin 네임을 사용하고 싶다.
    using namespace std;
    cin >> val;
    
    // using 안쓸 경우,
    std::cin >> val;
    
    • header파일 내에는 using 선언자(declaration) 사용하면 안됨
    • 해당 header 파일을 사용하는 모든 파일 내에서 해당 네임스페이스가 사용되므로 예기치 못한 이름 충돌 발생 가능성 존재.

    3.2 Library string Type

    string

    • variable-length sequence of characters

    초기화 방법들

    string s1; // default initialization, empty string
    string s2 = s1; // s2 is a copy of s1
    string s3 = "hiya"; // s3 is a copy of the string literal, "hiya"
    string s4(10, 'c'); // s4 is "cccccccccc"
    
    • string의 맨 끝에는 null character가 있을까?
    • 없다. 배열에 문자열을 저장할 때만 컴파일러가 추가해준다.
    • = 연산자의 역할은?
    • right-hand side쪽 initializer를 복사해서 left-hand variable에 할당한다.

    3.2.2 Operations on strings

    string operations

    os << s
    is >> s
    getline(is, s)
    s.empty()
    s.size()
    s[n]
    s1 + s2
    s1 = s2
    s1 == s2
    s1 != s2
    <, <=, >, >=
    

     

    whitespace 기준으로 끊어서 입력받는 cin

    string s;
    cin >> s // Hello World
    cout << s << endl; // Hello
    return 0;
    

     

    getline 사용해서 전체 라인 읽기

    • whitespace 무시하기 싫을 때 사용
    • 개행문자 추가 안됨 → endl 추가해서 개행 + buffer flushing
    int main()
    {
    	string line;
    	while (getline(cin, line)) {
    		cout << line << endl;
    	}
    	return 0;
    }
    

     

    string이 비어있는지 체크

    • string.empty()
    • return type: bool(true or false)

    string의 크기 반환

    • string.size()
    • type: size_type
    • size_type은 companion types중 하나이다.string::size_type
    • -> unsigned type이다. 
    • -> machine-independent하기 위해서 도입
    • int변수(signed)와 크기(unsigned)를 연산할 때 주의해야 하는 이유
    • -> size() 반환타입은 unsigned이고, int-unsigned 중 unsigned가 더 우선순위 높으므로 int→unsigned로 타입 캐스팅 되면서, 만약 int가 음수값이었다면 매우 큰 값으로 해석되기 때문

    Literal과 string 합치기

      • 연산으로 합칠 때, + 양쪽 operand중 적어도 한쪽은 string 변수이어야 한다.
    string s4 = s1 + ", "; // OK
    string s5 = "hello" + ", "; // ERROR, + 양쪽 다 literal 이므로.
    string s6 = s1 + ", " + "world"; // OK, (s1 + ", ") + "world" 이런식으로 처리된다.
    string s7 = "hello" + ", " + s2; // ERROR, ("hello" + ", ") 여기서 에러 발생
    

    3.2.3 Dealing with the Characters in a string

    string을 이루는 모든 character을 다루고 싶다.

    Range-Based for

    string s("Hello World!!!");
    decltype(s.size()) punct_cnt = 0;
    
    for (auto c : s) {
    	if (ispunct(c)) {
    		++punct_cnt;
    	}
    }
    cout << punct_cnt
    		 << " punctuation characters in " << s << endl;
    
    • ispunct()는 어떤 헤더파일에 있는가?
    • -> cctype.h
    • isalnum, isalpha 등 많은 함수들이 있다.

     

     

    string 내의 character을 바꾸려고 Range-Based for 사용하기

    ex) string 내의 모든 character를 대문자로 만들고 싶다.

    string s("Hello World!!!");
    for (auto &c : s)
    	c = toupper(c);
    cout << s << endl;
    
    • 여기서 &c 대신 c를 사용하면?
    • -> c: s의 문자가 c로 "복사"되는 것이기 때문에 c를 대문자로 만들어줘도 문자열에 반영되지 않는다.
    • -> &c: 개별 문자를 참조해서 그 문자 자체를 바꿔버린다.

    string 내의 일부 문자만 처리하고 싶다면?

    • subscript 혹은 iterator 사용
    • subscript
      • subscript operator: [index]
        • index type: string_size(unsigned)
      • usage: s[0]
      • return type: char type reference
      string s("some string");
      if (!s.empty())
      	s[0] = toupper(s[0]);
      
      for (decltype(s.size()) index = 0; index != s.size() && !isspace(s[index]); ++index)
      	s[index] = toupper(s[index])
      
      • for문의 condition 영역에서 &&가 사용되었다. 유의할 점은?
      • -> &&의 왼쪽 operand가 true일 때만 오른쪽 operand가 평가된다.
      • -> 따라서 문자열을 다 돌았는지 평가한 후 아직 다 돌지 않았을 때, 해당 위치의 문자가 스페이스인지 확인해주어야 index가 범위 내에 있는 경우에만 s[index]가 실행되도록 할 수 있다.
      • for문에서 decltype()을 사용하면 좋은 점은?
      • -> string.size()의 타입은 unsigned라서 무조건 0 이상이기 때문에 index가 0 미만이 되는걸 방지할 수 있다.
      • -> 따라서 index < size()인지만 체크해주면 돼서 편하다.
      const string s = "Hello";
      for (auto &c : s)
      
      • 위 코드에서 c의 타입은?
      • -> const char

    3.3 Library vector Type

    vector

    • 타입이 같은 object들의 집합
    • container에 속함
    • class template이다.
    • 벡터는 타입이 아니라 템플릿이다.
    • vector<int>, vector<vector<int>>, …
    • vector<>에 reference를 넣을 수 있을까?
    • -> 넣을 수 없다. reference는 object가 아니기 때문이다.

    vector 초기화하는 법

    vector<int> vec; // default 초기화, 가장 흔하게 쓰임. 런타임시에 요소 추가됨
    vector<int> vec2(vec); // vec의 모든 요소들을 vec2에 복사함.
    vector<int> vec3 = vec; // vec의 모든 요소들을 vec3에 복사함.
    vector<string> vec4 = vec; // error: 요소의 타입은 같아야 함. 
    
    // 리스트로 초기값 넣어주기(list initializing)
    vector<string> vec = {"one", "two", "three"};
    vector<string> vec{"one", "two", "three"};
    vector<string> vec(10, "HI");
    
    // 크기만 정해주고 싶을 때(Value Initialization)
    vector<int> vec(10); // 요소 10개, 모두 0으로 초기화됨
    vector<string> vec(10); // 요소 10개, 모두 ""로 초기화됨.
    
    • 크기만 정해주지 못할 경우
      • vector의 요소 타입이 특정 클래스인데 default 초기화를 지원하지 않는 경우 무조건 초기자를 공급해줘야 함.

    예외상황

    vector<string> vec{10}; // list initialization 하려는데 요소값이 10이고 요구타입이 string이라서
    // 10은 요소개 10개라는걸로 컴파일러가 해석한다. 
    vector<string> vec{10, "hi"}; // 마찬가지로 list initialization 안되서 "hi"가 10개인걸로 바뀜
    
    • vector의 크기를 지정해주는게 좋나?
    • -> 보통 vector 크기 지정하는건 불필요하고 성능면에서도 안좋다. 
      • 예외: 모든 요소가 실제로 같은 값일 때

    vector operations

    vector<int> vec{1, 2, 3};
    
    vec.push_back(t)
    vec.empty()
    vec.size()
    v[n]
    v1 = v2
    v1 = {a, b, c,...}
    v1 == v2 // 크기가 같고 각 인덱스별 요소가 모두 같은지,
    v1 != v2
    <, <=, >, >=
    for (auto &i : vec) {}
    

     

    subscripting은 요소를 넣을 수 없다

    vector<int> vec;
    for (decltype(vec.size()) idx = 0; idx != 10; ++idx)
    	vec[idx] = idx; // error: vec은 요소가 없다.
    

    3.4 Introducing Iterators

    사용법

    string s("some string");
    if (s.begin() != s.end()) { // s의 시작위치 = s.begin(), s의 끝 + 1 = s.end()
    	auto it = s.begin();
    	*it = toupper(*it);
    }
    
    *iter
    iter->mem
    ++iter
    --iter
    iter1 == iter2
    iter1 != iter2
    
    • for문에서 ≤ 10 안쓰고 ≠ 10 쓰는 이유?
    • -> c++ 개발자들은 iterator로 순회하는게 익숙하고, it ≠ x.end() 이렇게 쓰는게 익숙하다
    • -> ≠나 == operator는 모든 라이브러리 컨테이너에서 정의되어 있으므로 습관화하면 실수 줄일 수 있다.

    const vector<int>의 begin() 타입은 vector<int>::const_iterator다.

    vector<int>의 begin() 타입은 vector<int>::iterator다.

    const 안 붙은 vector 등도 const_iterator 만들고 싶을 때, cbegin, cend를 사용한다.

     

    iterator 멤버에 접근하는 법

    // 1. dereference + access
    (*it).empty() // OK
    *it.empty() // ERROR, access 먼저 일어남
    
    // 2. arrow operator
    it->empty() // OK
    

     

    iterator를 사용하는 루프에서 컨테이너에 요소 추가해서는 안된다.(크기가 바뀌기 때문)

     

    iter + n
    iter - n
    iter += n
    iter -= n
    iter1 - iter2
    >, >=, <, <=
    

     

    3.5 Arrays

    얼마나 많은 요소를 사용할건지 알 수 없으면 vector를 사용해라

     

    배열 초기화

    unsigned cnt = 42;
    constexpr unsigned sz = 42;
    
    int arr[10]; // OK, int 10개 배열
    int *parr[sz]; // OK, 42개 int*포인터
    string bad[cnt]; // cnt가 const 아님
    string strs[get_size()]; // get_size()가 constexpr이면 OK, 그 외엔 error
    

     

    명시적 초기화

    const unsigned sz = 3;
    int a1[sz] = {0, 1, 2};
    int a2[] = {0, 1, 2};
    int a3[5] = {0, 1, 2};
    string a4[3] = {"hi", "bye"};
    int a5[2] = {0, 1, 2}; // error
    

     

    char 배열은 특별하다

    char a1[] = {'c', '+', '+'}; // 크기3
    char a2[] = {'c', '+', '+', '\\0'}; // 크기4
    char a3[] = "C++"; // null terminator가 자동으로 '\\0' 붙여줌. 크기4
    const char a4[6] = "Daniel"; // error: a4 크기 6이고 이미 꽉차서 '\\0' 못붙힘
    

     

    배열은 복사나 할당 불가

    int a[] = {0, 1, 2};
    int a2[] = a; // error
    a2 = a; // error
    

     

    배열 포인터, 배열 참조자

    #include <iostream>
    using namespace std;
    
    int main()
    {
        int arr[10] = {1, 2, 3, 4, 5};
        int (&arrRef)[10] = arr; // arrRef는 arr이라는 object의 참조자이다.
        int (*Parr)[10] = &arr; // Parr은 arr의 주소값을 가리키는 포인터다.
        arrRef[0] = 10;
    
        cout << arr[0] << endl;
    
        *Parr[0] = 13;
        cout << *Parr[0] << endl;
        cout << arrRef[0] << endl;
    
        int* ptrs[10];
        int* (&arry)[10] = ptrs; // arry는 int*를 요소로 가지는 ptrs 배열의 참조자이다.
    }
    

     

    배열의 요소에 접근하기

    • 인덱스 타입으로 size_t 해야 함
      • machine-specific한 충분히 큰 unsigned type

    checking subscript values

    배열 범위를 넘는 인덱스를 조회하는 경우가 가장 많이 발생하는 보안 문제다.

    컴파일도 잘되고 실행도 잘돼서 치명적인 에러 생길 수 있다.

    3.5.3 Pointers and Arrays

    배열은 보통 컴파일러가 포인터로 바꿔버린다.

    string num[] = {"hell", "lo", "world"};
    string *p = &nums[0]; // 첫번째 "hello"를 가리키는 포인터
    
    string*p2 = nums; // p2 = &nums[0]과 동일
    

     

    포인터는 이터레이터다

    int arr[] = {1, 2, 3, 4};
    int *p = arr; // p는 arr의 첫번째 요소 가리킴
    ++p; // p -> arr[1] 가리킴
    

     

    포인터 대수 연산

    배열의 인덱스 타입은 unsigned가 아니라서 음수도 에러가 안뜬다.

    3.6 multi dimensional array

    size_t cnt = 0;
    int ia[rowCnt][colCnt];
    for (auto &row : ia)
    	for (auto & val : row) {
    		val = cnt;
    		++cnt;
     }
    
    for (auto row : ia)
    	for (auto val: row) {} // error:  컴파일 안됨
    
    // 둘 간의 차이
    // auto &row 결과는 4개의 int를 가지는 배열의 참조자이다.
    // auto row의 결과는, row가 참조자가 아니라서 컴파일러가 row를 array의 첫번째 원소를 가리키는 포인터
    // 로 바꿔버린다. 따라서 내부 for문 돌지 못한다.
    
    int *arr[4];
    int (*arr)[4];
    
    • 둘 간의 비교
    • -> 두번재는 4개의 int를 가지는 배열을 가리키는 포인터 arr
    • -> 첫번째는 4개의 int*요소를 가지는 배열 arr

    'c++' 카테고리의 다른 글

    C++ Primer CH7. Classes  (0) 2024.01.21
    C++ Primer CH6. Functions  (0) 2024.01.21
    C++ Primer CH4. Expressions  (0) 2024.01.21
    C++ Primer CH2. Variables and Basic Types  (0) 2024.01.21
    C++ Primer CH1. Getting Started  (0) 2024.01.21
Designed by Tistory.