본문 바로가기

awesome-c Beginner 번역/Introduction to 'fun' C

<비공식 번역>awesome-c Beginner번역: function.c

번역자가 시작하는 말..


이번 번역시리즈는 좀 짧습니다. 들어가보면 그 동안의 문장이 아닌 코드가 들어있습니다.


5개 정도의 코드가 있고 이 코드 외에는 큰 설명은 없습니다.


그래서 이번 번역은 코드를 붙여놓고 각 코드를 해석하는 형태로 진행하겠습니다.


적지만 읽어주시는 분들에게 감사드립니다.



functions.c


/**

* This are a collection of examples for C 201.

* These combine concepts you may or may not be

* familiar with and are especially useful for

* students new to C. There is a lot of really

* cool stuff you can do in C without any cool

* languages.

*

* This is file in particular is an introduction

* to fun function usage in C.

*/


#include <stdio.h>


int sum(int a, int b) {

 return a + b;

}


int sub(int a, int b) {

 return a - b;

}


// This function "get_operator" takes a *char expression

// and returns a function that takes two ints and returns

// an int.

int(*get_operator(char* expression)) (int, int) {

 int i;

 // char pointers are automatically given a final character '\0'

 // to allow us to know when the char* ends.

 for (i = 0; expression[i] != '\0'; i++) {

  switch (expression[i]) {

  case '+':

   return sum;

  case '-':

   return sub;

  }

 }

}


void print_operator(char* expression) {

 // get_operator will return a function that takes two ints

 // and returns an int.

 int(*operator)(int, int) = get_operator(expression);

 // sum is automatically converted to a pointer,

 // you could also say "operator == &sum", but that is longer.

 if (operator == sum) { // comparing functions!

  printf("Expression %s is a sum.\n", expression);

 }
 else if (operator == sub) { // comparing functions again!!

  printf("Expression %s is a sub.\n", expression);

 }
 else {

  printf("Expression %s has an unknown operation.\n", expression);

 }

 // Challenge:

 // Instead of just printing out which operation it is,

 // find the two operands and perform the operation on them.

 // Then print the result of the expression instead.

}


int main() {

 char* expression1 = "2 + 2";

 char* expression2 = "5 - 3";

 char* expression3 = "9 * 7";


 print_operator(expression1);

 print_operator(expression2);

 print_operator(expression3);

}


코드를 하나씩 분석해보겠습니다.

아! 참고로 이건 gcc를 사용한다고 합니다. 저는... visual studio를 사용하는 개발자란게 함정이지만
gcc를 음... 이거 윈도우 10에서 Bash Shell을 지원하는데 이걸로 할 수 있지 않을까요? 그럼 해봅시다.

 

환경 세팅중...

 

자 그럼 환경을 세팅을 하면서 코드 분석을 먼저 해보겠습니다.

눈에 띄는건 main함수를 제외한 함수가 4개가 보이는 것 같습니다.

 

그 중 두개는 정말 심플합니다.

sum, sub

int sum(int a, int b) {

return a + b;

}

 

int sub(int a, int b) {

return a - b;

}

 

이 함수는 말 그대로 들어온 인자 2개의 합과 차(a에서 b를 뺀 값 이라 정확하겠죠?)를 리턴해 줍니다.

 

다음 함수로 넘어가보죠, 함수 포인터가 들어간 get_operator입니다.

int (*get_operator(char* expression))(int, int) {

int i;

// char 포인터는 자동으로 끝에 null character를 줍니다.

// 우리는 이를 통해서 char*가 언제 끝나는지 알 수 있습니다.(좀 더 의역 : 반복문의 종료 시점으로 사용할 수 있습니다.)

for (i = 0; expression[i] != '\0'; i++) {

switch (expression[i]) {

case '+':

return sum;

case '-':

return sub;

}

}

}

 

C를 기초과정까지 배운분들은 포인터를 배웠음에도 함수 포인터는 꽤 낯설 것입니다.

저 역시도 아직 초보티가 풀풀 나는 개발자로서

함수포인터가 매우까진 아니지만 낯설게 느껴질 때가 종종 있어서,

그때마다 다시 찾아보곤 합니다. 이번에도 다시 찾아보면서 공부하겠습니다.

 

여러분은 포인터의 개념에 대해 배우셨을 겁니다.
그것이 아니라면 포인터부터 이해하고 오셔야합니다.

함수 포인터도 결국은 포인터기 때문에 꼭 이해하셔야 됩니다.

 

간단하게 포인터 변수는 C, C++에서 등장하는 개념으로 변수, 함수 등이 가지는 주소를 저장하는 변수입니다.

다른 언어에도 등장할 수 있겠지만 저는 C, C++에서 처음 배웠기에 이렇게만 설명드리겠습니다.

 

