PHpullh
심층 가이드/C++/Hello World

C++ · 심층 가이드

C++ Hello World 완전 정리

C++로 첫 프로그램을 세우면서 컴파일과 링크, 표준 출력 스트림, 명령행 인자와 환경 변수까지 실행 파일이 만들어지는 경로를 손으로 확인하는 예제 묶음입니다.

주제 10개 · 예제 코드 포함 · 최종 수정 2026-09-03 · 작성 pullh 편집팀

C++의 첫 프로그램은 인터프리터 언어와 사정이 다릅니다. 파일을 저장하고 실행하는 대신 전처리, 컴파일, 링크를 거쳐 실행 파일이 만들어지고, 초보자를 막는 오류 대부분이 이 단계 구분에서 나옵니다. 헤더를 빠뜨렸을 때와 정의를 빠뜨렸을 때 나오는 메시지가 다르다는 것만 알아도 진단 속도가 달라집니다. 화면에 한 줄을 찍는 이 작은 예제들은 사실 툴체인이 어디까지 정상인지 확인하는 점검용 도구에 가깝습니다.

순서는 출력에서 시작해 입력으로 넓어집니다. Hello World 출력로 std::cout과 스트림 삽입 연산자를 잡고, 변수 값을 같이 출력에서 여러 값을 이어 붙이는 방식을 익힙니다. 함수로 인사 만들기는 선언과 정의가 나뉘는 C++의 기본 구조를 처음 보여 주고, 명령행 인자 읽기와 환경 변수 읽기는 프로그램 바깥에서 값이 들어오는 두 경로를 각각 다룹니다. 유니코드 메시지 출력은 콘솔 인코딩 문제를 미리 겪어 두기 좋은 자리입니다.

실행 파일을 만들 때 표준 버전을 지정하지 않으면 컴파일러 기본값이 적용되는데, 이 기본값은 컴파일러와 배포판마다 다릅니다. 예제가 내 환경에서만 안 되는 상황의 상당수가 여기서 옵니다. 빌드 명령에 표준 버전 옵션을 명시해 두면 이 변수를 없앨 수 있습니다. 또 하나, 윈도우 콘솔에서 한글이 깨지는 문제는 코드가 아니라 콘솔 코드 페이지와 소스 파일 인코딩의 조합에서 발생합니다. 소스를 UTF-8로 저장하고 콘솔 출력 인코딩을 맞추면 해결되는 경우가 대부분이라, 이 시점에 원인을 정리해 두면 이후 문자열 예제 내내 시간을 아낍니다.

01Hello World 출력

<iostream>을 포함하고 main에서 std::cout으로 문자열 한 줄을 내보내는, 실행 가능한 최소 프로그램입니다. 새 툴체인이나 빌드 설정이 제대로 동작하는지 확인할 때 첫 코드로 씁니다. main은 return 문이 없어도 0을 돌려주도록 규격에 정해져 있는 유일한 함수입니다.

C++ code

// Hello World 출력
#include <iostream>

int main() {
    std::cout << "Hello, World 1!\n";
}
알아두면 좋은 점

-std=c++20 -Wall -Wextra처럼 표준과 경고 옵션을 함께 지정해 두면 툴체인 확인과 동시에 기본 진단 설정까지 점검할 수 있습니다.

자주 하는 실수

using namespace std;를 헤더에 적으면 그 헤더를 포함한 모든 파일에서 이름이 충돌할 수 있으므로, 쓰더라도 소스 파일 안으로 한정해야 합니다.

02변수 값을 같이 출력

std::getenv("APP_ENV")가 돌려준 포인터를 삼항 연산자로 검사해, 값이 있으면 그 값을 없으면 local을 출력합니다. 실행 환경에 따라 동작을 바꾸는 프로그램의 첫 단추에 해당합니다. 변수가 설정돼 있지 않으면 반환값이 nullptr이므로 검사 없이 역참조하면 그대로 크래시로 이어집니다.

C++ code

// 변수 값을 같이 출력
#include <cstdlib>
#include <iostream>

int main() {
    const char* env = std::getenv("APP_ENV");
    std::cout << (env ? env : "local") << "\n";
}
알아두면 좋은 점

설정을 읽는 자리를 함수 하나로 모아 두면, 나중에 파일이나 명령행에서 값을 받도록 바꿀 때 호출부를 건드리지 않아도 됩니다.

자주 하는 실수

std::getenv가 돌려준 포인터를 std::string으로 복사해 두지 않고 오래 보관하면, 이후 환경이 바뀔 때 그 메모리가 유효하지 않을 수 있습니다.

