而
Jean-François Fabre
在回答上述问题时,我想指出的是,通过使用以下结构,您可以更好地管理此类字节数组:
typedef struct {
size_t max; /* Number of chars allocated for */
size_t len; /* Number of chars in use */
unsigned char *data;
} bytearray;
#define BYTEARRAY_INIT { 0, 0, NULL }
void bytearray_init(bytearray *barray)
{
barray->max = 0;
barray->len = 0;
barray->data = NULL;
}
void bytearray_free(bytearray *barray)
{
free(barray->data);
barray->max = 0;
barray->len = 0;
barray->data = NULL;
}
要声明空字节数组,可以使用
bytearray myba = BYTEARRAY_INIT;
bytearray myba; bytearray_init(&myba);
. 两者是等价的。
当您不再需要数组时,请调用
bytearray_free(&myba);
free(NULL)
是安全的,什么也不做,所以释放一个
bytearray
已初始化但未使用的。
附加到
bytearray公司
:
int bytearray_append(bytearray *barray, const void *from, const size_t size)
{
if (barray->len + size > barray->max) {
const size_t len = barray->len + size;
size_t max;
void *data;
/* Example policy: */
if (len < 8)
max = 8; /* At least 8 chars, */
else
if (len < 4194304)
max = (3*len) / 2; /* grow by 50% up to 4,194,304 bytes, */
else
max = (len | 2097151) + 2097153 - 24; /* then pad to next multiple of 2,097,152 sans 24 bytes. */
data = realloc(barray->data, max);
if (!data) {
/* Not enough memory available. Old data is still valid. */
return -1;
}
barray->max = max;
barray->data = data;
}
/* Copy appended data; we know there is room now. */
memmove(barray->data + barray->len, from, size);
barray->len += size;
return 0;
}
因为这个函数至少在理论上不能重新分配内存,所以它会返回
0
不需要一个
malloc()
打电话,因为
realloc(NULL, size)
完全等同于
malloc(size)
“增长政策”是一个非常有争议的问题。你只需要
max = barray->len + size
,就这样结束了。但是,动态内存管理功能相对比较慢,所以在实践中,我们不想调用
realloc()
上面的策略试图做得更好,但不要太激进:它总是分配至少8个字符,即使需要更少的字符。最多4194304个字符,分配50%的额外字符。在上面,它将分配大小四舍五入到2097152的下一个倍数,并减去24;绝对不是
“这是最好的,这也是你应该做的”
. 此策略确保每个字节数组最多分配4194304=2
22
未使用的字符。然而,2097152=2
是AMD64(x86-64)上一个巨大页面的大小,是基本上所有体系结构上本机页面大小的两倍的幂。它还足够大,可以在基本上所有这样做的架构上从所谓的sbrk()分配切换到内存映射。这意味着如此巨大的分配使用堆中的一个单独的部分进行分配,而未使用的部分通常只是虚拟内存,在被访问之前不一定由任何RAM支持。因此,这项政策
倾向于
当然,如果你
(或测量!)典型工作负载中字节数组的典型大小,您可以为此优化增长策略,并获得更好的结果。
memmove()
而不是
memcpy()
,以防有人想重复同一字节数组的一部分:
只有在源区域和目标区域不重叠时才起作用;
即使在那种情况下也行。
当使用更高级的数据结构(如哈希表)时,上述结构的变体通常很有用(也就是说,在有大量空字节数组的情况下,这会更好。)
数据不是指向数据的指针,而是结构本身的一部分,作为C99灵活数组成员:
typedef struct {
size_t max;
size_t len;
unsigned char data[];
} bytearray;
不能声明字节数组本身(即。
bytearray myba;
不起作用);你总是宣布
对于这样的字节数组:
bytearray *myba = NULL;
. 指针为NULL时,将被视为空字节数组。
尤其要看有多少
data
对于这样的数组项,您可以使用访问器函数(也在与数据结构相同的头文件中定义),而不是
myba.len
:
static inline size_t bytearray_len(bytearray *const barray)
{
return (barray) ? barray->len : 0;
}
static inline size_t bytearray_max(bytearray *const barray)
{
return (barray) ? barray->max : 0;
}
这个
(expression) ? (if-true) : (if-false)
是三元运算符。在这种情况下,第一个函数与
static inline size_t bytearray_len(bytearray *const barray)
{
if (barray)
return barray->len;
else
return 0;
}
如果你想知道
bytearray *const barray
,请记住指针声明是从右向左读取的
*
barray
由于这类数组通常需要调整大小,因此调整大小通常放在一个单独的助手函数中:
bytearray *bytearray_resize(bytearray *const barray, const size_t len)
{
bytearray *temp;
if (!len) {
free(barray);
errno = 0;
return NULL;
}
if (!barray) {
temp = malloc(sizeof (bytearray) + len * sizeof barray->data[0]);
if (!temp) {
errno = ENOMEM;
return NULL;
}
temp->max = len;
temp->len = 0;
return temp;
}
if (barray->len > len)
barray->len = len;
if (barray->max == len)
return barray;
temp = realloc(barray, sizeof (bytearray) + len * sizeof barray->data[0]);
if (!temp) {
free(barray);
errno = ENOMEM;
return NULL;
}
temp->max = len;
return temp;
}
那是什么意思
errno = 0
NULL
具有
errno == ENOMEM
,就像
malloc()
/
去吧。但是,由于所需的新长度为零,因此通过释放旧字节数组(如果有的话)来节省内存,并返回
. 但既然这不是一个错误,我们设置
errno
无效的
,检查
厄尔诺
. 如果
厄尔诺
strerror(errno)
以获取描述性错误消息。)
你可能也注意到了
sizeof barray->data[0]
巴拉伊
sizeof
数据
成员,而不更改任何其他代码。
要将数据附加到这样一个字节数组中,我们可能希望能够指定是否预期进一步附加到同一个数组,或者这是否可能是最后一次附加,以便只需要确切的所需内存量。为了简单起见,我只在这里实现精确的大小版本。请注意,此函数返回指向(修改的)字节数组的指针:
bytearray *bytearray_append(bytearray *barray,
const void *from, const size_t size,
int exact)
{
size_t len = bytearray_len(barray) + size;
if (exact) {
barray = bytearray_resize(barray, len);
if (!barray)
return NULL; /* errno already set by bytearray_resize(). */
} else
if (bytearray_max(barray) < len) {
if (!exact) {
/* Apply growth policy */
if (len < 8)
len = 8;
else
if (len < 4194304)
len = (3 * len) / 2;
else
len = (len | 2097151) + 2097153 - 24;
}
barray = bytearray_resize(barray, len);
if (!barray)
return NULL; /* errno already set by the bytearray_resize() call */
}
if (size) {
memmove(barray->data + barray->len, from, size);
barray->len += size;
}
return barray;
}
这次,我们宣布
bytearray *barray
,因为我们换了地方
指向函数中的。如果第四个参数,
final
,则得到的字节数组正好是所需的大小;否则将应用增长策略。