사실 포인터 변수는 어떠한 주소도 가질 수 있지만 메모리 오류란 매우매우매우매우 심각한 것이므로,

컴파일러가 자체적으로 자료형이란 것을 통해 타입검사를 통해 부적절한 메모리 접근을 사전에 검출합니다.

 

조금 더 알고싶으시다면 구글에서 함수포인터라고 검색하시고 1~4번째 검색결과를 모두 읽어보시길 추천드립니다.

 

함수 포인터란 함수도 주소를 가지고 있으니, 이 주소를 저장할 수 있는 포인터 변수가 됩니다.

그럼 지금 등장하는 포인터 함수는?

함수의 주소를 가지는데 이를 이용하여 해당 함수의 또 다른 진입로?가 되고 그 함수를 실행할 수 있는 매개체?가 됩니다.

(이러한 개념의 지적태클 받습니다! 하나의 관심을 바랍니다ㅎ)

 

그럼 함수를 자세히 살펴보겠습니다.

함수 선언 자체를 보니 조금 헷갈리겠네요.

int (*get_operator(char* expression)) (int, int)

get_operator라는 함수는 인자로 char* 인 expression을 받게 되어 있습니다.

그런데 그걸 하나로 () 묶은 후에 인자를 또 받을 수 있게 (int, int)!!!!

 

ㄷ..두번인가?!

sum, sub를 받으려면 그들과 같은 형태를 취해야할 필요가 있습니다.

그래서 실제로 받는 인자는 char* expression 이지만

(*get_operator)(char* expression) 이렇게만 선언할 경우 sum, sub를 받아서 처리할 수 없기 때문에,

이와같은 형태를 취하는 것입니다.

 

내부로 들어가보겠습니다.

반복문을 통해서 expression으로 들어온 문자열을 앞에서 하나씩 확인하는 구조군요.

'\0' null 문자를 만나면 종료됩니다.

이때, 접근한 값이 '+'로 확인되면 sum을 호출, '-'로 확인되면 sub를 호출하는 구조네요.

 

자 이제 get_operator를 사용하는 print_operator함수를 보겠습니다.

void print_operator(char* expression) {

// get_operator는 두 int를 받아서 int결과를 리턴하는 함수입니다.

int (*operator)(int, int) = get_operator(expression);

 

// sum 은 자동으로 포인터(주소)로 변환됩니다. "operator == &sum" 이라고도 말할 수 있습니다.

// 그러나 그런 표현은 너무 길어서 지양합니다.

if (operator == sum) { // 비교!

printf("Expression %s is a sum.\n", expression);

} else if (operator == sub) { // 다시 한번 더 비교!

printf("Expression %s is a sub.\n", exrpession);

} else {

printf("Expression %s has an unkown operation.\n", expression);

}

 

// 첼린지:

// 위의 내용이 어떤 결과가 나오는지 화면에 찍어보기 전에,

// 두개의 인자를 찾아서 실행해보세요.

// 그리고 이 표현식의 결과를 화면에 출력해보세요.

}

 

먼저 함수 포인터를 선언하여 get_operator(expression) 함수를 받습니다.

그리고 이 operator가 sum이냐 sub냐에 따라서 들어온 string을 출력해줍니다.

 

이제 모든 함수 설명은 끝났습니다. 이제 메인을 보고 직접 실행해보도록 하죠.

메인에서는 2+2, 5-3, 9*7을 각각 print_operator에 넣어주죠.
그러면 각 string은 get_operator에 의해서 판독 될 것입니다.

그리고 해당함수가 어떤 값을 가지는지 넘겨주겠죠?

 

아까 준비하던 windows 10 Cmd에서 bash쉘이 준비됐습니다! 크으!

코드를 컴파일하고 실행하면!

 

 

짜잔! 이렇게 됩니다. 깔끔하죠?

혹시 sum, sub 함수를 보고 계산결과를 원하시는 분이 있다면

(아마도 직접 컴파일하지 않고 머릿속으로 대충 예상한 분!, 저 라던가, 저 라던가...) 

자세히 분석해본 결과 사실은 해당함수의 결과를 보려면 따로 실행해야 함을 수 있습니다.

 

여기까지가 function.c의 해설입니다.

도움이 되셨을지 모르겠네요.

지금껏 문서의 번역만 진행하다가 처음으로 코드의 해설로 넘어가서 꽤나 어색하고

어떻게 설명할지 난감합니다만

조금 더 자세하고 이해하기 쉬운 설명을 달 수 있도록 노력하겠습니다.

 

감사합니다ㅎ

 

다음은 function2.c 입니다!

 

 

출처1 : https://github.com/aleksandar-todorovic/awesome-c

출처2 : https://gist.github.com/eatonphil/21b3d6569f24ad164365