[Modern C++] 따배씨++ Chapter 1 정리

따베씨++ Chapter 1을 듣고 정리해보자!

Modern C++을 복습하기 위해 인프런에서 홍정모 교수님의 따라하며 배우는 C++를 정리해보자.
C++을 처음 배우는 사람이 대상이다보니, 초반부는 조금 쉽다고 느껴진다.
하지만 기초를 탄탄히 한다는 마음으로 정리를 해보았다.

쳅터 1은 C++의 기초적인 사용법이라는 이름으로 되어있다.

이 포스팅에서는 강의에서 나눈 쳅터와 동일하게 쳅터를 나누었다.

1. 프로그램의 구조

우선 기본적으로 프로그램의 구조는 다음과 같다.

#include <iostream>

int main()
{
    int x = 2;  // statement
    x = 5;
    int y = x + 3;  // expression은 x + 3

    return 0;
}

C++을 배우게 되면 아무 생각 없이 위와 같은 코드를 따라치게 된다.
기본적인 강의지만 여기서 이 코드가 컴퓨터가 어떻게 이해하는지 쉽게 설명을 해준다.

#이 붙으면 전처리기(preprocessor directive)를 사용한다고 생각하자. 우리가 프로그래밍을 하는데 전에 다른 개발자 분들이 만들어 놓은 라이브러리를 사용하여 프로그래밍을 한다면 좀 더 편하게 프로그래밍을 할 수 있다.

’<iostream>‘의 경우 io(input/output), stream이다. 입출력을 하는데 필요한 함수들이 정의되어 있다.

main함수는 OS가 프로그램을 실행하게 되면 찾게 되는 중요한 함수이다.
main이라는 함수의 이름은 우리가 임의로 바꿀 수가 없다.

또한 C++같은 경우 빈칸이나 공백은 OS가 컴파일을 하는데 영향을 미치지 않는다.

‘return’은 반환형을 의미하는데 main 함수를 int형으로 선언을 했기 때문에 위 코드에서는 정수형인 0을 반환하게 된다.

이 강의에서 statement와 expression의 개념에 대해 처음 접하게 되었는데,

Statement란 세미콜론으로 한 문장을 정의하고 기능을 수행하는 문구다.
문장의 끝을 표시하는 ;(세미콜론)이 무조건 있어야한다.

Expression이란 값을 반환하는 모든 문장을 말한다.

Statement가 Expression보다 훨씬 더 큰 개념인 것 같다.

조금 더 자세한 정보를 알고 싶으면 문장(Statement)과 표현식(Expression)를 참고해보면 좋을 것이다.

2. 주석 Comments

프로그래밍을 할 때 좋은 주석을 다는 것도 무척이나 중요하다.
코드를 보고 한번에 이해할 수 없는 부분은 주석을 다는 것을 추천한다.

C++에서 주석은 //를 이용하거나 주석으로 처리하고 싶은 부분이 많을 경우, /* */를 이용한다. Visual Studio에서는 주석을 치고 싶은 부분을 드래그 하고 Ctrl + k, Ctrl + c를 입력하면 된다.

수업 중에는 다음과 같은 이야기를 하셨다.

  • 함수 위에 주석을 활용하여 설명을 하는 것이 좋다.
  • 명령문 단위에서 헷갈릴 수 있는 부분은 설명을 하는 것을 추천한다.

주석을 잘 활용하여 나중에 보더라도 빠르게 코드를 이해할 수 있도록 해보자.

3. 변수와의 첫 만남

변수는 항상 초기화를 하는 것을 잊지 말자!

다음 간단한 코드를 한번 살펴보자.

#include <iostream>

int main()
{
    int y;
    std::cout << y << std::endl;
}

Visual Studio에서 초기화를 해주지 않으면 Debug 모드에서는 런타임 에러가 나지만,
Release 모드에서는 임의로 초기화를 시켜준다.

이것이 프로그래머를 편리하게 해주는 기능일 수도 있지만, 예상치 못한 버그로 작용하기도 한다.

