C프로그래밍 12강 - 구조체와 공용체(2)
구조체를 함수에 값 또는 포인터로 전달하는 방식의 차이를 이해하고 큰 구조체를 효율적으로 처리하는 방법을 익힌다. 구조체 비트필드의 정의와 저장 규칙, 하나의 기억공간을 여러 멤버가 공유하는 공용체의 특성도 함께 정리한다.
1. 함수에 구조체 자료 전달하기
구조체형 자료를 함수에 전달하는 방법은 크게 두 가지이다. 구조체 전체를 전달하는 값에 의한 전달과 구조체의 주소를 전달하는 참조에 의한 전달이다.
| 구분 | 형식 매개변수 | 전달 내용 | 원본 변경 |
|---|---|---|---|
| 값 전달 | 구조체형 | 구조체 전체의 복사본 | 복사본 수정은 원본에 영향 없음 |
| 참조 전달 | 구조체 포인터형 | 구조체가 저장된 주소 | 포인터를 통해 원본 수정 가능 |
두 방식 모두 함수에서 구조체 멤버를 처리할 수 있지만 전달되는 자료의 양과 원본 수정 가능 여부가 다르다. 구조체가 클수록 전체 복사 대신 주소 하나만 전달하는 방식의 효율성이 커진다.
2. 값에 의한 구조체 전달
값 전달에서는 함수의 형식 매개변수를 구조체형으로 선언한다. 호출할 때 구조체 전체가 형식 매개변수로 복사되므로 함수가 받는 구조체는 실 매개변수의 복사본이다.
struct num {
int x, y, sum, mul;
};
struct num calc(struct num number2)
{
number2.sum = number2.x + number2.y;
number2.mul = number2.x * number2.y;
return number2;
}
struct num number1;
number1.x = 10;
number1.y = 20;
number1 = calc(number1);
calc는 복사본 number2의 sum과 mul을 계산하여 구조체 전체를 반환한다. 반환값을 number1에 다시 대입하면 결과는 x=10, y=20, sum=30, mul=200이 된다.
값 전달은 함수 안에서 복사본을 바꾸므로 원본이 보존된다는 장점이 있다. 그러나 큰 구조체는 전체를 복사하는 데 시간이 들고 복사본을 위한 기억공간도 많이 필요해 비효율적일 수 있다.
3. 참조에 의한 구조체 전달
참조 전달에서는 형식 매개변수를 구조체 포인터형으로 선언하고 호출할 때 구조체 변수의 주소를 넘긴다. 실제로 복사되는 것은 주소 하나이며 함수는 화살표 연산자로 원본 멤버에 접근한다.
struct num *calc(struct num *number2)
{
number2->sum = number2->x + number2->y;
number2->mul = number2->x * number2->y;
return number2;
}
struct num number1;
number1.x = 10;
number1.y = 20;
calc(&number1);
함수에 &number1을 전달하므로 calc의 number2는 원본 구조체를 가리킨다. sum과 mul을 수정하면 number1에 바로 반영되어 같은 출력 결과 30과 200을 얻는다.
구조체의 크기와 관계없이 주소 하나만 전달하므로 큰 구조체는 포인터 전달이 전체 복사보다 시간과 기억공간 면에서 효율적이다.
4. 구조체 비트필드
비트필드(bit field)는 구조체 멤버의 기억공간을 비트 단위로 지정하여 자료를 저장하게 한 것이다. 필요한 비트만 사용하여 기억공간을 절약하거나 특정 상태와 조건을 나타내는 플래그를 표현할 때 활용한다.
struct 구조체명 {
자료형 멤버명 : 비트크기;
};
비트필드 멤버의 자료형은 기본적으로 int 또는 unsigned int를 사용하며 컴파일러 구현에 따라 다른 정수형도 가능하다. 실수형, 포인터형, 사용자 정의형, 배열은 사용할 수 없고, 한 비트필드의 크기는 지정한 자료형의 크기 이내여야 한다.
struct nibble {
unsigned int a : 1;
unsigned int b : 2;
unsigned int c : 1;
};
a, b, c는 하나의 unsigned int 접근단위 안에서 각각 1비트, 2비트, 1비트를 사용한다. 강의 예에서는 sizeof(struct nibble)이 sizeof(int)와 같다. 기초 자료형을 unsigned short로 선언하면 예시 크기도 short 단위가 된다.
5. 비트필드의 값과 저장 규칙
비트 크기와 값
struct test {
unsigned short a : 4;
unsigned short b : 2;
unsigned short c : 1;
unsigned short d : 7;
};
struct test bit = {3, 2, 1, 4};
각 멤버는 지정한 비트 수 안에서 값을 저장한다. 이후 bit.a=10, bit.b=3, bit.c=0처럼 점 연산자로 일반 구조체 멤버와 동일하게 참조할 수 있다.
접근단위와 배치
하나의 비트필드는 두 접근단위에 걸쳐 배치되지 않는다. 남은 비트가 부족하면 현재 접근단위의 나머지를 비워 두고 다음 접근단위에서 시작한다.
| 선언 형태 | 저장 효과 |
|---|---|
이름 없는 비트필드 unsigned short : 3; | 앞뒤 비트필드 사이에 3비트 패딩 배치 |
크기 0인 비트필드 unsigned short : 0; | 현재 접근단위를 건너뛰고 다음 접근단위에서 이후 멤버 시작 |
| 기초 자료형이 다른 비트필드 | 새로운 크기의 접근단위에 배치 |
비트필드의 실제 배치 세부는 컴파일러 구현과 접근단위의 영향을 받는다. 강의에서는 경계를 넘지 않는 배치, 이름 없는 패딩, 크기 0의 경계 이동을 구분한다.
6. 공용체의 개념과 기억공간
공용체(union)는 다수의 멤버로 구성되는 사용자 정의 자료형이라는 점에서는 구조체와 비슷하지만, 모든 멤버가 하나의 기억공간을 공유한다.
| 구분 | 구조체 | 공용체 |
|---|---|---|
| 멤버 공간 | 각 멤버가 독립적인 기억공간을 가짐 | 모든 멤버가 하나의 기억공간을 공유 |
| 자료 보관 | 여러 멤버의 값을 동시에 보관 | 공유 공간을 각 멤버의 자료형으로 사용 |
| 기본 크기 | 멤버 배치와 정렬을 고려 | 가장 큰 멤버를 저장할 수 있는 크기 |
union Var {
char a;
int b;
double c;
};
char가 1바이트, int가 4바이트, double이 8바이트인 강의 예에서 공용체 Var는 가장 큰 double을 저장할 수 있는 8바이트를 차지한다. a, b, c는 서로 별도 공간이 아니라 같은 8바이트 영역을 각자의 자료형으로 해석한다.
공용체 크기는 모든 멤버 크기의 합이 아니라 가장 큰 멤버를 저장할 수 있는 크기를 기준으로 한다.
7. 공용체 정의·선언과 멤버 접근
union Var {
char a;
int b;
double c;
};
union Var abc;
공용체 정의는 union 공용체명 { 멤버들 }; 형식이고 변수는 union 공용체명 변수명;으로 선언한다. 일반 공용체 변수의 멤버는 점 연산자로 접근한다.
abc.a = 'A';
abc.b = 66;
abc.c = 1234.5678;
세 멤버가 같은 공간을 공유하므로 한 멤버에 새 값을 저장하면 그 비트 배열을 다른 멤버형으로 해석한 결과도 달라진다. 여러 멤버의 독립적인 값을 동시에 유지하는 구조체와 다르다.
공용체 포인터
union Var *p = &abc;
p->a = 'A';
p->b = 66;
p->c = 1234.5678;
공용체 포인터에 대해서는 구조체 포인터와 마찬가지로 화살표 연산자 ->를 사용할 수 있다. 강의의 union test 예에서 short, float, double 멤버가 한 공간을 공유하며 전체 크기는 가장 큰 double에 맞춘 8바이트이다.
핵심 개념 정리
- 구조체를 함수에 전달할 때 전체 구조체를 복사하거나 구조체 포인터를 전달할 수 있다.
- 값 전달의 형식 매개변수는 복사본이므로 원본이 보존되지만 큰 구조체에서는 비용이 크다.
- 포인터 전달은 주소 하나만 복사하고 함수에서 원본 구조체를 수정할 수 있다.
- 비트필드는 구조체 멤버의 공간을 비트 단위로 지정하여 기억공간 절약과 플래그 표현에 사용한다.
- 이름 없는 비트필드는 패딩을 만들고 크기 0인 비트필드는 다음 접근단위로 이동시킨다.
- 공용체의 모든 멤버는 하나의 기억공간을 공유하며 크기는 가장 큰 멤버를 저장할 수 있어야 한다.
- 공용체 변수는 점 연산자, 공용체 포인터는 화살표 연산자로 멤버에 접근한다.
최종 정리: 큰 구조체는 주소만 전달하는 방식이 효율적이며, 비트필드는 비트 단위 공간을 설계하는 구조체 멤버이다. 구조체가 멤버별 독립 공간을 갖는 것과 달리 공용체는 같은 공간을 공유하므로 마지막으로 저장한 멤버의 비트 표현이 공유 공간 전체에 반영된다.
예상문제 20선
1. 구조체를 값으로 함수에 전달할 때 복사되는 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
값 전달은 실 매개변수인 구조체 전체를 형식 매개변수에 복사한다.
2. 값 전달에서 함수가 형식 매개변수를 수정하면?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
형식 매개변수는 실 매개변수의 복사본이므로 원본과 별개이다.
3. 큰 구조체의 값 전달이 비효율적일 수 있는 이유는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
구조체 용량이 크면 복사 시간과 복사본 저장 공간의 부담이 증가한다.
4. 참조 방식에서 함수의 형식 매개변수 자료형은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
구조체의 주소를 받기 위해 형식 매개변수를 구조체 포인터형으로 선언한다.
5. calc(&number1)에서 전달되는 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
주소 연산자 &가 구조체 변수 number1의 주소를 구한다.
6. 구조체 포인터 number2의 sum 멤버 접근식은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
구조체 포인터의 멤버는 화살표 연산자로 참조한다.
7. 큰 구조체에서 포인터 전달이 효율적인 이유는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
전체 구조체 대신 포인터 값 하나를 복사하므로 전달 비용이 작다.
8. 비트필드의 정의는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
비트필드는 멤버별로 사용할 비트 수를 직접 지정한다.
9. 비트필드의 주요 사용 목적은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
필요한 비트만 쓰고 상태나 조건을 비트 플래그로 표현할 수 있다.
10. 기본 비트필드 멤버에 사용할 수 있는 자료형은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
강의는 int 또는 unsigned int를 기본으로 제시하며 구현에 따라 다른 정수형도 가능하다고 설명한다.
11. 비트필드에 사용할 수 없는 자료형은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
실수형, 포인터형, 사용자 정의형, 배열은 비트필드 멤버 자료형으로 사용할 수 없다.
12. 이름 없는 unsigned short : 3; 비트필드의 역할은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
멤버 이름이 없는 비트필드는 해당 크기의 빈 비트 공간을 만든다.
13. 크기가 0인 이름 없는 비트필드의 효과는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
현재 접근단위의 나머지를 건너뛰고 뒤 멤버를 다음 접근단위에 배치한다.
14. 하나의 비트필드가 접근단위 경계를 넘게 될 때의 배치는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
강의에서는 하나의 비트필드가 두 접근단위에 걸쳐 배치되지 않는다고 설명한다.
15. 공용체에 대한 설명으로 옳은 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
공용체의 핵심은 여러 멤버가 동일한 기억공간을 공유하는 것이다.
16. 공용체와 구조체의 차이로 옳은 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
구조체는 여러 값을 함께 보관하지만 공용체는 동일 공간을 여러 형으로 사용한다.
17. char 1, int 4, double 8바이트인 union Var의 크기는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
가장 큰 멤버 double을 저장할 수 있는 8바이트 공간을 공유한다.
18. 공용체 변수 abc의 b 멤버 접근식은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
일반 공용체 변수의 멤버는 구조체 변수처럼 점 연산자로 접근한다.
19. 공용체 포인터 p의 c 멤버 접근식은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
공용체 포인터는 화살표 연산자로 가리키는 공용체의 멤버에 접근한다.
20. 공용체에서 한 멤버에 새 값을 저장하면?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
모든 멤버가 같은 공간을 사용하므로 새 멤버 저장은 공유 영역의 내용을 바꾼다.
댓글
댓글 쓰기