C++의 std::vector처럼 다양한 타입의 데이터를 담을 수 있는 벡터 자료구조는 매우 편리합니다. 하지만 임베디드 환경에서 동작하는 C 언어에서는 다음과 같은 제약이 존재하죠:
- malloc, realloc, free와 같은 동적 메모리 할당이 금지
- 표준 라이브러리 의존 최소화 필요
- 컴파일 타임에 크기가 고정된 구조 선호
이 글에서는 이런 조건을 만족하는 정적(static) 제네릭 벡터를 직접 구현해 보겠습니다.
목표
- 어떤 타입이든 담을 수 있는 제네릭 벡터
- malloc 없이 작동 (정적 메모리)
- 삽입, 삭제, 조회 기능 지원
- C 언어로 작성
📦 구조 설계
먼저 벡터 구조체는 다음과 같은 정보를 담습니다:
typedef struct {
uint8_t* buffer; // 데이터를 저장하는 버퍼
size_t element_size; // 요소 하나의 크기
size_t size; // 현재 저장된 요소 개수
size_t capacity; // 최대 저장 가능한 요소 개수
} StaticVector;
🛠️ 구현 코드
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#define MAX_VECTOR_CAPACITY 128 // 벡터 최대 용량 (임베디드 환경에서 제한)
// 제네릭 벡터 구조체
typedef struct {
uint8_t* buffer; // 외부에서 주입받은 버퍼
size_t element_size; // 요소 하나의 크기
size_t size; // 현재 저장된 요소 수
size_t capacity; // 전체 저장 가능한 요소 수
} StaticVector;
// 초기화 (버퍼는 외부에서 미리 선언하여 전달)
void vector_init_static(StaticVector* vec, void* buffer, size_t element_size, size_t capacity) {
vec->buffer = (uint8_t*)buffer;
vec->element_size = element_size;
vec->size = 0;
vec->capacity = capacity;
}
int vector_push_back(StaticVector* vec, void* element) {
if (vec->size >= vec->capacity) return -1; // overflow
memcpy(vec->buffer + vec->size * vec->element_size, element, vec->element_size);
vec->size++;
return 0;
}
int vector_insert(StaticVector* vec, size_t index, void* element) {
if (vec->size >= vec->capacity || index > vec->size) return -1;
uint8_t* insert_pos = vec->buffer + index * vec->element_size;
memmove(insert_pos + vec->element_size, insert_pos, (vec->size - index) * vec->element_size);
memcpy(insert_pos, element, vec->element_size);
vec->size++;
return 0;
}
int vector_erase(StaticVector* vec, size_t index) {
if (index >= vec->size) return -1;
uint8_t* erase_pos = vec->buffer + index * vec->element_size;
memmove(erase_pos, erase_pos + vec->element_size, (vec->size - index - 1) * vec->element_size);
vec->size--;
return 0;
}
void* vector_get(StaticVector* vec, size_t index) {
if (index >= vec->size) return NULL;
return vec->buffer + index * vec->element_size;
}
void vector_clear(StaticVector* vec) {
vec->size = 0;
}
✨ 사용 예시
int main() {
int buffer[MAX_VECTOR_CAPACITY]; // 정적으로 할당된 버퍼
StaticVector vec;
vector_init_static(&vec, buffer, sizeof(int), MAX_VECTOR_CAPACITY);
for (int i = 0; i < 5; ++i) {
vector_push_back(&vec, &i);
}
int value = 999;
vector_insert(&vec, 2, &value);
vector_erase(&vec, 0);
for (size_t i = 0; i < vec.size; ++i) {
int* p = (int*)vector_get(&vec, i);
printf("%d ", *p);
}
return 0;
}
출력 예:
✅ 정리
항목 |
설명 |
제네릭 지원 |
void*와 element_size를 통해 어떤 타입도 저장 가능 |
정적 메모리 |
외부에서 버퍼를 받아와 동적 할당 없이 작동 |
기본 연산 지원 |
push_back, insert, erase, get, clear 등 |
임베디드 최적화 |
malloc 없이도 안전하게 작동 |
// static_vector.h
#ifndef STATIC_VECTOR_H
#define STATIC_VECTOR_H
#include <stddef.h>
#include <stdint.h>
// 정적 제네릭 벡터 구조체
typedef struct {
uint8_t* buffer;
size_t element_size;
size_t size;
size_t capacity;
} StaticVector;
// 벡터 초기화 (외부 버퍼 필요)
void vector_init_static(StaticVector* vec, void* buffer, size_t element_size, size_t capacity);
// 요소 추가
int vector_push_back(StaticVector* vec, void* element);
// 요소 삽입
int vector_insert(StaticVector* vec, size_t index, void* element);
// 요소 삭제
int vector_erase(StaticVector* vec, size_t index);
// 요소 접근
void* vector_get(StaticVector* vec, size_t index);
// 벡터 비우기
void vector_clear(StaticVector* vec);
// ===== 이터레이터 =====
typedef struct {
StaticVector* vector;
size_t index;
} VectorIterator;
VectorIterator vector_begin(StaticVector* vec);
int vector_iterator_has_next(VectorIterator* it);
void* vector_iterator_next(VectorIterator* it);
#endif // STATIC_VECTOR_H
// static_vector.c
#include "static_vector.h"
#include <string.h>
void vector_init_static(StaticVector* vec, void* buffer, size_t element_size, size_t capacity) {
vec->buffer = (uint8_t*)buffer;
vec->element_size = element_size;
vec->size = 0;
vec->capacity = capacity;
}
int vector_push_back(StaticVector* vec, void* element) {
if (vec->size >= vec->capacity) return -1;
memcpy(vec->buffer + vec->size * vec->element_size, element, vec->element_size);
vec->size++;
return 0;
}
int vector_insert(StaticVector* vec, size_t index, void* element) {
if (vec->size >= vec->capacity || index > vec->size) return -1;
uint8_t* insert_pos = vec->buffer + index * vec->element_size;
memmove(insert_pos + vec->element_size, insert_pos, (vec->size - index) * vec->element_size);
memcpy(insert_pos, element, vec->element_size);
vec->size++;
return 0;
}
int vector_erase(StaticVector* vec, size_t index) {
if (index >= vec->size) return -1;
uint8_t* erase_pos = vec->buffer + index * vec->element_size;
memmove(erase_pos, erase_pos + vec->element_size, (vec->size - index - 1) * vec->element_size);
vec->size--;
return 0;
}
void* vector_get(StaticVector* vec, size_t index) {
if (index >= vec->size) return NULL;
return vec->buffer + index * vec->element_size;
}
void vector_clear(StaticVector* vec) {
vec->size = 0;
}
// ===== 이터레이터 구현 =====
VectorIterator vector_begin(StaticVector* vec) {
VectorIterator it = {vec, 0};
return it;
}
int vector_iterator_has_next(VectorIterator* it) {
return it->index < it->vector->size;
}
void* vector_iterator_next(VectorIterator* it) {
if (!vector_iterator_has_next(it)) return NULL;
return it->vector->buffer + (it->vector->element_size * it->index++);
}