초기화를 할 때는 보통
int x = 123 이라고 많이 하는데 int x(123)으로도 초기화 할 수도 있다.

다음 코드도 한번 살펴보자.

int x = 123;

std::cout << x << std::endl;    // #1
std::cout << &x << std::endl;   // #2

x = x + 2;                      // #3

&는 ampersand로 컴퓨터 내부의 메모리 주소를 16진수 숫자로 표현하는 것을 의미한다.

#1에서 출력이 되는 건 x 변수에 담고 있는 값이 출력이 되고,
#2에서 출력이 되는 건 x가 어떤 주소값을 가지고 있는 지 출력하게 된다.

이번 강의를 통해서 L-value & R-value에 대해서도 배웠는데,
L-value는 주소를 가지고 있는 변수, R-value는 임시적으로 저장을 한 다음 바로 사라지는 변수이다.

#3에서 x는 L-value, x+2는 R_value이다.

4. 입출력 스트림과의 첫 만남

기본적인 입출력 스트림인 cin, cout에 대해서 배웠다.

cout을 할 때, “\n”은 한 줄을 띄어주는 것을 의미하고, “\t”는 공백을 만들어주는데 자동적으로 정렬을 해준다. 또한 “\a”를 이용하면 소리를 낼 수도 있다.

<<는 Output operator, >>는 Input operator인데,
int형으로 변수를 하나 만들고 무수히 큰 숫자를 넣게 되면 그 숫자가 모두 표현이 되지 않는다.

따라서 입력 유효성 검증을 해줘야 한다!

5. 함수와의 첫 만남

함수는 말 그대로 function이다.

반복되는 패턴이 나오면 함수를 만드는 습관을 들이자.

함수를 만들면 함수도 컴퓨터의 메모리에 저장이 된다.

실습에서 Break Points를 활용해서 디버깅을 해보는 방법을 배웠는데,
코드의 흐름을 알고 싶을 때 매우 유용하다.

또한 함수도 함수를 부를 수 있다.

실습에 사용한 코드는 아래와 같다.

#include <iostream>

using namespace std;

// 함수의 이름은 자세하게 적어주는 것이 좋다.
// 반복되는 패턴이 나오면 함수를 만드는 습관을 가지자. 

void print()
{
	cout << "hello" << endl;
}

int addTwoNumber(int num1, int num2)
{
	print();
	int sum = num1 + num2;

	return sum;
}

int main()
{
	cout << addTwoNumber(1, 2) << endl;
	cout << addTwoNumber(3, 5) << endl;
}

또한 Void로 만든 함수에서도 return으로 반환을 할 수 있는데,
return 아래에 쓴 코드들은 실행이 안된 다는 것도 주의하자!

void print()
{
    cout << "first" << endl;

    return;

    cout << "second" << endl;   // 이 문장은 출력되지 않는다.  
}

6. 키워드와 식별자 이름짓기

이 강의에서는 C++의 문법을 설명하진 않고, 깔끔한 코드를 작성하기 위해서 가져야 하는 마음가짐에 대해 다루었다.

여기에 정해진 정답은 없다. 따라서 같이 프로그래밍을 하는 사람들끼리 규칙을 정하고 그 규칙에 맞춰서 프로그래밍을 하는 것이 좋다.

이름을 지을 때, C++에서 사용하고 있는 키워드나 숫자로 시작하면 안된다!

너무 길지 않으면서 충분한 의미가 반영될 수 있도록 식별자(Identifier)의 이름을 짓자.

7. 지역 범위

변수가 언제 생성되고 사라지는지 이번 강의를 통해 알 수 있었다.

코드 안에 뜬금 없이 {}가 있어도 에러가 나지 않는 것을 알 수 있었다.

아래 코드를 한번 살펴보자.

#include <iostream>

using namespace std;

void doSomething(int x) 
{
	x = 123;
	cout << x << " " << &x  << endl;    // #1
}

