要点:
1. 要通过C来实现类似于C++中的Vector模板,常用的做法是使用typedef,但这么做的话无法实现C++中拷贝函数及析构函数的功能,而且还需要考虑包含头文件的顺序,即目标类型必须先于Vector定义。
2. 故可以考虑使用void *类型指针。
3. 对于指针来说,重要的不是类型,唯一重要的是这个指针所指向的内存区域中一个元素的大小是多少,如char *指针指向的内存区域一个元素占8位,short *则占16位,对于一些结构体指针来说,这个值可以是任意倍的字节。
4. 对于32位机器来说,每个指针本身的大小都是32位
总结如上要点,得出的解决办法是,
typedef void *vector_entry_ptr;
typedef void (*vector_copy_func)(vector_entry_ptr, const vector_entry_ptr);
typedef void (*vector_cleanup_func)(vector_entry_ptr); /* the parameter passed to clean_f is the address of the ith element */
typedef struct _vector {
vector_entry_ptr datap;
unsigned long elementSize;
unsigned long capacity;
unsigned long count;
vector_copy_func copy_f;
vector_cleanup_func clean_f;
} Vector, *VectorPtr;
声明一个这样的数据结构。
copy_f以及clean_f是函数指针,这里充当拷贝构造函数和析构函数的作用。可以使用memcpy和free作为默认行为(当他们为NULL的时候)。
具体操作见源代码:
vector.h
/*
* Dynamic Allocated Array-Based Vector C Implementation
* For The Teco Project
* Copyright (C) 2010 milkyjing <milkyjing@gmail.com>
* Auguest 11th, 2010
*
* Modified for The RIXE Project at June 14th, 2011 by milkyjing
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* milkyjing
*
*/
#ifndef VECTOR_H
#define VECTOR_H
#pragma once
#include <memory.h>
#include <stdlib.h>
typedef void *vector_entry_ptr;
typedef void (*vector_copy_func)(vector_entry_ptr, const vector_entry_ptr);
typedef void (*vector_cleanup_func)(vector_entry_ptr); /* the parameter passed to clean_f is the address of the ith element */
typedef struct _vector {
vector_entry_ptr datap;
unsigned long elementSize;
unsigned long capacity;
unsigned long count;
vector_copy_func copy_f;
vector_cleanup_func clean_f;
} Vector, *VectorPtr;
typedef int (*vector_compare_func)(const vector_entry_ptr, const vector_entry_ptr); /* (src, key), `> 0' - left larger, `= 0' equal, `< 0', left smaller */
typedef int (*vector_compare_interval_func)(const vector_entry_ptr, const vector_entry_ptr, const vector_entry_ptr); /* (start, end, key), the last parameter is key, `> 0' - key on the left, `= 0' in the interval, `< 0', key on the right */
typedef void (*vector_traverse_func)(const vector_entry_ptr);
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
extern void vector_create(VectorPtr *pvptr, unsigned long elementSize, unsigned long elements, vector_copy_func copy_f, vector_cleanup_func clean_f);
extern unsigned long vector_get_size(const VectorPtr vptr);
extern const vector_entry_ptr vector_get_buf(const VectorPtr vptr);
extern int vector_assign(VectorPtr vptr, unsigned long i, const vector_entry_ptr element);
/* element can be NULL, if so, we will not make a copy to element. the return value is the address of the element retrieved */
extern const vector_entry_ptr vector_retrieve(const VectorPtr vptr, unsigned long i, vector_entry_ptr element);
extern void vector_push_back(VectorPtr vptr, const vector_entry_ptr element);
extern int vector_pop_back(VectorPtr vptr);
extern void vector_clear(VectorPtr vptr);
extern void vector_destroy(VectorPtr vptr);
/* UTILITY FUNCTIONS */
extern void vector_traverse(const VectorPtr vptr, vector_traverse_func traverse_func);
extern void vector_bulbsort(VectorPtr vptr, vector_compare_func compare_func);
/* s should always be 0, and l should always be vptr->count - 1, note that both s and l should be signed rather than unsigned */
extern void vector_quicksort(VectorPtr vptr, long s, long l, vector_compare_func compare_func);
/* if element = NULL, it won't copy the result to it, just return the index, which somehow increase performance */
extern long vector_sequential_search(const VectorPtr vptr, const vector_entry_ptr key, vector_compare_func compare_func, vector_entry_ptr element);
/* if element = NULL, it won't copy the result to it, just return the index, which somehow increase performance */
extern long vector_binary_search(const VectorPtr vptr, const vector_entry_ptr key, vector_compare_func compare_func, vector_entry_ptr element);
/* returns the left side element (start) */
extern long vector_binary_search_interval(const VectorPtr vptr, const vector_entry_ptr key, vector_compare_interval_func compare_interval_func);
#ifdef __cplusplus
}
#endif
#endif /* VECTOR_H */
/*
* Copyright (C) 2011 milkyjing <milkyjing@gmail.com>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* milkyjing
*
*/
#include "vector.h"
void vector_create(VectorPtr *pvptr, unsigned long elementSize, unsigned long elements, vector_copy_func copy_f, vector_cleanup_func clean_f) {
VectorPtr vptr;
vptr = (VectorPtr)malloc(sizeof(Vector));
vptr->elementSize = elementSize;
vptr->capacity = elements;
vptr->count = elements;
vptr->copy_f = copy_f; /* if copy_f = NULL, default copy function will be applied */
vptr->clean_f = clean_f; /* if clean_f = NULL, default cleanup function will be applied */
if (elements > 0) {
unsigned long size = vptr->capacity * vptr->elementSize;
vptr->datap = (vector_entry_ptr)malloc(size);
memset(vptr->datap, 0, size);
} else
vptr->datap = NULL;
*pvptr = vptr;
}
unsigned long vector_get_size(const VectorPtr vptr) {
return vptr->count;
}
const vector_entry_ptr vector_get_buf(const VectorPtr vptr) {
return (const vector_entry_ptr)vptr->datap;
}
int vector_assign(VectorPtr vptr, unsigned long i, const vector_entry_ptr element) {
vector_entry_ptr oldElem;
if (i >= vptr->count) return -1;
oldElem = (unsigned char *)vptr->datap + i * vptr->elementSize;
if (vptr->clean_f)
vptr->clean_f(oldElem);
if (vptr->copy_f)
vptr->copy_f(oldElem, element);
else
memcpy(oldElem, element, vptr->elementSize);
return 0;
}
const vector_entry_ptr vector_retrieve(const VectorPtr vptr, unsigned long i, vector_entry_ptr element) {
vector_entry_ptr oldElem;
if (i >= vptr->count) return NULL;
oldElem = (unsigned char *)vptr->datap + i * vptr->elementSize;
if (element) { /* make a copy */
if (vptr->copy_f)
vptr->copy_f(element, oldElem);
else
memcpy(element, oldElem, vptr->elementSize);
}
return (const vector_entry_ptr)oldElem;
}
void vector_push_back(VectorPtr vptr, const vector_entry_ptr element) {
vector_entry_ptr oldElem;
if (vptr->count == vptr->capacity) {
vptr->capacity += vptr->capacity / 2 + 1;
vptr->datap = (vector_entry_ptr)realloc(vptr->datap, vptr->capacity * vptr->elementSize);
}
oldElem = (unsigned char *)vptr->datap + vptr->count * vptr->elementSize;
if (vptr->copy_f)
vptr->copy_f(oldElem, element);
else
memcpy(oldElem, element, vptr->elementSize);
vptr->count++;
}
int vector_pop_back(VectorPtr vptr) {
vector_entry_ptr oldElem;
if (0 == vptr->count) return -1;
oldElem = (unsigned char *)vptr->datap + (vptr->count - 1) * vptr->elementSize;
if (vptr->clean_f)
vptr->clean_f(oldElem);
vptr->count--;
return 0;
}
void vector_clear(VectorPtr vptr) {
if (vptr->clean_f) {
unsigned long i;
for (i = 0; i < vptr->count; i++)
vptr->clean_f((unsigned char *)vptr->datap + i * vptr->elementSize); /* the parameter passed to clean_f is the address of the ith element */
}
free(vptr->datap);
vptr->datap = NULL;
vptr->capacity = 0;
vptr->count = 0;
/* other data members should stay unchanged */
}
void vector_destroy(VectorPtr vptr) {
vector_clear(vptr);
free(vptr);
}
/* UTILITY FUNCTIONS */
void vector_traverse(const VectorPtr vptr, vector_traverse_func traverse_func) {
unsigned long i;
for (i = 0; i < vptr->count; i++)
traverse_func((unsigned char *)vptr->datap + i * vptr->elementSize);
}
void vector_bulbsort(VectorPtr vptr, vector_compare_func compare_func) {
unsigned long i, j;
vector_entry_ptr p1, p2, pt;
pt = (vector_entry_ptr)malloc(vptr->elementSize); /* use as temp */
for (i = 0; i < vptr->count; i++) {
for (j = 0; j < vptr->count - i - 1; j++) {
p1 = (unsigned char *)vptr->datap + j * vptr->elementSize;
p2 = (unsigned char *)p1 + vptr->elementSize;
if (compare_func(p1, p2) > 0) {
memcpy(pt, p1, vptr->elementSize);
memcpy(p1, p2, vptr->elementSize);
memcpy(p2, pt, vptr->elementSize);
}
}
}
free(pt);
}
/* s should always be 0, and l should always be vptr->count - 1, note that both s and l should be signed rather than unsigned */
void vector_quicksort(VectorPtr vptr, long s, long l, vector_compare_func compare_func) {
long i, j;
vector_entry_ptr pt;
i = s;
j = l;
pt = (vector_entry_ptr)malloc(vptr->elementSize); /* use as temp */
if (s < l) {
memcpy(pt, (unsigned char *)vptr->datap + s * vptr->elementSize, vptr->elementSize);
while (i != j) {
while (j > i && compare_func((unsigned char *)vptr->datap + j * vptr->elementSize, pt) > 0) j--;
memcpy((unsigned char *)vptr->datap + i * vptr->elementSize, (unsigned char *)vptr->datap + j * vptr->elementSize, vptr->elementSize);
while (i < j && compare_func((unsigned char *)vptr->datap + i * vptr->elementSize, pt) < 0) i++;
memcpy((unsigned char *)vptr->datap + j * vptr->elementSize, (unsigned char *)vptr->datap + i * vptr->elementSize, vptr->elementSize);
}
memcpy((unsigned char *)vptr->datap + i * vptr->elementSize, pt, vptr->elementSize);
vector_quicksort(vptr, s, i - 1, compare_func);
vector_quicksort(vptr, i + 1, l, compare_func);
}
free(pt);
}
long vector_sequential_search(const VectorPtr vptr, const vector_entry_ptr key, vector_compare_func compare_func, vector_entry_ptr element) {
unsigned long i;
vector_entry_ptr temp;
for (i = 0; i < vptr->count; i++) {
temp = (unsigned char *)vptr->datap + i * vptr->elementSize;
if (compare_func(key, temp) == 0) {
if (element) {
if (vptr->copy_f)
vptr->copy_f(element, temp);
else
memcpy(element, temp, vptr->elementSize);
}
return i;
}
}
return -1;
}
long vector_binary_search(const VectorPtr vptr, const vector_entry_ptr key, vector_compare_func compare_func, vector_entry_ptr element) {
long bottom, top, mid;
vector_entry_ptr temp;
bottom = 0;
top = vptr->count - 1;
while (bottom < top) {
mid = (top + bottom) / 2;
temp = (unsigned char *)vptr->datap + mid * vptr->elementSize;
if (compare_func(temp, key) < 0)
bottom = mid + 1;
else
top = mid;
}
temp = (unsigned char *)vptr->datap + top * vptr->elementSize;
if (bottom > top || compare_func(temp, key) != 0) return -1;
if (element) {
if (vptr->copy_f)
vptr->copy_f(element, temp);
else
memcpy(element, temp, vptr->elementSize);
}
return top;
}
long vector_binary_search_interval(const VectorPtr vptr, const vector_entry_ptr key, vector_compare_interval_func compare_interval_func) {
long bottom, top, mid;
vector_entry_ptr start, end;
bottom = 0;
top = vptr->count - 2;
while (bottom < top) {
mid = (top + bottom) / 2;
start = (unsigned char *)vptr->datap + mid * vptr->elementSize;
end = (unsigned char *)start + vptr->elementSize;
if (compare_interval_func(start, end, key) < 0)
bottom = mid + 1;
else
top = mid;
}
start = (unsigned char *)vptr->datap + top * vptr->elementSize;
end = (unsigned char *)start + vptr->elementSize;
if (bottom > top || compare_interval_func(start, end, key) != 0) return -1;
return top;
}