
main()
C++에서 명령들은 함수라는 단위로 그룹화 된다, 이때 함수란 순차적으로 실행되는 명령들의 집합이다
모든 C++ 프로그램에는 특수 함수인 main()이 반드시 존재해야 한다
프로그램이 실행되면 main()의 명령들이 순차적으로 실행되고 종료된다
프로그래밍에서 이러한 함수, 변수 등의 이름을 식별자 (identifier)라고 한다
가장 기본적인 출력 프로그램으로 자세하게 파악해보자
#include <iostream> //전처리 지시문
int main() //main()
{
std::cout << "Hello World!"; //명령
return 0;
}
#include라는 전처리 지시문으로 iostream이라는 라이브러리의 내용을 포함시킨다 (iostream이란 console에서 text를 읽고 쓸 수 있게 해주는 C++ 표준 library)
std::cout << 으로 원하는 정보를 console에 출력시킨다
return 0;으로 해당 함수의 종료 (main()이기 때문에 프로그램의 종료)를 나타낸다
거의 모든 명령의 끝은 항상 ;으로 끝나야 하며 이를 지키지 않을 시 syntax error를 발생시킨다
주석
주석이란 프로그래머가 읽을 수 있는 메모이며 소스코드에 직접 삽입된다, 주석은 컴파일러에 의해 무시되어 프로그램에 영향을 주지 않는다
//a is integer and value is 100
int a = 100;
이런식으로 코드에 대한 설명과 메모를 하는 용도로 사용한다 (@TODO, @NOTE, @Fixme...)
이때 긴 코드에서 맨 오른쪽에 주석을 달면 읽기 힘들 수 있으니 코드 위에 주석을 작성하는것이 보기 좋다
/*a is integer and
value is 100*/
이렇게 /* */로 여러줄 주석도 가능하다, 이때 다중 줄 주석 내부에 또 다른 다중 줄 주석을 넣으면 주석으로 인식하지 않아 에러가 발생한다
단축키로는 ctrl + k + c로 주석, ctrl + k + u로 주석해제가 가능하다

