C++ Stack, 재귀 함수와 컴파일 과정 / Developer Command Prompt
자료구조 Stack
push, pop, top, empty 기본 연산재귀 함수(Recursive Function)
C 언어 기본 입출력 복습
scanf()와 printf() 활용%s, %d 서식 지정자\n을 이용한 줄바꿈코딩 기초 트레이닝
C/C++ 빌드 과정
.cpp, .obj, .exe 파일의 역할Developer Command Prompt
cd, dir을 이용한 빌드 경로 및 파일 확인dumpbin /DISASM을 이용한 .obj 파일 분석 방법: 스택(Stack)은 데이터를 한쪽에서 넣고 빼는 자료구조로, LIFO(Last In First Out, 후입선출) 방식으로 동작한다.
가장 마지막에 들어온 데이터가 가장 먼저 나오는 구조이다.
Push A
Push B
Push C
[ C ] ← Top
[ B ]
[ A ]
Pop → C
Pop → B
Pop → A
스택에서 주로 사용하는 연산은 다음과 같다.
push : 데이터를 스택에 추가pop : 가장 위의 데이터를 제거top : 가장 위의 데이터를 확인empty : 스택이 비어 있는지 확인: 게임에서도 가장 최근의 상태나 행동부터 처리해야 하는 경우 스택 구조를 활용할 수 있다.
즉, 최근에 발생한 일을 먼저 되돌리거나 처리해야 할 때 LIFO 구조가 적합하다.
: 재귀 함수(Recursive Function)는 함수가 자기 자신을 다시 호출하는 함수이다.
void CountDown(int n)
{
if (n == 0)
return;
CountDown(n - 1);
}
재귀 함수가 실행될 때 각각의 함수 호출 정보는 Call Stack에 저장된다.
CountDown(3)
↓
CountDown(2)
↓
CountDown(1)
↓
CountDown(0)
호출된 순서는 3 → 2 → 1 → 0이지만 함수가 종료되는 순서는 반대이다.
CountDown(0) 종료
CountDown(1) 종료
CountDown(2) 종료
CountDown(3) 종료
즉, 가장 마지막에 호출된 함수부터 종료되므로 LIFO 구조인 Stack과 연결된다.
∴ 단순 반복 작업에서는 반복문이 더 적절할 수 있고, 재귀적인 구조를 가진 문제에서 선택적으로 사용하는 것이 좋다.
:
문자열을 입력받아 그대로 출력하는 문제이다.
#include <stdio.h>
#define LEN_INPUT 1000001
int main(void)
{
char s1[LEN_INPUT];
scanf("%s", s1);
printf("%s", s1);
return 0;
}
scanf()를 이용하여 문자열을 입력받고 printf()를 이용해 출력했다.
scanf("%s", s1);
printf("%s", s1);
%s는 문자열을 입출력할 때 사용하는 서식 지정자이다.
정수 입력에서는 변수의 주소를 전달하기 위해 &를 사용하지만, 문자 배열의 이름은 배열의 시작 주소를 나타내므로 문자열 입력에서는 다음과 같이 작성할 수 있다.
scanf("%d", &a); // 정수
scanf("%s", s1); // 문자열
두 정수를 입력받고 정해진 출력 형식에 맞게 출력하는 문제이다.
#include <stdio.h>
int main(void)
{
int a;
int b;
scanf("%d %d", &a, &b);
printf("a = %d\n", a);
printf("b = %d", b);
return 0;
}
정수 입력에는 %d를 사용한다.
scanf("%d %d", &a, &b);
출력에서도 %d를 사용하며, 뒤에 전달한 값이 순서대로 각각의 %d에 들어간다.
printf("a = %d\n", a);
또한 \n을 이용해 출력 위치를 다음 줄로 이동할 수 있다.
이번 문제를 통해 코딩 테스트에서는 단순히 값이 맞는 것뿐만 아니라 문제에서 요구한 출력 형식을 정확하게 맞추는 것도 중요하다는 것을 확인했다.
: C/C++로 작성한 소스 코드는 작성한 형태 그대로 CPU에서 실행되는 것이 아니다.
기본적인 빌드 흐름은 다음과 같다.
소스 코드(.cpp)
↓
컴파일
↓
오브젝트 파일(.obj)
↓
링크
↓
실행 파일(.exe)
컴파일 과정에서 .cpp 파일은 기계가 실행할 수 있는 형태에 가까운 목적 코드가 들어 있는 .obj 파일로 변환된다.
여러 .obj 파일과 필요한 라이브러리를 링커가 연결하여 최종 실행 파일인 .exe를 만든다.
따라서 컴파일 오류와 링크 오류는 발생하는 단계 자체가 다르다.

: Visual Studio를 설치하면 일반 명령 프롬프트와 별도로 Developer Command Prompt for Visual Studio를 사용할 수 있다.
Visual Studio의 컴파일러와 개발 도구를 명령어로 사용할 수 있도록 필요한 환경이 설정되어 있는 명령 프롬프트이다.
: Windows 검색창에서 다음을 검색한다.
Developer Command Prompt for VS 2022
실행한 뒤 프로젝트의 빌드 결과가 있는 폴더로 이동한다.
예를 들어 .obj 파일이 다음 위치에 있다면,
프로젝트폴더\x64\Debug
cd 명령을 사용해 해당 폴더로 이동할 수 있다.
cd 프로젝트경로\x64\Debug
현재 폴더에 있는 파일을 확인하려면 다음 명령을 사용한다.
dir
파일 목록에서 .obj 파일이 생성되어 있는지 확인한다.
: .obj 파일을 사람이 확인할 수 있는 형태로 출력하기 위해 dumpbin을 사용할 수 있다.
dumpbin /DISASM main.obj
실행하면 다음과 같은 어셈블리 명령어들을 확인할 수 있다.
push
mov
sub
lea
call
pop
ret
평소 작성하는 C/C++ 코드가 컴파일 과정을 거치면서 CPU가 처리할 수 있는 저수준 명령어 형태로 변환된다는 것을 직접 확인할 수 있다.
이 실습에서는 각각의 어셈블리 명령어를 모두 해석하는 것보다,
내가 작성한 C/C++ 소스 코드가 컴파일을 거쳐 실제로 다른 형태의 명령어로 변환된다는 것을 확인하는 것
에 의미가 있다.
[학습 후 느낀 점과 개선하고 싶은 점]
: Stack의 LIFO 구조에서 시작하여 재귀 함수가 내부적으로 Call Stack을 사용하는 과정까지 연결해서 학습했다.
특히 재귀 함수가 단순히 '자기 자신을 호출하는 함수'라는 개념에서 끝나는 것이 아니라, 호출될 때마다 실행 정보가 Stack에 쌓이고 가장 최근 호출부터 반환된다는 점에서 Stack의 LIFO 구조와 직접 연결된다는 것을 이해할 수 있었다.
코딩 기초 문제에서는 문자열과 정수의 기본 입출력 방식, %s, %d와 같은 서식 지정자, \n을 이용한 줄바꿈과 출력 형식을 다시 확인했다.
또한 C/C++의 빌드 과정이 단순히 '실행 버튼을 누르는 것'으로 끝나는 것이 아니라,
소스 코드 → 컴파일 → 오브젝트 파일 → 링크 → 실행 파일
순서로 이루어진다는 점을 정리했다.
Developer Command Prompt와 dumpbin을 이용하면 생성된 .obj 파일의 내부를 확인할 수 있으며, 다음 실습에서는 직접 작성한 코드가 컴파일 이후 어떤 형태로 변환되는지 확인해볼 예정이다.