3. 구조체와 함수
구조체를 함수 인자로 전달
- 구조체 변수는 함수 인자로 사용할 수 있다.
- 이때 값에 의한 호출(pass by value)이 적용되어 구조체의 전체 값이 복사된다.
- 구조체 크기가 클 경우, 복사에 의한 메모리 낭비가 발생할 수 있다.
구조체 포인터를 함수 인자로 전달
- 구조체의 주소값을 함수로 전달하면, 함수 내부에서 원본 데이터를 직접 조작할 수 있다.
- 이를 통해 메모리를 효율적으로 사용할 수 있고, 함수 내 수정도 가능하다.
- 접근 시에는 -> 연산자(간접 멤버 접근 연산자)를 사용한다.
함수의 반환값으로 구조체 사용
- 함수가 구조체를 반환할 수 있다.
- 반환 시 구조체 값이 복사되어 전달된다.
💻 예제 코드 - 구조체를 함수 인자로 전달
#include <stdio.h>
#include <string.h>
struct Student {
int id;
char name[20];
};
// 값에 의한 호출
void printStudent(struct Student s) {
printf("ID: %d, Name: %s\n", s.id, s.name);
}
// 주소에 의한 호출
void changeName(struct Student *s, const char *newName) {
strcpy(s->name, newName); // 구조체 포인터는 -> 연산자 사용
}
struct Student createStudent(int id, const char *name) {
struct Student temp;
temp.id = id;
strcpy(temp.name, name);
return temp;
}
int main() {
struct Student s1 = {1, "Kim"};
printStudent(s1);
changeName(&s1, "Lee");
printStudent(s1);
struct Student s2 = createStudent(2, "Park");
printStudent(s2);
return 0;
}
✅ 실행 결과:
ID: 1, Name: Kim
ID: 1, Name: Lee
ID: 2, Name: Park
4. 중첩 구조체 (Nested Structure)
- 구조체 내부에 또 다른 구조체를 포함하는 형태이다.
- 계층적인 데이터 구조를 표현할 때 유용하다.
- 점(.) 연산자를 두 번 사용하여 하위 구조체의 멤버에 접근한다.
⭕ 예: outer.inner.a
💻 예제 코드 - 중첩 구조체 사용
#include <stdio.h>
struct Position {
int x;
int y;
};
struct Rectangle {
struct Position topLeft;
struct Position bottomRight;
};
int main() {
struct Rectangle rect = {{0, 0}, {10, 10}};
printf("Top-left: (%d, %d)\n", rect.topLeft.x, rect.topLeft.y);
printf("Bottom-right: (%d, %d)\n", rect.bottomRight.x, rect.bottomRight.y);
return 0;
}
✅ 실행 결과:
Top-left: (0, 0)
Bottom-right: (10, 10)
5. 공용체 (Union)
- 여러 멤버가 하나의 메모리 공간을 공유하는 사용자 정의 자료형이다.
- 메모리를 절약할 수 있으나, 하나의 멤버만 사용할 수 있다는 제약이 있다.
- 구조체는 각 멤버마다 독립적인 메모리 공간을 갖지만, 공용체는 가장 큰 멤버의 크기만큼만 메모리를 할당받는다.
⚠️ 동시에 여러 멤버를 사용하는 코드는 예기치 않은 결과를 낼 수 있다.
💻 예제 코드 - 공용체 사용
#include <stdio.h>
union Data {
int i;
float f;
char str[10];
};
int main() {
union Data data;
data.i = 10;
printf("data.i: %d\n", data.i);
data.f = 3.14;
printf("data.f: %.2f\n", data.f);
// 이전 값은 덮어쓰기 됨
printf("data.i after assigning to f: %d\n", data.i);
return 0;
}
✅ 실행 결과:
data.i: 10
data.f: 3.14
data.i after assigning to f: 1078523331 (⚠️ 값 손상)
6. 열거형 (Enumeration)
- 관련 있는 상수들을 묶어서 정수형 값으로 정의한 사용자 정의 자료형이다.
- enum 키워드를 사용한다.
- 각 열거형 상수는 기본적으로 0부터 시작하여 1씩 증가한다.
- 직접 정수 값을 지정할 수도 있다.
💻 예제 코드 - 열거형 사용
#include <stdio.h>
enum Fruit {APPLE, BANANA, PINEAPPLE};
int main() {
enum Fruit f;
f = BANANA;
printf("Selected fruit: %d\n", f); // 출력 결과: 1
return 0;
}
✅ 실행 결과:
Selected fruit: 1
7. typedef
- 새로운 자료형 이름(alias)을 정의할 때 사용하는 키워드이다.
- 코드의 가독성을 높이고, **이식성(portability)**을 향상시킨다.
- 구조체 이름이 길어지는 것을 방지할 수 있다.
- 구조체 배열에도 적용할 수 있다.
💻 예제 코드 - typedef 사용
#include <stdio.h>
typedef int Score;
typedef struct {
char name[20];
int age;
} Person;
int main() {
Score s = 95;
printf("Score: %d\n", s);
Person group[2] = {
{"Kim", 25},
{"Lee", 30}
};
for (int i = 0; i < 2; i++) {
printf("Name: %s, Age: %d\n", group[i].name, group[i].age);
}
return 0;
}
✅ 실행 결과:
Score: 95
Name: Kim, Age: 25
Name: Lee, Age: 30
8. 비트 필드(Bit Field)
- 구조체 멤버의 크기를 비트 단위로 정의할 수 있게 하는 기능이다.
- 메모리를 절약하거나, 특정 비트 크기만큼 값을 저장해야 할 때 유용하게 사용한다.
- 하드웨어 레지스터, 프로토콜 플래그, 디지털 신호 제어 등에 사용된다.
- 구조체 내부의 멤버 변수에 :숫자 형식으로 비트 수를 지정한다.
- 주로 int나 unsigned int 형식을 사용하여 정의한다.
- 비트 필드는 보통 1바이트(8비트)보다 작은 데이터를 다룰 수 있다는 점에서 메모리 효율성이 있다.
- 비트 필드는 일반 구조체처럼 점(.) 연산자나 포인터를 사용할 경우 간접 참조 연산자(->)를 사용한다.
⚠️ 비트 단위로 나누었기 때문에 연산 시에는 자료형의 크기 제한과 연산자의 우선순위를 항상 주의해야 한다.
💻 예제 코드: 비트 필드를 활용한 구조체 선언과 출력
#include <stdio.h>
// 비트 필드를 가진 구조체 정의
struct BitField {
unsigned int reserved : 3; // 상위 3비트는 사용하지 않음
unsigned int flag1 : 1; // 1비트 크기의 플래그 변수
unsigned int flag2 : 2; // 2비트 크기의 상태 변수
int value : 4; // 4비트 정수 (음수 포함 가능)
};
int main() {
struct BitField bf;
// 값 할당
bf.reserved = 0; // 사용하지 않는 상위 비트는 0으로 고정
bf.flag1 = 1; // 1비트: 0 또는 1
bf.flag2 = 2; // 2비트: 0~3
bf.value = -5; // 4비트 signed: -8 ~ +7
// 값 출력
printf("flag1 : %u\n", bf.flag1); // 출력: 1
printf("flag2 : %u\n", bf.flag2); // 출력: 2
printf("value : %d\n", bf.value); // 출력: -5
return 0;
}
📎 예제 설명
- unsigned int reserved : 3;는 상위 3비트를 사용하지 않도록 예약하는 역할을 한다.
- flag1, flag2, value는 각각 1, 2, 4비트로 구성되어 있어 총 10비트만으로 구성된 구조체가 된다.
- value는 부호 있는 정수(int)로 선언되어 있어 -8부터 7까지의 값을 저장할 수 있다.
- 이 구조체는 하드웨어 레지스터의 플래그 구조를 표현하는 데 적합하다.
✳️ 비트 필드는 컴파일러마다 정렬 방식이 다를 수 있으므로, 이식성 문제를 항상 고려해야 한다.
✳️ 정밀한 메모리 제어가 필요한 경우에는 디버깅이 어려울 수 있으므로 테스트를 충분히 해야 한다.
✅ 마무리 정리
1️⃣ 구조체는 함수의 인자나 반환값으로 사용할 수 있다.
2️⃣ 구조체 포인터를 사용하면 원본 데이터를 직접 수정할 수 있다.
3️⃣ 중첩 구조체는 구조체 안에 또 다른 구조체를 멤버로 포함한다.
4️⃣ 공용체는 여러 멤버가 하나의 메모리를 공유하여 메모리를 절약한다.
5️⃣ 공용체는 동시에 하나의 멤버만 사용할 수 있다.
6️⃣ 열거형은 관련된 상수들을 정수값으로 표현하는 사용자 정의 자료형이다.
7️⃣ typedef는 자료형에 별칭을 부여하여 가독성과 이식성을 높인다.
8️⃣ 비트 필드는 구조체 멤버를 비트 단위로 선언하여 메모리를 효율적으로 사용한다.
9️⃣ : 기호로 각 비트 필드 멤버의 크기를 정의하며, 포인터 접근 시 -> 연산자를 사용한다.
🔟 비트 필드 사용 시 부호 처리, 오버플로우 등 자료형 연산에 주의해야 한다.
'Language > C' 카테고리의 다른 글
| 📌 Chapter 16 - 동적 메모리와 연결 리스트 - 동적 할당 (1) | 2025.06.23 |
|---|---|
| 📌 Chapter 15 - 스트림과 파일 입출력 (0) | 2025.06.21 |
| 📌 Chapter 14 - 구조체 - 배열 (0) | 2025.06.20 |
| 📌 Chapter 14 - 구조체 (1) | 2025.06.20 |
| 📌 Chapter 13 - 문자와 문자열 - 다차원 배열과 문자열 (2) | 2025.06.19 |