03함수로 인사 만들기

인사말을 만드는 별도 함수는 정의되지 않았고, main이 직접 리터럴을 출력합니다. 함수로 빼내면 인사 문구를 한곳에서 바꿀 수 있습니다. 출력 끝의 \n은 줄만 바꾸고 버퍼를 비우지 않는 반면 std::endl은 매번 flush를 강제하므로, 반복 출력에서는 \n이 더 유리합니다.

C++ code

// 함수로 인사 만들기
#include <iostream>

int main() {
    std::cout << "Hello, World 3!\n";
}
알아두면 좋은 점

출력이 파일로 연결되면 버퍼가 커져 결과가 바로 보이지 않으므로, 진행 상황을 실시간으로 확인하려면 std::flush를 명시해야 합니다.

자주 하는 실수

std::endl을 반복문 안에서 쓰면 매번 flush가 일어나 출력이 크게 느려지는데, 그 비용은 코드만 봐서는 잘 드러나지 않습니다.

04배열을 순회하며 인사

배열 순회는 없고, 환경 변수 하나를 읽어 기본값과 함께 출력하는 코드입니다. 여러 이름을 돌려 가며 확인하려면 문자열 배열을 만들고 범위 기반 for로 getenv를 반복 호출하면 됩니다. 값이 빈 문자열로 설정된 경우와 아예 없는 경우는 이 코드로 구분되지 않습니다.

C++ code

// 배열을 순회하며 인사
#include <cstdlib>
#include <iostream>

int main() {
    const char* env = std::getenv("APP_ENV");
    std::cout << (env ? env : "local") << "\n";
}
알아두면 좋은 점

값이 비어 있는 경우까지 구분하려면 포인터가 널인지와 첫 문자가 널 문자인지를 따로 검사해야 의미가 분명해집니다.

자주 하는 실수

환경 변수 이름의 대소문자는 유닉스 계열에서 구분되므로, 소문자로 설정하고 대문자로 읽으면 언제나 기본값만 나옵니다.

05명령행 인자 읽기

main이 인자를 받지 않는 형태라 명령행 값을 읽지 못하고, 고정된 문자열만 출력합니다. 인자를 쓰려면 int main(int argc, char* argv[])로 선언해야 하며 argv[0]에는 실행 파일 이름이 들어갑니다. 여기 쓰인 리터럴은 const char 배열이고 널 문자까지가 하나의 덩어리입니다.

C++ code

// 명령행 인자 읽기
#include <iostream>

int main() {
    std::cout << "Hello, World 5!\n";
}
알아두면 좋은 점

argv[argc]는 널 포인터로 정해져 있어, 개수를 세는 대신 이 값을 종료 표시로 삼아 인자를 순회할 수도 있습니다.

자주 하는 실수

문자열 리터럴을 char*에 담아 내용을 고치려 하면 정의되지 않은 동작이며, C++11부터는 그런 대입 자체가 컴파일 오류입니다.

06템플릿 문자열 인사

문자열 템플릿을 채우는 대신, 환경 변수 값 또는 대체 문자열을 그대로 흘려보냅니다. 서식이 필요하면 C++20의 std::format으로 자리표시자를 채우는 방식이 스트림 연쇄보다 읽기 쉽습니다. getenv가 준 포인터는 수정하면 안 되고, 이후 환경을 변경하면 무효가 될 수 있습니다.

C++ code

// 템플릿 문자열 인사
#include <cstdlib>
#include <iostream>

int main() {
    const char* env = std::getenv("APP_ENV");
    std::cout << (env ? env : "local") << "\n";
}
알아두면 좋은 점

std::format은 인자를 위치 번호로 참조할 수 있어, 같은 값을 여러 자리에 넣을 때 인자를 반복해 넘기지 않아도 됩니다.

자주 하는 실수

설정값을 그대로 로그나 화면에 찍으면 토큰이나 비밀번호가 함께 남을 수 있으므로, 외부에서 주입되는 값은 출력 전에 걸러야 합니다.

07유니코드 메시지 출력

유니코드를 다루는 처리는 없고 ASCII 문자열 한 줄을 출력하는 코드입니다. 한글이나 이모지를 넣으면 UTF-8 바이트가 그대로 나가므로, 결과는 터미널의 인코딩 설정에 좌우됩니다. 프로그램이 정상 종료할 때 std::cout은 자동으로 flush되기 때문에 마지막 줄이 잘리지는 않습니다.

C++ code

// 유니코드 메시지 출력
#include <iostream>

