Language/C

📌 Chapter 14 - 구조체 - 함수, 중첩, 공용, 비트필드

e-cko 2025. 6. 20. 14:32
반응형
반응형

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️⃣ :
기호로 비트 필드 멤버의 크기를 정의하며, 포인터 접근 -> 연산자를 사용한다.
🔟
비트 필드 사용 부호 처리, 오버플로우 자료형 연산에 주의해야 한다.

 

반응형