ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [C언어/자료구조] 전화번호부 v5.0 (인프런)
    CS/자료구조&알고리즘 2020. 1. 14. 07:00

    ※ 인프런 무료강좌 C로 배우는 자료구조(권오흠 교수님)를 보고 개인적인 복습을 위해 정리한 내용입니다.


     

    전화번호부 v5.0의 개선사항

    https://skm1104.tistory.com/29 전화번호부 v1.0

    https://skm1104.tistory.com/30 전화번호부 v2.0

    https://skm1104.tistory.com/31 전화번호부 v3.0

    https://skm1104.tistory.com/32 전화번호부 v4.0

     

    v4.0에서부터 구조체 배열을 만들어 데이터를 담는 데 사용했다.

    그런데 C 프로그래밍에서 구조체 배열을 만들어 사용하는 것은 일반적인 경우는 아니다. 구조체의 크기가 클 경우 비효율적일 수 있기 때문이다.

     

    C언어에서는 함수를 호출할 때 매개변수의 전달방식이 call-by-value (값에 의한 전달방식)이다.

    따라서 호출할 때 넘겨주는 매개변수(실매개변수)와, 함수에서 전달받은 인자(형식매개변수)는 서로 다른 별개의 변수이다.

    (변수가 할당된 메모리 주소를 직접 전달한 것이 아니라 값을 복사해서 전달했기 때문. )

     

    C언어에서는 구조체 변수 간의 치환문을 지원하기 때문에, 구조체 변수를 매개변수로 쓰거나 구조체 변수를 리턴할 수 있다.

    구조체를 매개변수로 넘기면, 호출 시에 구조체의 모든 멤버들이 복사되어 전달되기 때문에

    복사되는 데이터의 양이 많다.

    구조체의 크기가 커질수록 이러한 방식은 비효율적이게 된다.

     

    구조체 변수를 리턴하는 경우에도 마찬가지로 모든 멤버들이 복사되어 전달된다.

    그런데 return을 할 때는 매개변수로 전달할 때와 달리

    이름 없는 임시 객체에 먼저 복사가 되고,

    함수가 return 되어 사라지고,

    그 다음에 치환문에 의해 임시객체에서 한번 더 복사되면서 최종적으로 전달이 된다.

    이렇게 두 번 복사가 되기 때문에 매개변수로 전달할 때보다도 비효율성 문제가 더 심각하다.

     

    우리가 만들었던 전화번호부 v4.0의 add함수나 remove함수에서는

    알파벳 순 정렬을 유지하기 위해

    사람을 추가하거나 삭제할 때마다 모든 구조체들이 다음 칸으로

    복사되며 한칸씩 옮겨진다.

    ( directory[i+1] = directory[i] )

    이 때도 모든 멤버들이 각각 복사되어 전달되기 때문에 복사되는 데이터의 양이 많다.

     

    이러한 문제를 해결하기 위해서,

    v5.0에서는 구조체에 대한 포인터와 동적 메모리 할당을 이용할 것이다.

     

     

    구조체 포인터

    v4.0의 구조체는

    Person directory[CAPACITY]; 로 선언했다.

    이렇게 선언하면 구조체 배열의 각 칸에 Person 타입의 구조체들이 담긴다.

     

    v5.0에서는

    Person * directory[CAPACITY]; 와 같이 포인터로 선언할 것이다.

    그러면 구조체 배열의 각 칸에 Person 타입의 구조체 자체가 아니라, 각 구조체의 주소값이 담긴다.

     

    구조체를 포인터로 선언했기 때문에, 구조체를 매개변수나 return문으로 주고받는 함수들의 수정이 필요하다.

    구조체를 매개변수로 주고받는 status 함수와 print_person 함수를 보자.

     

    status 함수

    void status() {
    	int i;
    	for (i=0; i<n; i++) 
    		print_person(directory[i]);
    	printf("Total %d persons. \n", n);
    }

    status 함수는 그대로이다.

    그러나 status 함수에서 directory[i]를 매개변수로 넘겨받은 print_person 함수는 다음과 같이 수정해야한다.

     

    print_person 함수

    void print_person(Person * p) {
    	printf("%s:\n", (*p).name);
    	printf("    Phone: %s:\n", (*p).number);
    	printf("    Email: %s:\n", (*p).email);
    	printf("    Group: %s:\n", (*p).group);
    }

    매개변수는 (Person p)가 아니라 (Person *p)로 포인터 형식으로 받는다.

    그리고 p가 포인터이므로 printf문에서도 p.number가 아니라 (*p).number로 출력해야한다.

     

    구조체 포인터로 전달을 하는 경우에도

    call-by-value 방식으로 값이 복사되어 전달되는 것은 변하지 않는다.

    그런데 이 때 구조체 포인터로 선언을 한 directory[i] 에는 구조체 자체가 담겨있는 것이 아니라, 구조체의 주소값이 담겨있다.

    그래서 v4.0 와는 달리, 구조체의 모든 멤버들이 일일히 복사되는 것이 아니라 구조체의 주소값만 복사된다.

     

    .연산자가 *연산자보다 우선순위가 높기 때문에,

    *p.name 처럼 작성하면 오류가 발생한다. 반드시 (*p).name 과 같이 괄호를 씌워야 한다.

    (*p).name 대신에 - > 연산자를 사용해서 p- > name 처럼 간결하게 쓸 수도 있다.

    이 화살표 연산자는 C 프로그래밍에서 자주 사용되므로, 익숙해지는 것이 좋다.

     

     

    v4.0에서는 구조체의 모든 멤버들을 일일히 복사해서 옮겼던 add 함수와 remove 함수도

    이렇게 구조체 포인터를 사용하면, 포인터에 담긴 주소값만 복사해서 옮기면 되기 때문에 복사할 데이터의 양을 많이 줄일 수 있다.

     

     

    댓글

Designed by Tistory.