본문 바로가기
프로그램 개발/C++ 개발

C++ 언어 간략한 문법 정리 1 (주석, 전처리 지시자, 네임스페이스, 변수)

by minchel1128 2021. 4. 23.

C++은 기본적으로 C언어에서의 확장판과 같습니다. 이 게시글에서 설명하는 내용은 전문가를 위한 C++책과 C++17 버전을 기반으로 하고 있습니다. 그리고 현재(2021년 4월 기준) C++20에 관한 번역서가 없어 구하게 되면 내용 업데이트를 진행하겠습니다.

모든 언어의 시작 프로그램인 Hello, World! 를 출력하는 프로그램 코드로서는 다음과 같습니다.

더보기
//Hello, World! 출력
#include<iostream>
using namespace std;

int main(void)
{
	cout << "Hello, World!" << endl;
	return 0;
}

여기서 확인 가능한 중요한 개념으로는 다음과 같습니다.

  • 주석
  • 전처리 지시자
  • main() 함수
  • I/O스트림(입출력)
  • 네임스페이스

주석은 프로그래머한테 메시지를 전달하기 위해 작성하는 부분으로 컴파일러는 해당 부분을 무시합니다. 주석을 작성하는 건 대단히 중요한데 해당 프로그램 코드 부분이 매우 간단하여 누구나 봐도 메커니즘까지 알 수 있는 경우가 아닌 경우 주석이 없다면 해당 코드를 작성할 때는 나와 신만이 아는 코드에서 신만이 아는 코드가 되는 기적을 볼 수 있습니다. 주석을 작성하는 방법으로는 C++의 경우에는 //을 사용하거나 /* */을 사용합니다. //은 한 줄의 내용을 가진 주석을 작성할 때 사용하고 /* */는 여러 줄을 가진 주석을 작성할 때 사용합니다. /*는 시작 부분을 */은 끝부분을 의미하고 //은 해당 줄의 주석 부분 시작을 의미합니다.

전처리 지시자는 프로그램 빌드 작업에서 가장 먼저 처리되는 부분으로 컴파일보다 우선하여 시작됩니다. 보통 전처리 지시자는 다른 헤더 파일을 가져오거나 값을 정의할 때 사용됩니다. 위 코드에서의 전처리 지시자는 #include <>이며 iostream헤더 파일을 불러오라는 의미를 가지고 있습니다. iostream헤더 파일은 기본 입출력 메커니즘을 선언하고 있으며 C언어 기존 표준 라이브러리의 헤더 파일을 표현할 때는. h확장자가 붙으며 네임스페이스를 사용하지 않고 C++의 표준 라이브러리의 헤더 파일을 불러올 때는 확장자를 생략하며 모든 항목이 std네임스페이스 혹은 그 하위 네임스페이스의 아래에 정의되어 있습니다.

자주 사용하는 전처리 지시자로는 다음과 같습니다.

전처리 지시자 기능 주 사용 법
#include<파일명> 지정한 파일의 내용을 지시자 위치에 넣는다 다른곳에 정의된 함수를 사용할 목적으로 사용한다.
#define 키 값 코드에서 키에 해당하는 부분을 모두 값으로 지정한 내용으로 바꾼다 주로 상수값이나 매크로를 정의하기 위해 사용했으며 C++에서는 개선된 메커니즘을 제공한다.
#ifdef 키
#endif

#ifndef 키
#endif
지정한 키가 #define문으로 정의되었는지 여부에 따라 묶인 코드 블럭을 포함시키거나 제외한다 주로 #include문장이 반복되는 것을 막는 용도로 사용한다
#pragma xyz xyz에 대한 구체적인 동작은 컴파일러마다 다르며 주로 전처리 과정에서 경고나 에러 메시지를 화면에 표시하는 용도로 사용한다. 좀 더 간결하게 중복을 막는데 사용한다.
더보기
#ifndef HEADER
#define HEADER
//헤더파일에 담을 내용
#endif

중복 인클루드를 막는 용도로 사용하는 방법의 예시이며

#pragma once
//헤더 파일에 담을 내용

#pragma once를 지원하면 간결하게 표현 가능합니다.

main() 함수의 경우 모든 프로그램의 시작 부분이며 항상 int값을 리턴합니다. 특별히 main() 함수 안에서는 리턴 문자를 생략하면 0의 값을 자동으로 반환합니다. main함수는 매개변수를 받지 않거나 두 매개변수를 받도록 할 수 있는데 이 경우 int main(int argc, char* argv [])형식으로 작성하게 되며 argc는 프로그램에 전달할 인수 개수를 지정하고 argv는 전달할 인수의 값을 가집니다. 보통 argv [0]에서는 프로그램 이름이 담기며 프로그램 이름을 참조하려면 공백이 지정될 수 있는 오류가 생길 수 있어 따로 플랫폼에서 제공하는 기능을 사용하는 것이 좋습니다.

