여기서는 마이크로소프트의 통합 개발환경인 비주얼 스튜디오를 이용해서, C언어 또는 C++ 콘솔 프로그램을 만드는 법에 대해서 알아봅시다. 콘솔 프로그램이란 문자만으로 이루어진 인터페이스를 가진 프로그램으로, 터미널을 열어서 명령 프롬프트 등을 통해 실행하는 것이 한 예시가 될 수 있습니다. 참고로 이 포스팅은 비주얼 스튜디오 커뮤니티 2019를 기준으로 작성되었습니다.
요즘 세상에 누가 콘솔 프로그램을 쓸까 싶을 수 있는데요. 수학이나 과학 문제를 수치적으로 푸는 프로그램같이 계산결과를 파일에 저장한 뒤, 이를 시각화하는 것은 별도의 프로그램에게 맡기는 경우라면 콘솔 어플리케이션도 나쁘지 않은 선택입니다. 그래서 저도 개인적으로 이러한 용도의 비주얼 스튜디오 콘솔 프로그램을 많이 작성했었습니다. 뿐만 아니라 비주얼 스튜디오로 C/C++를 처음 배울때도, 콘솔 프로그램을 통해서 "Hello World!"를 출력하는것부터 시작하죠.
일반적인 C언어 및 C++ 프로그램을 빌드하는 과정에서 소스, 헤더 파일들과 라이브러리가 어떻게 개입하는지에 대해 더 자세한 내용이 궁금하신 분들은, 시작하기에 앞서서 다음 포스팅을 읽어보는 것도 나쁘지 않습니다.
새 프로그램을 위해서는 먼저 이를 위한 프로젝트를 만들어야 합니다. 비주얼 스튜디오를 실행하면 나타나는 메인 화면에서 파일 > 새로 만들기 > 프로젝트를 선택합니다. 프로젝트 템플릿 중에서 콘솔 어플리케이션 (Console App)을 선택합니다.
새로운 프로젝트가 생성되면, "Hello World!"를 출력하는 샘플 C++ 소스 파일이 있습니다. 필요에 따라 내용만 수정해도 되고, 이 파일을 아예 삭제해도 상관없습니다.
오른쪽에 보이는 솔루션 탐색기에 보면, 프로젝트 이름이 있고 그 밑에 각종 소스 파일이나 헤더 파일들의 항목이 있는데요. 각 항목에다가 마우스를 갖다대고 우클릭 하면, 새로운 파일을 만들거나 기존에 있던 파일을 불러오는 것이 가능합니다.
새로운 파일을 만드는 경우, 이름을 지정하고 경로를 확인할 수 있습니다.
간단한 예시로서, 급수 계산을 통해 오일러 상수의 근사값을 계산하는 프로그램을 빌드해 봅시다. 중요한 수학 상수 중의 하나인 오일러 상수는 원칙적으로는 무한 급수로 나타나는 무리수입니다만, 유한 급수로 끊어서 근사값을 구하는것이 가능합니다. 오일러 상수와 이를 밑으로 하는 자연지수 함수에 대한 더 자세한 내용은 다음 포스팅에 소개되어 있습니다.
C언어 및 C++ 소스 코드입니다.
main_exp.cpp
#include<stdio.h>
#include<stdlib.h>
#define _USE_MATH_DEFINES
#include<math.h>
#include"SumSeries.h"
double get_term_exp(long int n);
int main(int argc, char* argv[]) {
double (*ptr_func)(long int);
ptr_func = &get_term_exp;
long int n_min = 0;
long int n_max = 20;
double e_euler =
sum_series(n_min, n_max, ptr_func);
fprintf(stdout,
"e from finite series\n");
fprintf(stdout,
" > e = %.16f\n", e_euler);
fprintf(stdout,
"e from C math library\n");
fprintf(stdout,
" > e = %.16f\n", M_E);
return 0;
}
double get_term_exp(long int n) {
if (n < 0) {
return 0.;
}
double term = 1.;
long int i;
for (i = 1; i <= n; i++) {
double factor_now = 1. / (double)i;
term = term * factor_now;
}
return term;
}
프로그램이 실행되면 호출되는 main 함수와 함께, 급수의 각 항을 정의한 get_term_exp라는 함수가 정의되어 있는데요. get_term_exp 함수의 포인터를 받아서 유한급수를 계산하는 sum_series 함수가 호출되고 있는 것을 볼 수 있습니다.
한가지 더 짚고 넘어가자면, 수학 함수 라이브러리의 math.h 헤더파일을 포함시키기 전에 _USE_MATH_DEFINES라는 식별자를 먼저 정의해줘야, 라이브러리에 내장된 원주율 등의 상수들을 사용할 수 있습니다.
함수들의 존재를 알려줘야 하기 때문에, 프로토타입이 선언되어 있는 헤더 파일이 필요합니다.
SumSeries.h
#ifndef _SUM_SERIES_H_
#define _SUM_SERIES_H_
#undef __WRAP_CXX_INI
#undef __WRAP_CXX_FIN
#ifdef __cplusplus
#define __WRAP_CXX_INI extern "C" {
#define __WRAP_CXX_FIN }
#else
#define __WRAP_CXX_INI /* empty */
#define __WRAP_CXX_FIN /* empty */
#endif
__WRAP_CXX_INI
double sum_series(long int n_min,
long int n_max,
double (*ptr_term)(long int));
__WRAP_CXX_FIN
#endif
헤더 파일에는 sum_series 함수의 프로토타입이 선언되어 있습니다. 따라서 이를 포함시킨 소스 파일에서 그 존재를 알 수 있죠. extern "C" 코드블록 및 __cplusplus 식별자가 있는것도 볼 수 있는데, 이는 C언어와 C++ 소스파일 모두에서 사용가능한 범용 헤더파일을 만들기 위한 것입니다.
마지막으로 sum_series 함수가 정의되어 있는 C언어 소스파일입니다.
SumSeries.c
#include"SumSeries.h"
double sum_series(long int n_min,
long int n_max,
double (*ptr_term)(long int)) {
double sum = 0.;
long int i;
for (i = n_min; i <= n_max; i++) {
sum += (*ptr_term)(i);
}
return sum;
}
소스 파일과 헤더 파일들이 프로젝트에 포함되고 나면, 프로그램을 빌드할 수 있습니다. 가운데 있는 드롭다운 메뉴를 통해 32비트 프로그램인지, 64비트 프로그램인지의 여부를 결정할 수 있습니다.
소스 및 헤더 파일들로부터 프로그램을 빌드하여 실행파일을 얻기 위해서는, 맨 위의 빌드 (Build) 탭에서 솔루션 빌드 (Build Solution)를 클릭하면 되겠습니다.
코드에 문제가 없다면, 빌드결과를 출력하는 창에 성공했다는 메시지가 뜹니다. 만약 뭔가 잘못된 것이 있다면 에러 메시지가 나오겠죠. 빌드가 완료된 프로그램을 실행하기 위해서는 디버그 (Debug) 탭의 디버깅하지 않고 시작 (Start Without Debugging)을 클릭하면 됩니다.
프로그램을 실행시키면 터미널이 하나 등장합니다. 우리가 printf 등의 출력함수에 인자로 넣은 내용들이 여기에 출력되죠. 표준 출력 포인터 stdout을 인자로 주고 fprintf 함수를 호출하는 경우에도 마찬가지입니다.
여기서는 간단한 프로그램을 예시로 들었습니다만, 외부 라이브러리를 사용하면 더 다양한 기능을 가진 프로그램을 만들 수 있습니다. 비주얼 스튜디오에서 사용할 수 있는 C/C++ 라이브러리를 설치하고 관리하기 위한 유틸리티로는 vcpkg가 있는데요. 더 자세한 사항은 다음 포스팅에 소개되어 있습니다.
비주얼 스튜디오에서는 별도의 라이브러리를 제작하는 기능도 제공하고 있습니다. 자주 사용하는 함수들을 모아서 라이브러리 파일로 만들어두면, 편리하게 프로그램을 작성할 수 있겠죠. 이 방법에 대한 자세한 사항은 다음 포스팅에 소개되어 있습니다.
앞에서 나온 예제 프로그램에서 함수의 포인터가 등장하는데요. 이는 변수가 아닌 함수 자체를 다른 함수의 인자 (매개변수)로 넘겨주기 위한 방법 중의 하나입니다. 더 자세한 내용이 궁금하신 분들은 다음 포스팅이 큰 도움이 되리라 생각합니다.
헤더 파일을 작성하는데 있어서 C언어와 C++ 소스 파일 모두에서 사용가능하게 하려면, extern "C" 코드블록과 __cplusplus 식별자가 필요하다고 앞에서 이야기했었습니다. 이렇게 하면 C언어 소스 파일에서 정의된 함수를 C++ 소스 코드에서 호출할 수 있게 되죠. 이 방법에 대한 자세한 내용은 다음 포스팅에 소개되어 있습니다.