int main()
{
	int x = 0;	// int x(0)과 동일
	cout << x << " " << &x << endl;     // #2

	// 중괄호로 변수가 생성되고 사라지는 범위를 지정해 줄 수 있다.
	{
		int x = 0;	// 다른 메모리 공간에 x가 생긴다.
		cout << x << " " << &x << endl; // #3

	}

	doSomething(x);

	cout << x << " " << &x << endl;    // #4
}

코드를 실행시키면 아래와 같은 결과가 나온다.

0 006EFA18      #2의 출력
0 006EFA0C      #3의 출력
123 006EF938    #1의 출력
0 006EFA18      #4의 출력

다른 메모리 공간에 변수가 생성된다는 것을 확인하기 위해서, &x를 이용해서 출력을 했다.

{}안에서 변수를 선언을 따로 하지 않고, 초기화만 해주면 같은 메모리 공간을 사용한다.

#include <iostream>

using namespace std;

int main()
{
    int x = 0;

    cout << x << " " << &x << endl;     // #1  

    {
        x = 1;  // 변수 선언을 따로 하지 않고, 값을 대입

        cout << x << " " << &x << endl;     // #2
    }

    cout << x << " " << &x << endl;  // #3
}

코드를 실행시키면 아래와 같은 결과가 나온다.

0 00FBFF0C      #1의 출력
1 00FBFF0C      #2의 출력
1 00FBFF0C      #3의 출력

8. 연산자와의 첫 만남

이번 강의에서 배운 단어들은 다음과 같다.

  • 리터럴
  • 피연산자
  • 단항, 이항, 삼항

피연산자는 연산의 대상을 말한다. 또한 피연산자의 갯수에 따라 단항, 이항, 삼항 연산자로 구분을 하게 된다.

리터럴은 코드에 직접 삽입된 값을 의미한다. 그 값이 숫자가 될수도 있고 문자열도 될 수 있다.

다음 코드를 살펴보자.

#include <iostream>

using namespace std;

int main()
{
	int x = 2;	// x is variable, 2 is literal
	// 문자열도 literal
	
	// 단항 연산자
	cout << -x << endl;

	// 이항 연산자
	cout << x + 2 << endl;

	// 삼항 연산자 ()에 있는 것이 참이면 왼쪽에 있는 값을 가져온다. 거짓이면 오른쪽
	int y = (x > 0) ? 1 : 2;
	cout << y << endl;
}

조건 연산자는 C++언어에서 유일한 삼항 연산자이다.

9. 기본적인 서식 맞추기

서식을 맞추는 이유는 컴퓨터를 위해서가 아니라 프로그래머를 위해서다.

나중에 코드를 보더라도 한 눈에 알아볼 수 있도록 코드를 작성하는 것이 중요하다.

코드를 짤 때 통일성을 가지고, 공백을 잘 활용하자.

줄바꿈을 할 때 Operator를 남겨두고 코드를 작성하는 것도 하나의 센스이다.

cout << "Hello"
     << "I'm TaeyoungKim!" << endl;

위와 같이 코드를 작성하기 보단,

cout << "Hello" <<
        "I'm TaeyoungKim!" << endl;

<<를 활용해 다른 프로그래머가 보더라도 아직 cout이 끝나지 않았음을 알려주자.

10. 선언과 정의의 분리

간단한 함수를 작성하는 경우는 상관이 없지만, 함수가 길어지고 코드가 길어지다 보면 선언과 정의를 분리하는 습관을 가지는 것이 매우 중요하다.

선언과 정의를 분리하고 파일별로 이를 관리하는 것이 효율적이다.

선언을 미리 하는 것을 Forward declaration이라 하고, 정의를 Definition이라 한다.

선언과 정의를 분리하고 바로 다음 쳅터에서 파일을 따로 하여 코드를 작성하는 법을 알아 보았다.

11~12. 헤더 파일 만들기, 헤더 가드가 필요한 이유

함수별로 파일을 분리하는 것은 나중에 재사용을 하기 위함이다.
헤더파일을 만들고 #include를 통해 원하는 곳에서 함수를 재사용하면 효율이 높아진다.