I/O스트림은 입출력 스트림이라고도 부르는데 이는 사용자 혹은 파일에서 값을 불러와서 다른 출력장치나 파일로 출력을 하는 부분입니다. C++에서는 C언어의 printf()와 scanf() 함수를 쓰는 것과는 다르게 기본 입출력을 std::cout 하고 std::cin을 사용하며 추가적으로 <<와 >>연산자를 사용해 표현합니다. 다음은 기본 출력 예시입니다.

std::cout<<"Hello, " << 213 << "World!"<<std::endl;

위에서 볼 수 있다시피 << 연산자를 사용해 Java나 Python에서 볼 수 있는 기본 출력의 + 연산자와 대응된다고 볼 수 있으며 안의 내용들을 하나로 엮어주는 역할을 담당합니다. 또한 맨 마지막 std::endl은 한 줄이 끝났다는 의미로 C언어의 \n과 같은 역할을 담당합니다. 추가적으로 \n와 같은 문자를 이스케이프 시퀀스라고 부르는데 주로 사용하는 예시로는 다음과 같습니다.

이스케이프 시퀀스 설명
\n 개행(줄 바꿈)
\r 캐리지 리턴(출력 커서를 처음으로 옮김)
\t
\\ 역슬래시 출력(\모양은 한글 윈도우에서의 원화 표시지만 다른 언어에서는 /모양의 역의 모양을 가지고 있습니다. 단 일본어 윈도우에서는 엔화로 표시됩니다.)
\" 따옴표 출력

그리고 기본적인 입력으로는 std::cin을 사용하고 사용 예시는 다음과 같습니다.

int num;
std::cin >> num;
std::cout << num << std::endl;

C언어에서 사용하던 printf()와 scanf() 함수는 C++에서도 여전히 사용 가능하지만 타입 안정성을 보장하지 않아 사용을 권장하지 않습니다.

네임 스페이스는 코드의 이름이 서로 충돌하는 문제를 해결하기 위해 나온 개념으로서 기본 적인 네임스페이스로는 std가 있습니다. 네임스페이스가 사용되는 이유로는 어느 라이브러리에서 정의되어있는 함수가 있고 같은 이름으로 다른 라이브러리에 정의되어있는 함수가 있는 경우 두 라이브러리 상의 함수를 사용하는 경우 컴파일러는 해당 함수를 구별할 수 없고 외부 라이브러리의 코드는 수정이 불가능하므로 자신의 코드를 수정해야 하지만 고치기엔 이미 코드가 커져버린 경우에는 문제가 발생하게 됩니다. 이런 경우를 해결하기 위해 나온 것이 네임스페이스라고 할 수 있습니다.

사용하는 예시로는 다음과 같습니다.

#include <iostream>
#include "namespaces.h"

void mycode::print()
{
	std::cout << "called in the mycode namespace" << std::endl;
}
// 다른 방식으로 작성
// 주석 코드를 지우고 사용하시면 됩니다.
/*
namespace mycode {
	void print()
    {
    		std::cout << "called in the mycode namespace" << std::endl;
	}
}*/

그리고 호출하는 방식으로는 다음과 같습니다.

mycode::print();

그리고 같은 함수를 많이 사용하는 경우에는 네임스페이스의 접두어를 계속 사용하는 것은 귀찮으므로 using 지시자를 이용해 네임스페이스 접두어를 생략할 수 있습니다. 또한 여러 네임스페이스들을 using 지시자를 이용해 처리가 가능하지만 남용하지 않도록 해야 하며 그 이유로는 여러 네임스페이스들을 using으로 처리하면 기존의 컴파일러가 구별 못하는 문제가 재발하므로 주의해야 합니다.

사용방법은 다음과 같습니다.

#include <iostream>
using namespace std;

int main()
{
	cout << "Hello, World!" << endl;
    return 0;
}
/* using을 사용하지 않은 코드

int main()
{
	std::cout << "Hello, World!" << std::endl;
    return 0;
}
*/

헤더 파일 안에서는 using문을 사용해서는 안되는데 그 이유로는 헤더 파일 안에 using을 사용하게 되면 인클루드 하는 모든 파일에서 using문에서 지정한 방식으로 호출해야 하기 때문입니다.

C++17부터는 중첩된 네임스페이스를 보다 쉽게 사용 가능하도록 개선되었으며 앨리어스를 사용해 다르게 표현하거나 짧게 만들 수 있게 되었습니다. 예시는 다음과 같습니다.

