벡터는 이미 할당이 되어있다는 가정이기 때문에, 모든 데이터를 NULL 또는 0으로 초기화만 해준다.
재할당
staticvoidre_allocate(vector* vec, int capacity){
if (capacity == 0)
capacity = 1;
int* new_data = (int*)malloc(capacity * sizeof(int));
for (int i = 0; i < vec->size; ++i)
new_data[i] = vec->data[i];
free(vec->data);
vec->data = new_data;
vec->capacity = capacity;
}
<stdlib.h>의 realloc()과 기능상으로는 거의 비슷하다고 보면 되는데, 구현하는 김에 같이 해버렸다.
벡터의 capacity가 초기화 직후에는 0이기 때문에 호출시, 예외적으로 1을 넣어주었다.
data 자료형의 byte_size * capacity 만큼 할당해준 뒤,
이전의 모든 데이터를 새로 할당한 데이터에 복사한다.
이후, 이전 데이터는 해제한 뒤, 새 데이터 주소를 vec의 data에 복사해준다.
위와 같이 진행된다.
이전 데이터가 NULL일 경우는 size도 0이기 때문에 복사하지 않으며,
NULL pointer는 free() 해도 아무 일도 일어나지 않는다.
벡터의 함수 내에서만 쓰이기 때문에 static으로 선언했다.
맨 뒤에 데이터 추가
voidpush_back(vector* vec, int data){
if (vec->size == vec->capacity)
re_allocate(vec, vec->capacity * 2);
vec->data[vec->size] = data;
++vec->size;
}
만약, vector의 size가 꽉 찼다면, 앞서 구현했던 재할당 함수를 호출한다.
따로 설정하지 않으면 기본값으로 현재 용량의 두배를 재할당한다.
이런식으로 진행된다.
이후 배열에 vector의 size를 index로 넣어 맨 마지막에 data를 추가한다.
vector의 size를 하나 증가시킨다.
맨 뒤의 데이터 삭제
voidpop_back(vector* vec){
if (vec->size == 0)
return;
--vec->size;
}
vector의 size가 0일 때의 예외를 처리해준 뒤, vector의 size만 감소시켰다.
vector의 size보다 크거나 같은 index에 직접 접근하는 것이 아닌 이상은 접근할 수 없으므로 내버려둔다.
데이터 개수 감소에 따라 더 작은 크기로 재할당은 해주지 않았다.
이미 한 번 들어온 최대 개수는 앞으로도 들어올 가능성이 있기에 불필요한 오버헤드 발생을 줄이기 위해서다.
물론 따로 구현 해줘도 상관없다. (자료 구조는 사용할 곳에 따라 유동적일 수 있음)
배열 용량 사전 설정
voidreserve(vector* vec, int capacity){
if (capacity <= vec->capacity)
return;
re_allocate(vec, capacity);
}
벡터의 단점에서 언급했던 부분을 보완하는 함수다.
다시 말하자면 불필요한 재할당과 복사의 반복에 대한 오버헤드를 줄이기 위함이다.
따라서 미리 사용할 만큼의 용량을 할당하고 사용할 수 있게 한다.
만약 vector의 기존 capacity보다 작거나 같으면 굳이 할 필요 없으므로 바로 return.
그게 아니라면, 인자로 받은 capacity를 재할당 함수의 인자로 넣어 호출한다.
중간에 데이터 삽입
voidinsert(vector* vec, int index, int data){
if (index < 0 || index > vec->size)
return;
if (vec->size == vec->capacity)
re_allocate(vec, vec->capacity * 2);
for (int i = vec->size - 1; i >= index; --i)
vec->data[i + 1] = vec->data[i];
vec->data[index] = data;
++vec->size;
}
먼저 index가 0보다 작을 때와, index가 vector의 size보다 클 때의 예외 처리를 해주었다.
이후 push_back()과 똑같이 데이터가 더 들어갈 공간이 없다면, 두 배 재할당을 해준다.
이제 데이터를 오른쪽으로 밀어주며 새로운 데이터가 들어갈 자리를 마련해준다.
이 때 중요한 점은 배열의 뒤부터 순회하며 데이터를 옮겨줘야 한다는 점이다.
앞부터 순회하며 옮긴다면 데이터가 overlap 되는 현상이 생기며 1, 2, 3, 3, 3, 3으로 채워질 것이다.
이후 인자로 받은 index에 인자로 받은 data를 넣어주고, vector의 size를 올려주면 끝이다.
중간의 데이터 삭제
voiderase(vector* vec, int index){
if (index < 0 || index >= vec->size)
return;
for (int i = index + 1; i < vec->size; ++i)
vec->data[i - 1] = vec->data[i];
--vec->size;
}
이 역시 먼저 index가 0보다 작을 때와, index가 vector의 size보다 클 때의 예외 처리를 해주었다.
이후 인자로 받은 삭제할 부분의 index를 덮어주기 위해 나머지 데이터들을 왼쪽으로 당겨준다.
이번에 중요한 점은 중간에 데이터 삽입과는 달리 반대로 배열의 처음부터 순회하며 데이터를 옮겨야 한다는 점이다.
만약, 뒤부터 순회하며 옮긴다면 이번엔 반대로 overlap되며 1, 2, 5, 5, 5, 5가 채워질 것이다.
삭제에 대한 이해를 돕기 위해 사용하지 않는 부분을 회색으로 칠해보았다.
pop_back() 처럼 직접 데이터를 지우진 않지만, 정상적인 방법으로는 접근할 수 없어 내버려둔다.