如何增加指向具有可变长度数组的结构的指针?
How do I increment the pointer to a structure which has variable length array?
我在访问指向具有可变大小数组的结构(传递给函数的数组)的指针时遇到问题。
char name[20];
char value[20];
}param_t;
typedef struct object {
char name[20];
int no_of_params;
param_t params[];
};
int main()
{
int no_of_objs = 3, no_of_params = 5;
object_t *objs = malloc(no_of_objs * (sizeof(object_t) + (no_of_params * sizeof(param_t)) );
//...
//...
objs++; //Increment to point to the next item <-- Problem: ignores params[] size
// blah
// blah
// blah
我已经分配内存有 3 个 object_t,每个 object_t 存储 5 个 param_t。
内存分配很好,我可以为成员设置值。
objs[0] 非常好。
现在,如果我增加 objs (objs++) 以指向下一个对象,它实际上指向前一个对象的参数地址。它完全忽略了 params[]。
现在,如果我为第二个 *objs 设置值,它实际上会覆盖前一个 *objs.
的 params[]
我的问题是:这是 C 编译器中的错误吗?
如果没有,如何遍历objs?
这不是 C 编译器中的错误,只是使用可变长度数组的缺点。
允许编译器将 struct
的可变长度数组成员视为对 sizeof
没有贡献。
这有效地破坏了指针算法,这意味着您不能像您所说的那样轻松 "traverse the objects"。
恐怕这就是生活。从 C11 中删除可变长度数组作为强制要求的充分理由。
根据 Bathsheba 的回答,您最好将其实现为指向对象的指针数组:
int no_of_objs = 3, no_of_params = 5;
object_t **objs = malloc(no_of_objs * sizeof(object_t *);
for (int i=0; i<no_of_objs; i++)
objs[i]= malloc ((sizeof(object_t) + (no_of_params * sizeof(param_t)) );
如果您确实需要将此内存分配为单个块,则需要确定适当对齐的元素大小:
size_t object_t_size(size_t nparams)
{
return (offsetof(object_t, params[nparams]) | (_Alignof(object_t) - 1)) + 1;
}
这里,nparams
是分配给object_t
的params
个灵活数组成员的param_t
个元素数。
为了向前遍历包含具有 params
个灵活数组成员的 object_t
集合的内存块,您需要一种方法来找到下一个 object_t
的位置:
object_t *next_object_t(object_t *obj, size_t nparams)
{
return (object_t *)((char *)obj + object_t_size(nparams));
}
即使 object_t
具有不同的大小(包括它们的 params
灵活数组成员)也可以使用以上方法,只要知道每个个体 object_t
的大小并且分配后不会改变(或者至少,在没有代码重新分配内存和随机播放内容以扩展或减小特定 object_t
的大小时)。
如果集合中的所有object_t
大小相同(包括其params
灵活数组成员的固定大小),则可以获得指向的指针第i个元素:
object_t *indexed_object_t(object_t *base, size_t nparams, size_t idx)
{
return (object_t *)((char *)base + object_t_size(nparams) * idx);
}
请注意,上述函数不适用于从任意元素向后索引。这可以通过为索引使用带符号类型并将 object_t_size
的 return 值转换为适当宽的带符号整数类型来解决:
object_t *indexed_object_t(object_t *base, size_t nparams, int idx)
{
return (object_t *)((char *)base + (intmax_t)object_t_size(nparams) * idx);
}
我在访问指向具有可变大小数组的结构(传递给函数的数组)的指针时遇到问题。
char name[20];
char value[20];
}param_t;
typedef struct object {
char name[20];
int no_of_params;
param_t params[];
};
int main()
{
int no_of_objs = 3, no_of_params = 5;
object_t *objs = malloc(no_of_objs * (sizeof(object_t) + (no_of_params * sizeof(param_t)) );
//...
//...
objs++; //Increment to point to the next item <-- Problem: ignores params[] size
// blah
// blah
// blah
我已经分配内存有 3 个 object_t,每个 object_t 存储 5 个 param_t。
内存分配很好,我可以为成员设置值。 objs[0] 非常好。 现在,如果我增加 objs (objs++) 以指向下一个对象,它实际上指向前一个对象的参数地址。它完全忽略了 params[]。 现在,如果我为第二个 *objs 设置值,它实际上会覆盖前一个 *objs.
的 params[]我的问题是:这是 C 编译器中的错误吗? 如果没有,如何遍历objs?
这不是 C 编译器中的错误,只是使用可变长度数组的缺点。
允许编译器将 struct
的可变长度数组成员视为对 sizeof
没有贡献。
这有效地破坏了指针算法,这意味着您不能像您所说的那样轻松 "traverse the objects"。
恐怕这就是生活。从 C11 中删除可变长度数组作为强制要求的充分理由。
根据 Bathsheba 的回答,您最好将其实现为指向对象的指针数组:
int no_of_objs = 3, no_of_params = 5;
object_t **objs = malloc(no_of_objs * sizeof(object_t *);
for (int i=0; i<no_of_objs; i++)
objs[i]= malloc ((sizeof(object_t) + (no_of_params * sizeof(param_t)) );
如果您确实需要将此内存分配为单个块,则需要确定适当对齐的元素大小:
size_t object_t_size(size_t nparams)
{
return (offsetof(object_t, params[nparams]) | (_Alignof(object_t) - 1)) + 1;
}
这里,nparams
是分配给object_t
的params
个灵活数组成员的param_t
个元素数。
为了向前遍历包含具有 params
个灵活数组成员的 object_t
集合的内存块,您需要一种方法来找到下一个 object_t
的位置:
object_t *next_object_t(object_t *obj, size_t nparams)
{
return (object_t *)((char *)obj + object_t_size(nparams));
}
即使 object_t
具有不同的大小(包括它们的 params
灵活数组成员)也可以使用以上方法,只要知道每个个体 object_t
的大小并且分配后不会改变(或者至少,在没有代码重新分配内存和随机播放内容以扩展或减小特定 object_t
的大小时)。
如果集合中的所有object_t
大小相同(包括其params
灵活数组成员的固定大小),则可以获得指向的指针第i个元素:
object_t *indexed_object_t(object_t *base, size_t nparams, size_t idx)
{
return (object_t *)((char *)base + object_t_size(nparams) * idx);
}
请注意,上述函数不适用于从任意元素向后索引。这可以通过为索引使用带符号类型并将 object_t_size
的 return 值转换为适当宽的带符号整数类型来解决:
object_t *indexed_object_t(object_t *base, size_t nparams, int idx)
{
return (object_t *)((char *)base + (intmax_t)object_t_size(nparams) * idx);
}