namespace MyLibraries {
    namespace Networking {
    	namespace FTP {
     		/*코드 작성*/
    	}
    }
}

기존에는 위와 같이 작성을 해야 했지만 아래와 같이 간결하게 작성이 가능합니다.

namespace MyLibraries::Networking::FTP{
	/*코드 작성*/
}

또한 앨리어스의 사용법은 다음과 같습니다.

namespace MyFTP = MyLibraries::Networking::FTP;

 

리터럴의 경우 코드에 표시한 숫자나 스트링과 같은 값을 의미하며 C++에서는 여러 표준 리터럴을 지원합니다.

  • 십진수
  • 8진수
  • 16진수
  • 이진수
  • 부동소수점
  • 배정도 부동소수점
  • 단일 문자
  • '0'으로 끝나는 문자 배열

로 구성되어 있으며 숫자 리터럴에서는 자릿수 구분자로 '를 사용할 수 있습니다.

또한 17 버전으로 올라가면서 16진수 부동소수점도 지원합니다.

변수는 값을 저장하는 부분이며 C++에서는 코드 안 어디에서나 선언이 가능합니다. 현재의 블록에서 변수를 선언한다면 그다음부터 어디에서나 그 변수에 접근을 할 수 있습니다. 또한 변수를 선언할 때는 값을 대입하여 초기화를 하지 않아도 되지만 초기화되지 않은 변수는 버그를 일으킬 가능성이 높아 변수를 초기화하는 것을 권장합니다.

또한 대부분의 컴파일러는 변수를 선언을 할 때 초기화가 되어있지 않은 경우라면 경고 또는 에러 메시지를 발생시키게 설정되어 있습니다.

자주 사용하는 변수로서는 다음과 같습니다.

타입 설명 사용 예
(signed) int
signed
부호가 있는 정수를 표현하며 값의 범위는 컴파일러마다 다르지만 일반적으로 4바이트(-2,147,483,648 ~ 2,147,483,647까지 표현) 크기이다. int i = 1;
signed int i = -5;
signed i = 3;
(signed) short (int) 작은 범위의 정수(대부분 2바이트) short s = 13;
short int s = 14;
shigned short s = 15;
(signed) long (int) 큰 범위의 정수(대부분 4바이트) long l = -7L;
(signed) long long (int) 아주 큰 범위의 정수(대부분 8바이트이지만 최소한은 long보다는 크다) long long l = 14LL;
unsigned (int)
unsigned short (int)
unsigned long (int)
unsigned long long (int)
앞의 int, short, long, long에서 0이상의 정수로 제한한다. unsigned int i = 2U;
float 단정도 부동소수점수 float f = 7.2f;
double 배정도 부동소수점수, 정밀도가 더 크다 double d = 7.2;
long double 롱배정도 부동소수점수이며 정밀도가 훨씬 더 크다 long double d = 16.98L;
char 단일 문자 char ch = 'm';
char16_t 16비트 단일 문자 char16_t c16 = u'm';
char32_t 32비트 단일 문자 char32_t c32 = u'm';
wchar_t 단일 확장 문자 wchar_t w = L'm';
bool 부울 타입으로 참과 거짓값을 가진다. bool b = true;

C++에서는 아쉽게도 여러 글자를 저장하는 변수가 없습니다. 다만 이는 표준 라이브러리의 스트링에서 제공을 하고 있습니다. 그리고 c++17로 올라가게 되면서 한 바이트를 표현하는 std::byte라는 타입이 새로 추가되었으며 std::byte b{42}; 형식으로 사용합니다.

그리고 이들 변수 타입들은 실행 중에 바꿀 수 있는데 이를 캐스팅 혹은 동적 형 변환, 타입 캐스팅이라고 부릅니다.

다만 강제적으로 형 변환하는 경우 값의 손실이 있을 수 있어 필요한 경우 이외에는 사용하지 않는 것을 권장합니다.

예시 코드는 다음과 같습니다.

float f = 3.14159f; //실수형 변수 선언

int if1 = (int)f; // 1번 방법
int if2 = int(f); // 2번 방법
int if3 = static_cast<int>(f); // 3번 방법

여기서 1번은 기존 C언어에서 주로 사용하던 방법이며 2번은 잘 사용되지 않는 방식입니다. 3번은 캐스팅이 필요하다면 사용하는 것을 권장하는 방법입니다. 여기서 값들은 전부 3으로 들어가게 되며 소수점 아래 부분은 소실됩니다.

여기까지 C++ 언어 문법 간단한 정리 1부를 마무리하겠습니다.

728x90
반응형