본문 바로가기
c언어

c언어 구조체와 함수와의 관계 알아보기

by 개발자 L 2023. 1. 30.
반응형

c언어 구조체와 함수와의 관계 알아보기

네 안녕하세요, 이번 포스팅에서는 구조체와 함수와의 관계에 대하여 알아보도록 하겠습니다.

우리가 구조체를 사용을 하면서 이러한 의문점들을 가질 수 있을 거라 생각을 합니다.

  • 함수의 인수로 구조체를 넘길 수 있을까?
  • 함수에서 반환값을 받을 때 구조체를 반환할 수 있을까?

일단 결론부터 말씀 드리자면 둘 다 가능합니다.

그래서 이들을 어떻게 하면 쓸 수 있는지 알아보려 합니다.

그럼 지금부터 시작하도록 하겠습니다.

 

1. 구조체를 함수의 인수로 넘기는 방법

우리가 구조체를 함수의 인수로 넘길 경우 역시 일반 자료형을 함수의 인수로 넘기는 것과 별반 다르지 않습니다.

다만, 구조체의 크기에 따라서 함수의 인자로 넘어가는 시간이 좀 더 걸릴 수 있습니다.

한 가지 예를 들어보도록 하겠습니다.

두 명의 학생들의 데이터를 받아서 학번이 같을 경우 동일한 학생이라고 판정하는 함수를 한 번 작성을 해보도록 하겠습니다.

int equal_number(struct student s1, struct student s2) // 학번 동일 여부 판별 함수
{
    if(s1.number == s2.number)
    {
        return 1;
    }
    
    else
    {
        return 0;
    }
}

int main()
{
    struct student a = {1, "Kim", 3.8};
    struct student b = {2, "Lee", 2.6};
    
    if(equal_number(a, b) == 1)
    {
        printf("같은 학생입니다.\n");
    }
    
    else
    {
        printf("다른 학생입니다.\n");
    }
    
    return 0;
}

구조체가 있다는 가정 하에 한 번 이렇게 작성을 해봤습니다.

여기서 보시면 알 수 있듯이,

학번이 같다면 1을 반환하고, 다르면 0을 반환하도록 만들었습니다.

이런 식으로 일반적인 방법으로 해도 반환을 여타 자료형들과 같이 받을 수 있지만,

구조체에는 여러가지 자료형과 변수들이 저장이 되기 때문에 천차만별인 자료형이 들어갈 때마다

그 자료형의 크기만큼 구조체의 메모리 크기가 커지기 때문에 데이터 처리 시간이 더 커질 수 있습니다.

그래서 대체적으로는 구조체를 직접 복사하여 보내는 방법보다는

구조체의 포인터를 인수로 보내는 방법을 택합니다.

그 이유는 포인터는 포인터 할당 메모리인 4바이트 그 이상 그 이하로도 변하지 않고 항상 일정하기 때문에

훨씬 빠르고 안정적이기 때문입니다.

그럼 위에 작성한 함수를 한 번 수정을 해보도록 하겠습니다.

int equal_number(struct student *p1, struct student *p2) // 학번 동일 여부 판별 함수
{
    if(p1 -> number == p2 -> number)
    {
        return 1;
    }
    
    else
    {
        return 0;
    }
}

int main()
{
    struct student a = {1, "Kim", 3.8};
    struct student b = {2, "Lee", 2.6};
    
    if(equal_number(&a, &b) == 1)
    {
        printf("같은 학생입니다.\n");
    }
    
    else
    {
        printf("다른 학생입니다.\n");
    }
    
    return 0;
}

이렇게 간접 멤버 연산자로 바꿔주고,

'main' 함수 내에서 받아지는 인자를 a와 b의 주소로 받아 간접 참조를 할 수 있도록 해뒀습니다.

하지만 포인터를 사용하는 방법 역시 단점이 존재합니다.

메모리의 크기가 일정하기 때문에 처리가 빠르지만,

반대로 포인터를 잘못 쓰게 된다면 데이터를 훼손시키는 결과 역시 나올 수 있기 때문입니다.

그래서 저의 경우는 처음 작성할 때는 직접 받는 함수를 먼저 작성을 하고,

그 후에 메모리 공간 할당을 줄이기 위해서 포인터로 받는 함수로 일부 수정을 하는 방법을 권해드립니다.

그렇게 하면 실수를 하는 경우도 훨씬 줄어들기 때문입니다.

 

1 - 1. 포인터를 사용할 때 원본 훼손을 방지하는 방법

물론, 원본을 훼손되는 것을 막는 방법 역시 존재합니다.

원본을 포인터를 통하여 읽기만 하고 쓸 필요는 없을 경우에는 매개변수를 상수화 시켜주는 키워드인 'const'를 써줍니다.

그렇게 하면 매개변수가 상수가 되어버렸기 때문에 포인터가 원본에 접근하여 값을 변경하려는 현상이 일어날 때,

데이터의 훼손 대신 에러가 나면서 막아주게 됩니다.

int equal_number(struct student const *p1, struct student const *p2) // p1, p2를 통하여 구조체 원본 변경 금지
{
    if(p1 -> number == p2 -> number)
    {
        return 1;
    }
    
    else
    {
        return 0;
    }
}

이런 식으로 쓰시면 됩니다.

반응형

 

2. 구조체를 함수의 반환값으로 넘기는 방법

구조체를 함수의 반환값으로 넘길 때에도 함수의 인자로 넘어갈 때처럼 복사본이 전달이 됩니다.

그리고 구조체를 반환값으로 사용을 하게 되면 한 번에 여러 개의 값을 반환할 수 있어서 편리합니다.

사용 방법은 이렇습니다.

struct student create()
{
    struct student s;
    s.number = 3;
    strcpy(s.name = "Lee");
    s.grade = 3.0;
    return s; // 구조체 s가 반환이 되면서 main 함수에 있는 변수 a로 복사가 됨.
}

int main()
{
    struct student a;
    
    a = create(); // 반환되어진 구조체 참조
    
    return 0;
}

이렇게 사용을 하시면 됩니다.

 

지금까지 구조체와 함수와의 관계에 대해서 알아보았습니다.

다음 포스팅에서는 공용체에 대하여 알아보도록 하겠습니다.

긴 글 읽어주신 독자분들께 진심으로 감사합니다~

반응형

댓글