int main() {
    std::cout << "Hello, World 7!\n";
}
알아두면 좋은 점

소스 파일 인코딩을 UTF-8로 고정하려면 MSVC에서 /utf-8 옵션이 필요하고, 그러지 않으면 한글 리터럴이 다른 인코딩으로 해석됩니다.

자주 하는 실수

한글 문자열의 size()는 글자 수가 아니라 바이트 수를 돌려주므로, 이를 화면 폭으로 착각해 자르면 문자 중간이 끊깁니다.

08환경 변수 읽기

<cstdlib>의 getenv로 APP_ENV를 조회하고, 없으면 local을 대신 출력하는 전형적인 기본값 처리입니다. 배포 환경 구분처럼 외부에서 주입되는 설정을 읽을 때 씁니다. 이 함수는 스레드 안전이 보장되지 않으므로, 다른 스레드가 동시에 환경을 바꾸면 경쟁 상태가 됩니다.

C++ code

// 환경 변수 읽기
#include <cstdlib>
#include <iostream>

int main() {
    const char* env = std::getenv("APP_ENV");
    std::cout << (env ? env : "local") << "\n";
}
알아두면 좋은 점

환경 변수는 프로세스가 시작할 때 복사되므로, 실행 중에 셸에서 값을 바꿔도 이미 돌고 있는 프로세스에는 반영되지 않습니다.

자주 하는 실수

환경을 바꾸는 함수와 getenv가 동시에 실행되면 경합이 생기므로, 설정값은 스레드를 만들기 전에 한 번에 읽어 두는 편이 안전합니다.

09컬렉션 요약 출력

컬렉션을 요약하는 코드는 없고, 문자열 하나를 표준 출력으로 보내는 것이 전부입니다. 개수나 합계를 찍으려면 컨테이너와 <numeric>의 알고리즘이 함께 필요합니다. 출력량이 많아지면 std::ios::sync_with_stdio(false)로 C 표준 입출력과의 동기화를 끊어 처리량을 올릴 수 있지만, 그 뒤로는 printf와 섞어 쓰면 안 됩니다.

C++ code

// 컬렉션 요약 출력
#include <iostream>

int main() {
    std::cout << "Hello, World 9!\n";
}
알아두면 좋은 점

std::ios::sync_with_stdio(false)와 함께 std::cin.tie(nullptr)까지 끊으면 입력 전 자동 flush도 사라져 대량 입출력이 더 빨라집니다.

자주 하는 실수

동기화를 끊은 뒤 printf와 스트림 출력을 섞어 쓰면 순서가 뒤바뀌므로, 프로젝트 전체에서 한쪽으로 통일해야 합니다.

10버전 정보 출력

버전 문자열 대신 환경 변수 APP_ENV의 값을 읽어 출력합니다. 빌드 정보를 찍고 싶다면 컴파일러가 정의하는 __cplusplus나 빌드 스크립트가 주입한 매크로를 쓰는 편이 맞습니다. MSVC에서는 getenv가 사용 중단 경고를 내며 _dupenv_s를 권하므로, 이식성이 필요하면 감싸는 함수를 두는 것이 좋습니다.

C++ code

// 버전 정보 출력
#include <cstdlib>
#include <iostream>

int main() {
    const char* env = std::getenv("APP_ENV");
    std::cout << (env ? env : "local") << "\n";
}
알아두면 좋은 점

__cplusplus 값은 MSVC에서 별도 옵션을 켜야 실제 표준 번호를 돌려주므로, 기능 확인에는 <version>의 기능 매크로가 더 정확합니다.

자주 하는 실수

버전 문자열을 소스에 직접 적어 두면 갱신을 잊기 쉬우므로, 빌드 시스템이 정의한 값으로 주입받아야 실제 산출물과 어긋나지 않습니다.

정리하며

  • 빌드 명령에 표준 버전 옵션을 명시해 컴파일러 기본값 차이로 생기는 혼란을 없앱니다
  • 컴파일 오류와 링크 오류를 구분해 읽으면 헤더 문제인지 정의 문제인지 바로 갈립니다
  • 명령행 인자는 개수 확인 후 접근해야 하며, 프로그램 이름이 첫 번째 자리를 차지합니다
  • 한글 출력이 깨지면 소스 인코딩과 콘솔 코드 페이지를 함께 맞춰야 해결됩니다

더 깊이 들어가고 싶다면 C++ 학습 라이브러리에서 다른 주제 가이드를 이어서 보거나, 언어 비교에서 같은 개념이 다른 언어에서 어떻게 표현되는지 확인해 보세요.