바로 전 전화번호부 알고리즘 version 4.0을 떠올려 봅시다. 위 그림과 같이 4개의 field를 가지는 struct person을 정의한 후 Person type의 directory 배열을 만들어 사용하였습니다. 이 배열의 각 칸이 구조체인 구조입니다. 예를 들어 특정 칸의 특정 멤버정보를 가져올려면 directory[2].number = ~ 이런식으로 표기 하였습니다. 하지만 일반적으로 c프로그래밍에서 이런식의 구조체 배열을 사용하는 것은 일반적인 스타일이 아닙니다. (효율적이지 않습니다.)
예를 들어 version 4.0의 status() 함수에서 print_person함수를 호출하는 과정을 위 그림에서 살펴봅시다.
c언어에서 함수를 호출할때 값에 의한 호출 (call by value) 방식을 사용합니다. 이 과정에서 함수를 호출할때 실매개변수와 호출되는 함수에 있는 형식매개변수는 사실 별개의 다른 변수입니다.
위의 print_person(directory[i])에서 directory[i]와 print_person(Person p)에서 Person p의 관계를 살펴봅시다. 함수가 호출되면 이 함수에서 p라는 지역변수가 만들어지고 실매개변수의 값이 형식매개변수값으로 복사가 됩니다. 복사되는 벼수가 정수,실수가 아니라 구조체 변수 즉, 구조체 안에 모든 멤버들이 복사됩니다. p = directory[i] 와 같은 치환문을 생각하면 쉬운데 이 치환문처럼 복사가 되는 것은 구조체 안에 여러개의 값을 가진 변수 각각의 값이 따로 복사가 되는 것입니다. >> 복사되는 양이 많고 구조체의 덩치가 커지면 곤란합니다.
위의 그림을 살펴봅시다. 앞서 이러난 일은 매개변수의 전달에서만 해당되는 것이 아닙니다. 함수에서 마지막에 리턴하는 구절에서도 마찬가지로 복사가 발생합니다. 구조체 변수 directory[i] 자체를 리턴하는 경우 이 구조체가 thePerson변수로 복사가 됩니다. 엄밀하게 말하면 리턴문에서는 더욱더 심각한데 어떤 함수가 어떤 데이터를 리턴하면 즉시 복사가 되지않고 이름이 없는 임시 객체에 복사가 한번된 다음 함수내 데이터가 사라지고 이때 다시 임시객체에 복사된 데이터가 thePerson변수에 한번 더 복사 됩니다.
또 다른 예를 살펴보면 어떤 칸에 새로운 구조체를 삽입하려면 다음의 모든 구조체들이 다음칸으로 복사되야합니다. 또한 하나하나의 구조체안의 멤버또한 복사됩니다. 즉, 복사되는 양이 점점 들어날 것입니다.
구조체의 배열을 사용하게되면 이런일을 피할 수 없습니다.
그래서 전화번호부 알고리즘 version5.0에서는 구조체의 배열을 사용하지 않고 구조체의 포인터들의 배열을 사용합니다. 위의 그림을 살펴봅시다. struct person은 똑같지만 배열 directory의 type이 그냥 Person type이 아니라 Person * type입니다. 이것이 어떤 것을 의미 할까요 ? 앞의 Person이 의미하는 것은 배열의 각 칸의 type을 의미하는것인데 version4.0에서는 배열의 각 칸의 type이 구조체 Person이라는 의미지만 version5.0에서는 배열이름은 똑같은 directory지만 배열의 타입이 Person에 대한 포인터 type입니다. 배열 directory의 각 칸 그자체가 Person type(이름 넘버 주소 등)이 아니라 이것은 그냥 하나의 포인터일 뿐이고 그것은 하나의 구조체 변수의 주소를 저장하는 포인터 type의 배열입니다.
status() 함수에서 print_person()함수를 호출하는 경우를 다시한번 살펴봅시다.
배열 directory가 Person type의 배열이 아니라 Person에 대한 포인터 type의 directory라면 이 directory 각 칸 안에는 이름 번호 등이 저장되는것이 아니라 이값들은 다른데 저장이 되고 이 값들의 주소를 저장하는 식으로 되어 있습니다. 이러면 코드의 수정이 필요한데, print_person의 매개변수 타입이 Person *p로 수정되야 합니다.
이 경우에도 call by value라는 기본적인 매개변수 전달 방식이 달라지는 것은 아닙니다. c에서 실매개변수 directory[2]와 형식매개변수 p는 별개의 변수입니다. 다만 호출하는 순간 directory[i]값이 p로 복사가 되는 것일뿐 입니다. 즉, directory[2]가 가리키는 주소가 p로 복사되는 것입니다. 복사되는것은 이름 번호 등 이런값이 아니라 단지 주소일 뿐 입니다. 이렇게 되면 복사되는 정보의 양이 훨씬 줄어듭니다. 구조체의 덩치가 커진다고 해서 주소값 자체가 커지는건 아니기 때문입니다.
포인터로부터 그 포인터가 가리키고 있는 변수의 값을 출력하기 위해 *연산자를 사용했습니다. 즉, 우리가 출력해야 할 것은 *p입니다. 단일 변수가 아니라 구조체이므로 그 구조체 안에 있는 멤버를 호출하기 위해서는 괄호를 사용해 (*p).name 식으로 표기해야 합니다. 괄호는 필수적으로 사용해야하는데 .연산자가 *연산자보다 우선순위에서 높기 때문입니다.
앞으로 c프로그래밍을 할때 가장 많이 사용하는 연산자 중 하나가 (*p).name 이런 표기인데 이 연산자는 p ->name이라는 새로운 연산자로 대체합니다.
'Language > 자료구조' 카테고리의 다른 글
[11] C review < 연결리스트의 추가 > (0) | 2021.11.26 |
---|---|
[10] C review < 연결리스트의 개념 > (0) | 2021.11.25 |
[7] C review < 전화번호부 알고리즘 version4.0 > (0) | 2021.11.23 |
[6] C review < 전화번호부 알고리즘 version3.0 > (0) | 2021.11.22 |
[5] C review < 전화번호부 알고리즘 version2.0 > (0) | 2021.11.22 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!