//QueueInterface.h
#pragma once //로드 시 해당 헤더파일을 한 번만 불러옴(중복 방지)
#ifndef QUEUE_H //QUEUE_H가 아직 Define되지 않았다면 Define
typedef struct Node {
int data;
struct Node* next;
}Node;
typedef struct
{
Node* front;
Node* rear;
}Queue;
int isEmpty(Queue*);
void initQueue(Queue*);
void Enqueue(Queue*, int);
int Dequeue(Queue*);
int Peek(Queue*);
void ShowQueue(Queue*);
#endif
//QueueInterface.c
#include <stdio.h>
#include <stdlib.h>
#include "QueueInterface.h"
int isEmpty(Queue* q) //해당 q가 비어있는지 확인
{
if (q->front == NULL)
{
printf("Queue is Empty");
return 1;
}
return 0;
}
void initQueue(Queue* q) //해당 q를 초기화
{
q->front = NULL;
q->rear = NULL;
}
void Enqueue(Queue* q, int value) //해당 q에 새 노드와 데이터를 추가
{
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->next = NULL;
if (q->front == NULL)
{
q->rear = newNode;
q->front = newNode;
}
else
{
q->rear->next = newNode;
q->rear = newNode;
}
}
int Dequeue(Queue* q) //해당 q의 가장 오래된 데이터를 반환 및 삭제
{
if (isEmpty(q))
{
return -1;
}
Node* temp = q->front;
int value = temp->data;
q->front = q->front->next;
if (q->front == NULL)
q->rear = NULL;
free(temp);
return value;
}
int Peek(Queue* q) //가장 오래된 데이터 확인
{
if (isEmpty(q))
{
return -1;
}
Node* node = q->front;
return node->data;
}
void ShowQueue(Queue* q) //해당 q의 모든 내용물 출력
{
Node* current = q->front;
if (isEmpty(q))
{
return;
}
while (current != NULL)
{
printf("%d\n", current->data);
current = current->next;
}
printf("NULL\n");
}
//main.c
#include <stdio.h>
#include "QueueInterface.h"
int main()
{
Queue q;
initQueue(&q);
int choice, value;
while (1)
{
printf("\n===== Queue Menu =====\n");
printf("1. Enqueue\n");
printf("2. Dequeue\n");
printf("3. Peek\n");
printf("4. Show Queue\n");
printf("5. Exit\n");
printf("Enter your choice: ");
scanf("%d", &choice);
switch (choice)
{
case 1:
printf("Enter value to enqueue: ");
scanf("%d", &value);
Enqueue(&q, value);
break;
case 2:
value = Dequeue(&q);
if (value != -1)
printf("Dequeued: %d\n", value);
break;
case 3:
value = Peek(&q);
if (value != -1)
printf("Front: %d\n", value);
break;
case 4:
printf("Queue: ");
ShowQueue(&q);
break;
case 5:
printf("Exiting...\n");
return 0;
default:
printf("Invalid choice. Try again.\n");
}
}
return 0;
}
오늘의 학습 포인트
C언어는 헤더파일에 함수를 미리 선언하고 .c파일을 통해 함수 내용을 정의해 줄 수 있다.
(.c파일에 헤더파일을 include하는 것으로 정의 가능)
이것은 일종의 C# 인터페이스나 abstract, virtual 클래스처럼 사용이 가능하다.
하지만 여러개의 헤더파일에 같은 이름의 함수를 선언하는 것(오버로딩)은 C언어에서 지원하지 않는다.
전략 패턴을 사용하기 위해서는
typedef struct Strategy { //전략패턴용 구조체 생성
int (*peek)(void* data);
} Strategy;
int Stack_Peek(Stack* s); //stack용 peek함수
int Queue_Peek(Queue* q); //queue용 peek함수
Strategy s;
s.peek = &Stack_Peek; //s.peek에 Stack_Peek 함수를 지정
s.peek(stack_ptr); //s.peek는 Stack_Peek로 사용됨
s.peek = &Queue_Peek; //s.peek에 Queue_Peek 함수를 지정
s.peek(queue_ptr); //s.peek는 Queue_Peek로 사용됨
구조체를 만들고 함수 포인터를 이용하여
해당 함수를 "덮어씌우는" 것으로 전략패턴을 비슷하게 따라할 수 있다.
하지만 완전히 오버로딩을 할 수는 없고
큐를 사용 중에 스택으로 바꾸려면 s.peek = &Queue_Peek;를 적어줘야하는 문제가 발생하는데
enum 타입을 사용해서 손 쉽게 함수로 만들어 줄 수 있다.
typedef enum literal
{
Queue,
Stack
}li;
void changeStrategy(li type, Strategy st)
{
switch (type)
{
case Queue:
st.peek = &Queue_Peek;
break;
case Stack:
st.peek = &Stack_Peek;
break;
}
}
아쉽게도 C언어에는 type을 받아올 수 있는 기능이 없다고 한다 (GPT피셜)
하지만 함수 포인터와 열거형 타입을 이용하여 전략 패턴을 편하게 구현하는 방법은 찾아냈다.