주석은 보통 3가지의 목적을 달성하기 위해 사용한다
라이브러리, 함수가 무엇을 하는 함수인지 작성
라이브러리, 함수가 어떤 목적을 어떻게 달성하는지 작성
해당 코드가 왜 사용되었는지 작성
굉장히 좋지 않은 주석은 다음과 같다
//Hp is zero
Hp = 0;
가볍게 내용만 봐도 바로 아는 내용을 주석으로 적을 필요는 없다
//If Hp = 0, Player Die
Hp = 0;
이렇게 왜 코드가 작성되었는지 의미를 나타내는 주석이 좋은 주석이라 할 수 있다
또한 다른 방법이 존재하지만 왜 이러한 방법으로 해당 코드를 작성했는지 설명하는 용도로 사용도 가능하다
// We decided to use a linked list instead of an array because
// arrays do insertion too slowly.
주석 처리를 통해 디버깅도 가능하며(코드의 일부를 비활성화하며 문제의 원인인 코드를 찾을 수 있다) 임시 작업을 우선 컴파일 시켜야 할 때 사용하기도 굉장히 좋다
주석을 작성했으면 해당 코드가 변경될 때 주석도 같이 refresh 해줘야 한다, 이를 하지 않으면 거짓된 내용의 주석이 되기 때문에 오히려 작성하지 않는것보다 못하는 행위가 된다
Object, Variable
프로그램을 실행하면 운영체제가 프로그램을 RAM에 load하며 이때 하드코딩된 데이터 ex) Hello World와 같은 텍스트가 이 시점에 load된다
운영체제는 프로그램이 실행되는 동안 사용할 추가 RAM을 예약한다, 이 메모리는 사용자의 input value를 저장하거나 file, network에서 읽은 data를 저장하고나 실행 도중 계산된 값을 저장하는 용도이며 이러한 data들을 나중에 다시 사용하기 위한 용도이다
C++에서는 직접 메모리 엑세스는 권장되지 않는 엑세스 방식이다, Object를 통한 간접 메모리 엑세스를 권장한다
(실제 메모리 n번에 저장된 값을 가져와라 -> 어떤 객체에 저장된 값을 가져와라를 권장한다는 의미)
Object는 값을 보관할 수 있는 저장 영역을 나타낸다 (일부 할당된 메모리 공간)
이러한 Object에 이름을 붙힌게 바로 변수 (Variable)이다
결론적으로 Object는 메모리에 값을 저장하는데 사용되며 변수는 이름이 있는 Object를 의미한다
변수 선언
변수를 사용하기 위해서는 우선 선언을 해야한다
자료형 변수명;
int a; //int타입의 a라는 식별자를 가진 변수 선언 (Object의 식별자가 a이며 타입은 int라는 의미와 같다)
컴파일 타임에서 컴파일러는 선언된 자료형의 변수를 기록하게 되며 해당 시점부터 이 변수명을 사용할 떄마다 컴파일러는 이 변수명을 참조한다는 것을 알게 된다
컴파일러는 변수선언에 있어서 필요한 메모리 양, 어떤 저장 공간에 배치 (Ram, Register), 언제 생성되고 파괴될지를 결정한다
이후 런타임 (프로그램이 메모리에 로드되어 실행될 때)에 값을 실제로 저장할 수 있는 위치인 Ram이나 Register를 받고 저장공간을 예약한다 이를 allocation (할당)이라고 한다, 메모리가 할당되면 Object가 생성된다
따라서 a라는 변수가 메모리상 160의 위치에 allocation되고 우리가 a를 사용하게 되면 메모리상 160의 위치 데이터에 엑세스 하게 되는 것이다
변수의 자료형은 컴파일 타임에 알려져야 하고 프로그램을 다시 컴파일 하지 않는 이상 자료형이 변경될 수 없다
(컴파일 타임에 할당할 메모리의 크기를 알려줘야 하기 때문)
int a;
int b;
int a, b;
이 두개는 같은 의미이다
하지만
int a, int b;
이런 선언 방식은 불가능하다
또한
int a; int b; //가능하지만 추천하지 않음
컴파일에 문제는 없지만 보통 단일 문장에서 여러 변수를 선언하는건 지향하지 않는다
변수 값 할당, 초기화
변수가 선언되면 초기화와 할당을 통해 값을 줄 수 있다
int Height = 180; //복사 초기화, C방식 초기화
int Height;
Height = 180; //할당
Height = 160; //할당
이렇게 = 연산자를 이용하여 오른쪽의 값을 왼쪽의 변수로 복사 할당(copy assingment) 할 수 있다
변수의 값은 언제든 변경이 가능하다
이렇게 값이 들어간 변수는 std::cout << 을 통해 console에 출력이 가능하다
int Height{ 160 };
int Height(160); //직접 초기화
이런 다양한 방식으로 초기화도 가능하다
복사 초기화는 복잡한 객체에 대해서 임시객체가 생성된 후 복사 생성자가 호출되어 복사가 필요하여 효율이 떨어져 선호도가 낮았지만 현재 C++17에서는 많이 개선되었다
직접 초기화는 복잡한 객체에 대해서 효율적인 초기화를 위해 도입, 하지만 변수와 함수의 구분이 힘들어져 선호도가 낮아짐
중괄호 초기화 (리스트 초기화)는 위의 단점들을 해소하여 초기화 수행을 명확하게 전달할 수 있는 방법이다 (변수, 함수 구분이 힘들지 않고 복사 초기화인지 복사 할당인지 구분이 힘들지 않다)
또한 축소 변환이 발생하는 값으로 초기화 할 시 compiler error를 발생시켜 명확한 초기화가 가능하다 (데이터 손실 방지 가능)
(전부 다 복사생성자가 있다면 복사생성자가 호출됨)
int Height {5.5f};

