본문 바로가기
Computer Science/C 언어

[C 17]Understanding Generic Programming / 제네릭 프로그래밍 이해

by rnasterofmysea 2024. 12. 15.
반응형

Understanding Generic Programming / 제네릭 프로그래밍 이해

제네릭 프로그래밍이란?

  • 정의: 데이터 타입에 관계없이 동작하는 코드를 작성하는 기법.
  • 목적:
    1. 코드 재사용성: 다양한 데이터 타입에 대해 동일한 코드를 사용.
    2. 유연성: 타입에 구애받지 않고 동작.
    3. 중복 제거: 동일한 기능을 여러 타입으로 구현할 필요 없음.

C 언어에서 제네릭 프로그래밍

C 언어는 템플릿(C++에서 제공)을 지원하지 않으므로, 매크로void * 포인터를 활용하여 제네릭 프로그래밍을 구현합니다.

사용 사례

  1. 데이터 구조(스택, 큐, 링크드 리스트 등)에서 다양한 데이터 타입 처리.
  2. 정렬, 검색 알고리즘에서 데이터 타입 독립적 구현.

13.2. Macros and Inline Functions / 매크로와 인라인 함수

매크로를 사용한 제네릭 프로그래밍

매크로 예제: MAX

#define MAX(a, b) ((a) > (b) ? (a) : (b))

int main() {
    int x = 10, y = 20;
    printf("Max: %d\n", MAX(x, y)); // 출력: 20

    double a = 5.5, b = 3.3;
    printf("Max: %.2f\n", MAX(a, b)); // 출력: 5.5
    return 0;
}

매크로의 장점

  • 다양한 데이터 타입에서 동작.
  • 간단한 함수 구현에 유리.

매크로의 단점

  1. 디버깅 어려움: 매크로는 텍스트 치환이므로, 디버깅 과정에서 원래 코드를 확인하기 어려움.
  2. 부작용 가능:
    printf("%d\n", MAX(x++, y)); // x가 두 번 증가할 수 있음
    

인라인 함수를 사용한 대체

인라인 함수는 매크로의 단점을 해결하며, 컴파일러가 함수 호출 대신 코드를 삽입하도록 요청합니다.

인라인 함수 예제

inline int max(int a, int b) {
    return (a > b) ? a : b;
}

int main() {
    int x = 10, y = 20;
    printf("Max: %d\n", max(x, y)); // 출력: 20
    return 0;
}

장점

  • 타입 안전성 제공.
  • 디버깅 편리.
  • 부작용 방지.

단점

  • 함수 크기가 클 경우 코드 팽창 가능.

13.3. Using void * Pointers / void * 포인터 사용

void *는 타입이 지정되지 않은 포인터로, 제네릭 데이터를 처리하는 데 유용합니다.

1. void *를 사용한 배열 검색

#include <stdio.h>
#include <string.h>

int compare_int(const void *a, const void *b) {
    return *(int *)a - *(int *)b;
}

void *find(void *array, size_t n, size_t size, void *target, int (*cmp)(const void *, const void *)) {
    for (size_t i = 0; i < n; i++) {
        void *element = (char *)array + i * size;
        if (cmp(element, target) == 0) {
            return element;
        }
    }
    return NULL;
}

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int target = 3;
    int *result = (int *)find(arr, 5, sizeof(int), &target, compare_int);
    if (result) {
        printf("Found: %d\n", *result);
    } else {
        printf("Not found\n");
    }
    return 0;
}

코드 설명

  • void *로 배열 요소의 타입과 관계없이 검색 가능.
  • 비교 함수(cmp)로 데이터 타입별 비교 방식을 전달.

2. void *를 사용한 링크드 리스트

#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
    void *data;
    struct Node *next;
} Node;

Node *createNode(void *data) {
    Node *node = (Node *)malloc(sizeof(Node));
    node->data = data;
    node->next = NULL;
    return node;
}

void printList(Node *head, void (*print)(void *)) {
    Node *current = head;
    while (current) {
        print(current->data);
        current = current->next;
    }
}

void printInt(void *data) {
    printf("%d -> ", *(int *)data);
}

int main() {
    int a = 10, b = 20, c = 30;
    Node *head = createNode(&a);
    head->next = createNode(&b);
    head->next->next = createNode(&c);

    printList(head, printInt);
    printf("NULL\n");
    return 0;
}

코드 설명

  • 링크드 리스트의 데이터는 void *로 저장되어 타입 독립적으로 사용 가능.
  • print 함수 포인터를 활용해 데이터를 출력.

13.4. Writing Type-Independent Code / 타입에 독립적인 코드 작성

1. 제네릭 동적 배열 생성

#include <stdio.h>
#include <stdlib.h>

#define CREATE_ARRAY(type, size) (type *)malloc(sizeof(type) * (size))

int main() {
    int *arr = CREATE_ARRAY(int, 5);
    for (int i = 0; i < 5; i++) {
        arr[i] = i + 1;
        printf("%d ", arr[i]);
    }
    free(arr);
    return 0;
}

설명

  • CREATE_ARRAY 매크로로 동적 배열을 생성하며, 데이터 타입과 크기를 동적으로 지정 가능.

2. 제네릭 정렬: qsort

C 표준 라이브러리의 qsort 함수는 타입 독립적인 정렬을 제공합니다.

예제

#include <stdio.h>
#include <stdlib.h>

int compare_int(const void *a, const void *b) {
    return *(int *)a - *(int *)b;
}

int main() {
    int arr[] = {5, 3, 2, 4, 1};
    size_t n = sizeof(arr) / sizeof(arr[0]);

    qsort(arr, n, sizeof(int), compare_int);

    for (size_t i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    return 0;
}

설명

  • qsort는 배열 크기, 요소 크기, 비교 함수 포인터를 받아 제네릭 정렬을 수행.
  • 데이터를 정렬할 때 데이터 타입에 구애받지 않음.

요약

  1. 매크로:
    • 텍스트 치환을 통해 간단한 제네릭 코드를 작성.
  2. 인라인 함수:
    • 타입 안전성과 디버깅 편의성을 제공하며, 매크로를 대체.
  3. void * 포인터:
    • 타입 독립적인 데이터 처리 및 함수 설계.
  4. 타입 독립적 데이터 구조:
    • 동적 배열, 링크드 리스트, 정렬 등에서 활용.
  5. C 표준 라이브러리 활용:
반응형