W.IT/W.C언어

[c 언어] 함수(function)의 return

wbbro 2020. 10. 11. 17:36

return 이란

 c언어로 프로그래밍하면서 함수라는 요소는 필수적입니다. 그렇다면 그의 짝꿍인 return 또한 빼놓을 수 없겠죠. 

 return은 말 그래도 반환해준다는 의미입니다. 여기서 반환은 함수가 끝났다는 의미를 가지고 마지막 남기는 메시지를 반환해준다고 생각하시면 됩니다. 이때 반환되는 자료형은 함수의 타입(반환 자료형)이 됩니다.

 

return 사용 방법은 아래와 같습니다.

타입(반환 자료형) 함수 이름(매개변수)
{
    함수 내용;

    return 반환 값;
}

 

 return에 대한 간단한 코드를 작성해 예를 들어보겠습니다.

 

#include <stdio.h>

double func3()
{
    return 3.0/2;
}

char *func2()
{
    return "HELLO";
}

char func1()
{
    return 'a';
}

int main(int agrc, char *argv[])
{
    char a;
    cha *b;
    double c;
	
    a = func1;
    b = func2;
    c = func3;
	
    return 0;
}

 

 처음 main함수는 int라는 타입(반환 자료형)으로 선언되어있습니다.
 그렇다면 main함수의 마지막으로 선언되어있는 return에서 반환되는 값은 정수형 0이겠죠.

 마찬가지로 func1 함수는 char라는 타입(반환 자료형)으로 선언되어 있고 return 옆에 'a'라는 문자가 붙어있습니다.

 이 함수는 문자형으로 반환하고 그 반환 데이터는 'a'입니다.

 

 fun2 함수와 fun3 함수도 각자 char *, double이라는 타입(반환 자료형)으로 선언되어 있고 return 옆에 데이터가 붙어 있습니다. fun2 함수는 해당 데이터의 주소형(문자열의 포인터)을 반환하며 fun3은 실수형으로 반환합니다.

 

 여기서 주의할 점은 반환 형과 retrun 옆에 들어가는 데이터 형을 일치시켜줘야 합니다.

return 하기 전 함수 확인

 retrun은 그 함수의 마지막을 알리는 신호입니다. 그럼 retrun을 하기 전에 여러분은 그 함수를 점검할 필요성이 있습니다. 집을 나설 때 불을 껐는지, 가스밸브는 잠갔는지, 에어컨을 껐는지와 같은 이치라고 보시면 됩니다.

 

 그럼 저희가 확인을 해야 하는 것은 무엇일까요? 

 가장 중요한 것은 동적 할당된 메모리 값들입니다. 이 값들은 저희가 free 해주지 않으면 함수가 끝나도 계속 메모리에 남아있을 겁니다. 이상태로 지속되다 보면 언젠가 메모리에 쌓이고 쌓여 메모리 full이 나겠죠. 그러니 동적 할당한 함수의 용도가 끝났으면 꼭 free를 해주고 나와야 합니다.

 

 두 번째 확인해야 하는 것은 그 함수의 가독성입니다. 제가 main함수 설명을 하며 말씀드렸다시피 함수의 가독성은 매우 중요합니다. 그러므로 끝맺음한 이 함수의 가독성이 떨어지고 복잡하다면 그 함수를 다시 한번 정리하고 분할할 필요성이 있습니다.

 

 세 번째는 이 함수 안 에러 체크입니다. 보통 에러 체크를 하고 에러가 있을 경우 그 에러를 반환해주는 방식을 많이 사용하십니다. 그런데 그 반환 값들이 다 동일하면 어디서 에러가 났는지 코드가 길어진다면 구별하기 쉽지 않겠죠? 그러므로 다 다른 반환 값으로 반환해주었는지 확인을 해야 합니다.


 

함수의 타입(반환 자료형) void, void 포인터

 void는 비어있다는 의미입니다. 이 의미는 이 함수는 반환 값이 없다는 것입니다. 하지만 void 포인터는 그 반대로 모든 자료형 포인터로 반환이 가능합니다.

 

 이 두 가지 예를 간단한 코드를 통해 들어본다면,

 

 1. void

 

void func1()
{
    printf("비어있다.");
}

 

 이 경우에는 굳이 return 값을 주지 않아도 됩니다. 위처럼 반환 값 없이 함수를 끝내겠다는 의미이지요.

 

 2. void*

 

void *func()
{
	char *a = "ABC";
	return a;
}

int main(int argc, char *argv[])
{
	char *b = NULL;
    
	b = (char *)func();
    
    return = 1;
}

 

 위 코드는 void *func을 char *로 받아 b 값에 "ABC"를 넣겠다는 의미입니다. 여기서 중요한 건 형 변환입니다.

저희가 allocation 함수인 calloc, malloc 또한 void * 함수라 앞에 형 변환을 해주고 사용하듯이 void * 는 반환 값이 void*로 반환이 되어 이 값을 넣을 변수와 자료형이 맞게 형 변환해주는 작업이 필요합니다.


대부분의 반환 자료형

프로그래밍을 하면서 대부분(거의 모든) 반환 자료형은 아래 세 가지로 구현되어 있습니다.

1. char*
2. int
3. sturcture(*)

 

 1번 문자형 포인터이고 2번은 정수 값, 3번은 구조체 혹은 구조체 포인터입니다.

 

 프로그래밍을 하시다 보면 데이터의 크기가 프로그램의 많은 영향을 미친다는 것을 느끼실 겁니다. 그래서 저희는 프로그래밍을 할 때 데이터 크기를 알맞게 계산해서 집어넣거나 넉넉하게 잡아 집어넣는 경우가 많죠.

 

 반환 값도 마찬가지입니다. 문자형 포인터는 8byte, int 형은 4byte이며 구조체는 각자가 설정한 크기로 반환될 것입니다. 여기서 int 형까지는 데이터 크기가 넉넉하여 상관없지만 char, short 같은 데이터 크기가 작은 것은 그 범위가 넘어갈 수도 있기에 문제의 요소가 많습니다.

 그러므로 최대한 그런 자료형 반환은 지양하고 위 3개의 데이터 반환을 지향하셨으면 좋겠습니다.