이때
int Height {};
int Height {0};
이 둘은 0으로 초기화 하는 방법이지만 사용처는 다르게 쓰는게 좋다
실제로 초기값 0을 사용하려면 {0}, 객채의 값이 임시값이고 추후에 할당된다면 {}로 사용해도 된다
변수는 선언했으면 초기화를 바로 하는것을 권장한다
인스턴스화는 바로 변수가 선언되고 초기화가 되었음을 의미한다, 이렇게 인스턴스화 된 객체를 인스턴스라고 부른다
또한 ,로 여러 변수를 선언했다면 각각 초기화가 가능하다
int a = 5, b = 10;
int a(7), b(8);
int a{9}, b{10};
int a{}, b{};
하지만
int a, b = 5;
이렇게 초기화 한다면 a는 초기화 되지 않고 b만 5로 초기화 된다
초기화를 하고난 변수를 사용하지 않는다면 컴파일러에서 warning을 띄우게 되는데 이를 막기 위해서 C++17에서는 다음과 같은 attribute를 도입하였다
[[maybe_unused]]int a {100};
이렇게 하면 컴파일러는 a는 초기화 후 사용되지 않아도 warning을 뱉지 않게된다
또한 해당 변수를 최적화 하여 성능상 좋아질 수 있다
iostream
iostream은 입출력 라이브러리이며 C++ Standard Library의 일부이다, 이 Library를 통해 우리는 키보드에서 input을 받고 console에 output을 출력할 수 있다
io는 input, output을 의미한다
#include <iostream>
전처리 지시문을 통해 library를 include하여 사용한다
std::cout
std::cout << "Test";
std::cout을 통해 데이터를 console로 보낸 후 text로 출력이 가능해진다
여기서 데이터는 text뿐 아니라 숫자도 가능하며 변수의 값도 가능하다
std::cout << 5;
int a {100};
std::cout << a;
여러개의 데이터를 한 줄에 넣고 싶다면 <<를 여러번 사용하여 연결이 가능하다
std::cout<< "Hi" << "Hello";
cout은 전송된 데이터를 출력하라고 콘솔로 요청한다, 이때 콘솔로 즉시 모든 데이터가 전송되지 않고 buffer (이러한 요청을 수집하기 위한 메모리 영역)에 저장된다, buffer는 주기적으로 flush되며 이때 buffer에 들어온 데이터만 console로 전송되게 된다, 따라서 buffer flush전에 프로그램이 종료 혹은 일시정지 (debug)된다면 그 후의 data는 console로 전송되지 않게 된다
이러한 buffering은 여러 출력 요청을 모아서 출력하기 때문에 console로 전송시키는 횟수를 줄여 성능을 향상시킨다
std::endl
endl은 줄바꿈이다
std::cout << "Hi" << std::endl;
std::cout << "Hello" << std::endl;
//Hi
//Hello
이때 사실상 마지막줄에는 endl이 기술적으로는 필요 없지만 유용한 목적으로 사용될 수 있다
영어에서 마침표 느낌으로 마무리 느낌의 목적
커서를 미리 다음줄로 이동시켜 그 뒤의 코드에서 cout이 들어갈 때 바로 다음줄에 출력할 수 있게 하는 목적
일부 운영체제나 명령 프롬프트에서는 커서를 새 줄로 이동시키지 않고 프로그램을 종료 시키면 해당 상태를 그대로 유지하며 다음 명령 프롬프트에서 현재 줄에 이어서 값이 출력될 수 있기 때문에 이를 방지하는 목적
하지만 endl은 단점이 존재한다, 바로 endl을 하게 되면 줄바꿈을 출력하고 buffer를 flush한다는 점이다
여러개의 endl이 사용되게 되면 그 횟수만큼 buffer를 flush하기 때문에 성능상 좋지 않다
C++에서의 출력 시스템은 알아서 주기적으로 buffer를 flush하게 되도록 설계가 되어있다 따라서 자체적으로 flush할 필요가 없다
그렇다면 줄바꿈은 어떻게 처리할까? 바로 '\n'을 사용하면 된다, 이는 buffer flush 를 발생시키지 않고 줄바꿈을 하게 해주며 ""로 묶인 text에서도 사용이 가능하다
std::cout << "Hi" << '\n';
std::cout << "Hello and \n you" << '\n';
//Hi
//Hello and
//you
//
C++에서 문자는 '', 문자열은 ""안에 기입을 하는데 \n은 두개의 문자처럼 보이지만 ''안에 기입해서 사용한다, 왜 그럴까?
'\n'은 2개의 symbol로 표현되지만 컴파일러는 single linefeed(LF)로 처리하기 때문에 ''로 묶는다
std::cin
cin은 키보드의 입력을 받게 해준다 그리고 원하는 변수에 해당 데이터를 할당할 수 있다
int x {};
std::cin >> x;
std::cout << x << '\n';
또한 여러개의 입력을 한 라인에서 받을 수 있다
int x{};
int y{};
std::cin >> x >> y;
std::cout << x << y << '\n';
물론 이렇게 cin으로 입력을 받아 할당하더라도 변수는 초기화해서 사용하는것을 권장한다
C++ IO Libarary에서 엔터키를 안 치고 입력을 받는 방법은 없다
cin에서도 cout과 마찬가지로 buffering이 존재한다
데이터 입력 시 입력 buffer의 끝에 데이터가 추가된다, 이때 enter키를 눌러야 하기 때문에 \n도 함께 추가된다, 그 후 >> 연산자는 입력 buffer의 문자를 제거하고 해당 데이터를 변수에 할당한다 (복사 할당)
다음 코드를 보고 분석해보자
std::cout << "Enter two numbers: ";
int x{};
std::cin >> x;
int y{};
std::cin >> y;
std::cout << "You entered " << x << " and " << y << '\n';
10을 치고 enter를 치면 std::cin >> y로 입력 대기를 한다, 이때 50을 치고 enter를 치게 되면 10, 50이 출력된다
하지만 10 50을 한번에 치고 enter를 쳐도 10, 50이 출력된다 어떻게 이게 가능할까?
입력 buffer로는 10 50 \n이 넘어가게 된다 하지만 공백을 만나면 멈추기 때문에 10만 cin >> x로 넘어가게 되고 공백 뒤의 데이터인 50이 입력 buffer에 남아있기 때문에 추가 입력을 하지 않아도 cin >> y로 넘어가게 되어 10 50이 출력되는 것이다
std::string name;
int age;
std::cout << "Enter your name: ";
std::cin >> name; // 공백 이전까지만 읽음
std::cout << "Enter your age: ";
std::cin >> age;
std::cout << "Name: " << name << ", Age: " << age << std::endl;
return 0;
여기서 Kelvin Park을 입력하게 되면 Name에는 Kelvin이 들어가고 Age에는 아무것도 들어가지 않고 출력된다 (공백을 만나 멈추게 되고 Park은 int 타입인 age에 들어갈 수 없기 때문)