一般来说,数据(比如一个struct)在内存中,有的部分是定长的(比如int,double或指针),有的数据不定长(比如c字符串),不定长的部分一般通过指针链接到定长的部分中。
对于一个通用的vector,一般包含四部分,每个元素占内存的大小elemSize,已分配内存的元素个数allocatedSize,实际存储元素个数logicalSize,指向已分配内存的指针void* data,其中的指针可通过运算指向vector中任一元素。
情况1:如果数据不包含不定长部分,使用vector时只需将数据拷贝到vector中就可以了。
情况2:如果数据包含不定长部分,由用户负责不定长部分的内存分配和释放,使用vector方法与情况1类似。情况3:数据包含不定长部分,由vector负责整个数据的内存分配与释放,将数据拷贝到vector中时,vector要申请额外内存空间复制不定长的部分,当从vector中删除数据时,vector要首先释放不定长部分的内存,然后删除定长的部分。
再赋予vector自动排序和检索的功能,因此vector中添加了三个额外的函数,分别是:
提供两数据地址比较两数据大小的cmpfn
提供数据地址释放其不定长部分内存空间的freefn
提供源数据和目的数据的地址,为目的数据中不定长部分申请内存空间,将源数据中不定长部分拷贝到目的数据中不定长部分的copyfn。
以下是通用vector的头文件,实现文件与简单测试文件:
#ifndef _VECTOR_H_
#define _VECTOR_H_
typedef int (*VectorCompareFunction)(const void* elemAddr1, const void* elemAddr2);
typedef void (*VectorMapFunction)(void* elemAddr, void* auxData);
typedef void (*VectorFreeFunction)(void* elemAddr);
typedef void (*VectorCopyFunction)(void* destAddr, const void* srcAddr);
typedef struct{
void* data;
int elemSize;
int allocatedSize;
int logicalSize;
VectorFreeFunction freefn;
VectorCopyFunction copyfn;
VectorCompareFunction cmpfn;
} vector;
void VectorNew(vector* v, int elemSize, VectorFreeFunction freefn, VectorCopyFunction copyfn, VectorCompareFunction cmpfn);
void VectorDispose(vector* v);
int VectorLength(const vector* v);
void* VectorNth(const vector* v, int position);
void VectorInsert(vector* v, const void* elemAddr, int position);
void VectorAppend(vector* v, const void* elemAddr);
void VectorReplace(vector* v, const void* elemAddr, int position);
void VectorDelete(vector* v, int position);
int VectorSearch(vector* v, const void* key, int startIndex, bool isSorted);
void VectorSort(vector* v);
void VectorMap(vector* v, VectorMapFunction mapfn, void* auxData);
#endif
#include "vector.h"
#include <stdlib.h>
#include <assert.h>
#include <string.h>
void VectorNew(vector* v, int elemSize, VectorFreeFunction freefn, VectorCopyFunction copyfn, VectorCompareFunction cmpfn) {
v->elemSize = elemSize;
v->allocatedSize = 4;
v->logicalSize = 0;
v->data = malloc(v->allocatedSize * v->elemSize);
assert(v->data != NULL);
v->freefn = freefn;
v->copyfn = copyfn;
v->cmpfn = cmpfn;
}
void VectorDispose(vector* v) {
if(v->freefn != NULL) {
for(int i=0; i<v->logicalSize; i++) {
void* elemAddr = (char*)v->data + i * v->elemSize;
v->freefn(elemAddr);
}
}
free(v->data);
}
int VectorLength(const vector* v) {
return v->logicalSize;
}
void* VectorNth(const vector* v, int position) {
assert((position>=0) && (position<v->logicalSize));
return (char*)v->data + position * v->elemSize;
}
static void VectorGrow(vector* v) {
v->data = realloc(v->data, 2*v->allocatedSize*v->elemSize);
assert(v->data != NULL);
v->allocatedSize *= 2;
}
void VectorInsert(vector* v, const void* elemAddr, int position) {
assert((position>=0) && (position<=v->logicalSize));
if(v->logicalSize == v->allocatedSize)
VectorGrow(v);
void* srcAddr = (char*)v->data + position * v->elemSize;
int byteCount = (v->logicalSize - position) * v->elemSize;
memmove((char*)srcAddr+v->elemSize, srcAddr, byteCount);
memcpy(srcAddr, elemAddr, v->elemSize);
if(v->copyfn != NULL)
v->copyfn(srcAddr, elemAddr);
v->logicalSize++;
}
void VectorAppend(vector* v, const void* elemAddr) {
if(v->logicalSize == v->allocatedSize)
VectorGrow(v);
void* destAddr = (char*)v->data + v->logicalSize * v->elemSize;
memcpy(destAddr, elemAddr, v->elemSize);
if(v->copyfn != NULL)
v->copyfn(destAddr, elemAddr);
v->logicalSize++;
}
void VectorReplace(vector* v, const void* elemAddr, int position) {
assert((position>=0) && (position<v->logicalSize));
void* destAddr = (char*)v->data + position * v->elemSize;
if(v->freefn != NULL)
v->freefn(destAddr);
memcpy(destAddr, elemAddr, v->elemSize);
if(v->copyfn != NULL)
v->copyfn(destAddr, elemAddr);
}
void VectorDelete(vector* v, int position) {
assert((position>=0) && (position<v->logicalSize));
void* destAddr = (char*)v->data + position * v->elemSize;
if(v->freefn != NULL)
v->freefn(destAddr);
int byteCount = (v->logicalSize-1 - position) * v->elemSize;
memmove(destAddr, (char*)destAddr+v->elemSize, byteCount);
v->logicalSize--;
}
void VectorSort(vector* v) {
assert(v->cmpfn != NULL);
qsort(v->data, v->logicalSize, v->elemSize, v->cmpfn);
}
int VectorSearch(vector* v, const void* key, int startIndex, bool isSorted) {
assert(v->cmpfn != NULL);
assert((startIndex>=0) && (startIndex<=v->logicalSize));
void* base = (char*)v->data + startIndex * v->elemSize;
int num = v->logicalSize - startIndex;
int keyIndex = -1;
if(isSorted == true) {
void* result = bsearch(key, base, num, v->elemSize, v->cmpfn);
if(result != NULL)
keyIndex = ((char*)result - (char*)v->data) / v->elemSize;
return keyIndex;
}
for(int i=0; i<num; i++) {
void* elemAddr = (char*)base + i * v->elemSize;
if(v->cmpfn(elemAddr, key) == 0) {
keyIndex = i + startIndex;
break;
}
}
return keyIndex;
}
void VectorMap(vector* v, VectorMapFunction mapfn, void* auxData) {
for(int i=0; i<v->logicalSize; i++) {
void* elemAddr = (char*)v->data + i * v->elemSize;
mapfn(elemAddr, auxData);
}
}
#include "vector.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int int_cmp(const void* elemAddr1, const void* elemAddr2) {
return *(int*)elemAddr1 - *(int*)elemAddr2;
}
int cstr_cmp(const void* elemAddr1, const void* elemAddr2) {
return strcmp(*(char**)elemAddr1, *(char**)elemAddr2);
}
void cstr_copy(void* destAddr, const void* srcAddr) {
*(char**)destAddr = strdup(*(char**)srcAddr);
}
void cstr_free(void* elemAddr) {
free(*(char**)elemAddr);
}
void int_accumulation(void* elemAddr, void* auxData) {
*(int*)auxData += *(int*)elemAddr;
}
void cstr_total_lens(void* elemAddr, void* auxData) {
*(int*)auxData += strlen(*(char**)elemAddr);
}
void cstr_concat_all(void* elemAddr, void* auxData) {
strcat((char*)auxData, *(char**)elemAddr);
}
int main(int argc, char** argv) {
vector v;
int int_array[] = {0, 1, 2, 3, 4, 5};
VectorNew(&v, sizeof(int), NULL, NULL, int_cmp);
printf("Initialize an int vector.\n");
printf("VectorLength:%d.\n", VectorLength(&v));
for(int i=0; i<sizeof(int_array)/sizeof(int); i++) {
VectorInsert(&v, &int_array[i], 0);
VectorAppend(&v, &int_array[i]);
}
printf("Elements in vector:");
for(int i=0; i<VectorLength(&v); i++) {
printf("%d ", *(int*)VectorNth(&v, i));
}
printf("\n");
for(int i=11; i>5; i--) {
VectorReplace(&v, (char*)v.data+i*v.elemSize, 11-i);
VectorDelete(&v, i);
}
printf("Elements in vector after replace/delete:");
for(int i=0; i<VectorLength(&v); i++) {
printf("%d ", *(int*)VectorNth(&v, i));
}
printf("\n");
VectorSort(&v);
printf("first search:%d\n", VectorSearch(&v, &int_array[2], 1, false));
printf("second search:%d\n", VectorSearch(&v, &int_array[3], 1, true));
printf("third search:%d\n", VectorSearch(&v, &int_array[4], 6, false));
printf("forth search:%d\n", VectorSearch(&v, &int_array[5], 6, true));
int sum = 0;
VectorMap(&v, int_accumulation, &sum);
printf("sum of the vector:%d\n", sum);
VectorDispose(&v);
char* cstr_array[] = {"wo", "ai", "tian", "an", "men", "hahha"};
VectorNew(&v, sizeof(char*), cstr_free, cstr_copy, cstr_cmp);
printf("Initialize an int vector.\n");
printf("VectorLength:%d.\n", VectorLength(&v));
for(int i=0; i<sizeof(cstr_array)/sizeof(char*); i++) {
VectorInsert(&v, &cstr_array[i], 0);
VectorAppend(&v, &cstr_array[i]);
}
printf("Elements in vector:");
for(int i=0; i<VectorLength(&v); i++) {
printf("%s ", *(char**)VectorNth(&v, i));
}
printf("\n");
for(int i=11; i>5; i--) {
VectorReplace(&v, (char*)v.data+i*v.elemSize, 11-i);
VectorDelete(&v, i);
}
VectorSort(&v);
printf("Elements in vector after replace/delete/sort:");
for(int i=0; i<VectorLength(&v); i++) {
printf("%s ", *(char**)VectorNth(&v, i));
}
printf("\n");
printf("first search %s from index 1:%d\n", cstr_array[2], VectorSearch(&v, cstr_array+2, 1, false));
printf("second search %s from index 1:%d\n", cstr_array[3], VectorSearch(&v, cstr_array+3, 1, true));
int total_lens = 0;
VectorMap(&v, cstr_total_lens, &total_lens);
printf("toatal of the vector:%d\n", total_lens);
char* str_all = (char*)malloc(total_lens + 1);
str_all[0] = 0;
VectorMap(&v, cstr_concat_all, str_all);
printf("str_all is %s\n", str_all);
VectorDispose(&v);
return 0;
}
另外VectorMapFunction仿照函数式编程的风格,可将一个函数应用在vector遍历数据中,并将返回结果存入*auxData。