#include를 할 때, <>는 컴파일러 설치할 때, 같이 있는 헤더파일을 사용할 때 쓴다.
““는 유저가 직접 헤더파일을 만들고 그 헤더파일을 사용할 때 쓴다.

.h 파일에는 선언을 하고 .cpp 파일에는 정의를 하는 것이 일반적이지만
간단한 함수같은 경우는 .h 파일에 정의까지 하는 경우도 있다.

add.h라는 간단한 헤더파일을 만들고 다음과 같은 내용을 작성해보자.

#pragma once

int add(int x, int y);

#pragma once는 여러 헤더파일을 include할 때, 중복이 되는 이름을 가지고 있을 경우 한번만 include만 하도록 나타낸다. 따라서 헤더파일을 만들 때 필히 있어야 한다!

add.cpp라는 cpp파일을 만들고 add()함수를 정의해보자.

int add(int x, int y)
{
	return x + y;
}

이렇게 되면 메인 cpp 파일에서 #include "add.h"를 추가해주므로써 add()함수를 바로 사용할 수 있다.

메인 cpp파일은 다음과 같다.

#include <iostream>
#include "add.h"    // add()함수를 바로 사용 가능하도록 한다!

using namespace std;

int main()
{
	cout << add(1, 2) << endl;
}

13. 네임스페이스

여러가지 함수를 만들었는데 함수 이름을 동일하게 가져가고 싶다면 네임스페이스를 사용하자.

네임스페이스 안의 함수를 사용하고 싶으면 ::이라는 기호를 사용하면 된다.

사용한 코드는 다음과 같다.

#include <iostream>

using namespace std;

// 함수 이름을 동일하게 가져가고 싶다면
// 네임스페이스를 사용하자.

namespace MySpace1
{
	namespace InnerSpace
	{
		void myFunction()
		{
			cout << "hello" << endl;
		}
	}

	int doSomething(int a, int b)
	{
		return a + b;
	}

}

int doSomething(int a, int b)
{
	return a * b;
}

int main()
{
	cout << doSomething(3, 4) << endl;              // #1
	cout << MySpace1::doSomething(3, 4) << endl;    // #2
	cout << MySpace1::InnerSpace::myFunction << endl;   // #3
}

동일한 doSomething() 함수이지만, namespace를 사용해서 다른 결과가 나오도록 했다.

14. 전처리기와의 첫 만남

전처리기는 크게 #include와 매크로로 나눌 수 있다.

이번 강의에서는 매크로에 대해서 코드를 작성해보았다.

매크로는 대체 텍스트가 있는 경우와 없는 경우 2가지로 또 나뉘는데
#define을 사용해서 매크로를 정할 수 있다. 이때 명령어가 아니므로 세미콜론을 붙이지 않는다!

대체 텍스트가 없는 경우는 조건부 컴파일을 할 때 쓰인다.

매르코를 만들 때 ()를 잘 사용해주자!

실습에서 사용한 코드는 다음과 같다.

#include <iostream>

#define MAX(a,b) ((a > b)? a: b)
#define LIKE_APPLE

using namespace std;

int main()
{
	cout << MAX(1,2) << endl;	// 보통 max는 <algorithm>에 있다. 만들 필요 X

// #define으로 LIKE_APPLE이 정의가 되어있으면 apple 출력 그렇지 않으면 orange 출력
#ifdef LIKE_APPLE	
	cout << "Apple" << endl;
#else
	cout << "Orange" << endl;
#endif // LIKE_APPLE

}

코드에서 #define LIKE_APPLE 부분을 주석처리 해주면 cout << "Orange" << endl;이 활성화 된다!


쳅터 1에서는 C++을 처음 배울 때 알면 좋은 것들에 대해서 알아봤다.
강의 중간중간에 좋은 프로그래머가 되기 위해 알고 있으면 좋은 팁들을 많이 소개해주는 느낌이었다.

댓글남기기