[C++ Primer Plus] Chapter 4. 복합 데이터형
Table of Contents
C++ 기초 플러스 책을 읽고 공부한 노트입니다.
배열 #
- 배열을 선언할 때 원소의 개수는 다음 중에 하나여야 한다.
- 값
- 정수 상수
- 상수 수식
int values1[40]; // 1
const int length = 40;
int values2[length]; // 2
int values3[10 * sizeof(int)]; // 3
- 생성과 동시에 초기화할 수 있다. 반면, 초기화를 나중에 할 수는 없다.
int arr[3] = {1, 2, 3};
- 초기화할 때 처음 원소를 명시적으로 하면 나머지는 알아서 0이 된다.
int arr[3] = {0};
[]
속을 비우면 초기화 개수 대로 배열이 만들어진다.- 배열의 원소 개수는
sizeof(arr) / sizeof(int)
로 구할 수 있다.
int arr[] = {1, 2, 3}; // 3개의 int형 데이터를 가진 배열로 만들어진다.
- 배열 초기화 시
=
가 없어도 된다. - 중괄호 공백으로
0
으로 다 초기화할 수 있다. - 초기화 시 narrowing은 안 된다.
int arr[] {1, 2, 3, 4};
int arr[] {};
int arr[] {1, 2, 3.3333, 4}; // (X)
문자열 #
- 모든 문자열의 마지막 문자는 널 문자(
\0
)여야 한다. - 문자열 상수로 배열을 초기화할 때 널 문자까지 고려해야 한다.
[]
속을 배우면 알아서 널 문자까지 고려 해 준다.- 배열이 문자열 상수보다 더 크면 널 문자로 계속 채운다.
char arr[] = "Hello"; // 6개의 char형 데이터를 가진 배열로 만들어진다.
‘S’
는 문자이다."S"
는 문자열이며 S문자와 널 문자가 합쳐진 두 개의 문자로 이루어진 문자열이다.- 따라서 아래와 같은 것은 안 된다.
char keyword = "S"; (X)
- White space(빈칸, 탭, 캐리지리턴)로 분리된 두 개의 문자열 상수는 하나의 문자열 상수로 결합된다.
cout << "Hello " "My friend"; // "Hello My friend"
<cstring>
헤더 파일에 있는strlen()
함수는 배열의 전체 크기가 아니라 배열에 저장된 문자열의 크기를 리턴한다. 널 문자는 제외한다.
문자열 읽기 #
cin
은 White Space를 문자열의 끝으로 간주한다. 그래서 중간에 White Space가 있는 문자열을 입력하면 White Space 전까지만 저장하고 그 이후 문자들은 입력 큐에 남겨 놓는다. 그러면 다음cin
에서는 사용자 입력을 받지 않고 입력 큐에 남아있던 문자들을 저장한다.
- 전체 한 줄의 문자열을 저장하기 위해서는…
cin.getline(배열이름, 배열크기)
getline
은 매개변수로 입력받은 (배열크기 – 1)크기를 넘어서거나, 개행 문자를 만나면 개행 문자를 널 문자로 교체하고 입력한 후 읽기를 종료한다.getline
이 지정된 배열 크기를 넘어서는 문자를 입력받으면 나머지는 입력 큐에 남겨두고,failbit
를 설정한다.
cin.get(배열이름, 배열크기)
get
은 개행 문자를 입력 큐에 남겨둔다. 다음 입력이 개행 문자를 입력하는 것을 방지하기 위해cin.get()
을 사용해 문자 하나를 읽어버릴 수 있다. 아니면cin.get(배열이름, 배열크기).get()
이렇게 할 수도 있다.get
이 빈 행을 읽으면failbit
라는 것이 설정되어서 추가적은 입력을 막는다. 이것을 복원하려면cin.clear()
를 해주어야 한다.
함수 | 입력 | 버퍼남김 | 무엇을 읽으면 failbit? |
---|---|---|---|
cin |
White Space 이전까지 | 개행 문자 | |
cin.getline(배열이름, 크기) |
개행까지 | X (개행은 널로 대체) | 초과 크기의 문자열 |
cin.get(배열이름, 크기) |
개행까지 | 개행 문자 | 빈 행 |
if (cin.fail() == 1)
{
cin.clear(); // 내부 상태 플러그 초기화
cin.ignore(INT_MAX, '\n'); // 해당 길이 만큼 or 개행 문자까지 읽어서 입력 버퍼를 비운다.
}
string 클래스 #
- 리스트 초기화가 가능하다.
char arr[] = {"Hello"};
string str = {"Hello"};
문자배열 | string | |
---|---|---|
크기 | 선언만 하면 크기가 제각각이다. 이후에 cin 으로 입력을 저장하면 지정된 크기 만큼만 저장된다. |
선언만 하면 크기가 0 이다. 이후에 cin 으로 입력을 저장해도 자동으로 크기를 조절해 문자열을 넣는다. |
대입과 추가 | C라이브러리 <cstring> 를 사용해서 strcpy(arr1, arr2) 로 복사한다. strcat(arr1, arr2) 로 덧붙인다. |
= 로 대입한다. + , += 으로 덧붙인다. |
길이구하기 | 길이는 strlen(arr) 로 구할 수 있다. |
길이는 str.size() 로 구할 수 있다. |
// 문자배열
cin.getline(arr, arrSize);
// string
// <istream>은 string형을 인식하지 못한다.
getline(cin, str);
다른 형태의 문자열 상수 #
wchar_t
,char16_t
,char32_t
L
,u
,U
접두사를 사용해서 문자열 상수로 초기화할 수 있다.
wchar_t arr[] = L"Hello";
- 접두사
R
을 붙여서 raw 문자열을 사용할 수도 있다.
구조체 #
- 여러 종류의 데이터를 모아서 하나의 단위로 묶어서 저장할 수 있다.
- 함수도 멤버로 가질 수 있다.
struct Student
{
char name[20];
int age;
};
Student s1 = { "KimKim", 1 };
Student s2 {}; // =를 생략할 수도 있다, 0으로 모두 초기화.
cout << s1.age;
struct // 데이터형 이름 없음. 구조체의 정의와 동시에 변수생성.
{
char name[20];
int age;
} s1, s2;
- 구조체 배열
Student sArr[2] = { { "KimKim", 1 }, { "SoSo", 2 } };
공용체 #
- 여러 종류의 데이터를 모아서 하나의 단위로 묶었지만, 하나의 데이터만 보관할 수 있다.
- 공용체의 크기 = 제일 큰 멤버의 크기
union OneForAll
{
int Num;
char Name[20];
};
열거체 #
0
부터 순서대로 대입되며, 명시적으로 지정하면 기본값은 무시된다.
enum Color { Red, Orange, Yellow, Blue, Green, Purple };
int
형으로 자동으로 승급될 수 있다.
int number = 3 + Red;
- 반대로
int
형이 자동으로enum
형으로 되진 않는다.
Color c = Color (3);
- 대입연산자만 사용할 수 있고, 산술 연산자는 안 된다.
++c; // (X)
- 주로 기호 상수들을 정의하는 용도로 사용되므로 이름을 생략할 수도 있다.
enum { Red, Orange, Yellow, Blue, Green, Purple };
- 어떤 정수값이 열거체 값 범위 안에 들어있으면, 그 값이 열거자 값이 아니더라도, 데이터형 변환을 통해서 열거체 변수에 대입할 수 있다.
포인터 #
int *
이든double *
이든 그 크기는 같다. (컴퓨터 시스템에 따라 다름)
int number = 7;
int * pointer = &number;
// *pointer == number == number의 값
// pointer == &number == number의 주소값
int * ptr, num; // ptr은 포인터형으로 num은 int형으로 생성된다.
- 포인터에 직접 주소를 대입할 수 있다. 데이터형을 반드시 명시해야 한다.
int * number = (int *) 0xB8000000;
new연산자로 런타임에 메모리 할당 (C의 malloc 대체) #
int * ptr = new int;
- 여기서
ptr
이 가리키고 있는 메모리의 이름(number
같은 변수 이름)이 없다. 그냥 메모리 블록을 가리키고 있다. 그래서ptr
을 통해서만 메모리 접근이 가능하다. - 컴퓨터 메모리가 부족해서
new
의 메모리 할당 요청을 허용할 수 없으면new
는0
을 리턴한다. 이렇게 값이0
인 포인터는 널 포인터라고 부른다. 널 포인터는 무언가 일이 잘못되었다는 것을 나타낼 때 사용된다.
delete 연산자로 메모리 해제 #
int * ptr = new int;
delete ptr;
- 메모리 누수를 방지하기 위해 다 쓴 메모리는
delete
연산자를 사용해 메모리를 해제해 주어야한다. delete
로 해제된 메모리를 또 다시delete
로 해제하면 안 된다. 보통의 변수로 대입한 메모리도delete
로 해제할 수 없다.- 널 포인터를
delete
하면 아무일도 일어나지 않는다 (안전하다)
new를 사용해 생성한 동적 배열 (동적 바인딩) #
int * ptr = new int [10];
delete [] ptr;
*ptr
이나ptr[0]
으로 배열의 첫번째 원소에 접근 가능 하다.ptr + 1
을 하면ptr
이 가리키는 데이터형의 크기만큼 한 칸 뒤로 가서,ptr[0]
은 두번째 원소인ptr[1]
을 가리키게 된다.
포인터와 배열 #
int numbers[3] = {1, 2, 3};
int * ptr = numbers;
-
numbers
==&numbers[0]
numbers
배열의 첫 번째 원소의 주소값
-
numbers
!=&numbers
&numbers
는 배열 전체의 주소값이다.- 따라서
numbers + 1
은 다음 원소의 주소값이지만, &numbers + 1
을 하면 전체 배열을 넘어간 다음의 주소값이다.
-
*numbers
==numbers[0]
numbers
배열의 첫 번째 원소의 값
-
*(numbers + 1)
==numbers[1]
numbers
배열의 두 번째 원소의 값
- 여기서
numbers
대신ptr
을 넣어도 똑같다. - 다른점:
- (1)
ptr
은 값을 변경할 수 있지만 배열 이름인numbers
는 값을 변경할 수 없다. - (2)
sizeof(numbers)
는 배열 전체의 크기이지만sizeof(ptr)
은 포인터의 크기이다.
- (1)
- 포인터 배열과 배열 포인터
short * ptr [20]; // short* 형 포인터가 20개 있는 배열
short (*ptr)[20]; // short 형 자료가 20개 있는 배열을 가리키는 포인터
포인터와 문자열 #
- 배열의 이름, 포인터, 문자열 상수 모두 동등하게 첫번째 문자열의 주소를 나타낸다.
char arr[10] = "beautiful";
cout << arr;
char * ptr = "beautiful";
cout << ptr;
cout << "beautiful"
- 문자열 상수나 초기화되지 않은 포인터를 문자열 입력에 절대 사용하지 않아야 한다. 포인터의 경우, 초기회되지 않았다면, 입력된 문자가 어디에 저장될지 알 수 없어진다.
const char * ptr1 = "notgood";
cin >> ptr1;
char * ptr2;
cin >> ptr2 //(X)
strncpy(arr1, arr2, 최대 문자 수);
- 이것은 최대 문자수가 다 안 담기면 널 문자를 추가하지 않는다. 따라서 반드시 수기로 추가해야 한다.
new를 사용한 동적 구조체 생성 #
연산자 | 형식 |
---|---|
도트 멤버 연산자 | [구조체의 이름.멤버] , [(*포인터).멤버] |
화살표 멤버 연산자 | [구조체를 지시하는 포인터->멤버] |
struct Flower
{
char name[20];
double price;
};
Flower * f = new Flower;
cin.get(f->name, 20);
cin >> f->price;
cin >> (*f).price;
delete f;
데이터 저장을 위한 메모리 공간의 종류 #
- 자동 공간(automatic)
- 변수들이 자신이 정의되어있는 함수가 호출되는 순간에 생겨나서, 함수가 종료되는 시점까지만 존재한다. (블록 안에서만 유효하다)
- 스택에 저장된다. 따라서 순차적으로 저장되고 역순으로 해제된다.
- 정적 공간(static)
- 프로그램이 실행되는 동안에 지속적으로 존재하는 공간이다.
- 함수 외부에서 변수를 정의하거나
static
키워드로 정의하면 된다.
static double fee = 56.5;
- 동적 공간(힙)(dynamic)
new
와delete
을 사용해서 동적으로 메모리를 할당하고 해제한다.- 힙에 저장된다.
배열의 대안 #
vector
템플릿 클래스string
클래스처럼 자동으로 런타임에 메모리가 할당/해제 된다. (힙)
vector<int> vec(10);
// 10개 원소를 가진 int형 배열.
// 10자리에 변수가능.
// 자동으로 0으로 모두 초기화 됨.
array
템플릿 클래스- 크기가 고정되어 크기를 런타임에 바꿀 수 없다. (스택)
array<int, 10> arr; // 10자리에 변수 불가능.