我可以通过为封闭结构分配更多 space 来 "over-extend" 一个数组吗?
Can I "over-extend" an array by allocating more space to the enclosing struct?
老实说,这样的代码有效还是会产生UB?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct __attribute__((__packed__)) weird_struct
{
int some;
unsigned char value[1];
};
int main(void)
{
unsigned char text[] = "Allie has a cat";
struct weird_struct *ws =
malloc(sizeof(struct weird_struct) + sizeof(text) - 1);
ws->some = 5;
strcpy(ws->value, text);
printf("some = %d, value = %s\n", ws->some, ws->value);
free(ws);
return 0;
}
我从不认为它对这样的事情有效,但似乎 SystemV 消息队列确实可以做到这一点:see the man page.
那么,如果 SysV 消息队列可以做到这一点,也许我也可以做到这一点?我想我会发现这对通过网络发送数据很有用(因此 __attribute__((__packed__))
)。
或者,也许这是 SysV 消息队列的特定保证,我不应该在其他地方做类似的事情?或者,也许这个技术可以使用,只是我做错了?我想我最好问一下。
malloc(sizeof(struct weird_struct) + sizeof(text) - 1)
中的这个- 1
是因为我考虑到由于unsigned char value[1]
无论如何分配了一个字节所以我可以从sizeof(text)
中减去它。
标准的 C 方法(自 C99
)执行此操作将使用 flexible array member。该结构的最后一个成员需要是不完整的数组类型,您可以在运行时分配所需的内存量。
类似
struct __attribute__((__packed__)) weird_struct
{
int some;
unsigned char value [ ]; //nothing, no 0, no 1, no nothing...
};
以后
struct weird_struct *ws =
malloc(sizeof(struct weird_struct) + strlen("this to be copied") + 1);
或
struct weird_struct *ws =
malloc(sizeof(struct weird_struct) + sizeof("this to be copied"));
会完成任务的。
相关,引用 C11
标准,章节 §6.7.2.1
As a special case, the last element of a structure with more than one named member may
have an incomplete array type; this is called a flexible array member. In most situations,
the flexible array member is ignored. In particular, the size of the structure is as if the
flexible array member were omitted except that it may have more trailing padding than
the omission would imply. However, when a .
(or ->
) operator has a left operand that is
(a pointer to) a structure with a flexible array member and the right operand names that
member, it behaves as if that member were replaced with the longest array (with the same
element type) that would not make the structure larger than the object being accessed; the
offset of the array shall remain that of the flexible array member, even if this would differ
from that of the replacement array. If this array would have no elements, it behaves as if
it had one element but the behavior is undefined if any attempt is made to access that
element or to generate a pointer one past it.
关于单元素数组的用法,来自online gcc manual page for zero-length array support option
struct line {
int length;
char contents[0];
};
struct line *thisline = (struct line *)
malloc (sizeof (struct line) + this_length);
thisline->length = this_length;
In ISO C90, you would have to give contents
a length of 1, which means either you waste space or complicate the argument to malloc.
这也回答了 malloc()
参数中的 -1
部分,因为 sizeof(char)
在 C 中保证是 1
。
如果代码访问超出规定范围的数组对象,标准允许实现以他们认为合适的任何方式行事,即使代码拥有将被访问的存储空间。据我所知,这条规则旨在允许编译器给出如下内容:
struct s1 { char arr[4]; char y; } *p;
int x;
...
p->y = 1;
p->arr[x] = 2;
return p->y;
将其视为等同于:
struct s1 { char arr[4]; char y; } *p;
int x;
...
p->arr[x] = 2;
p->y = 1;
return 1;
避免额外的加载步骤,而不必悲观地考虑 x
可能等于 4 的可能性。高质量的编译器应该能够识别某些涉及访问超出其规定范围的数组的构造(例如那些涉及指向以单元素数组作为其最后一个元素的结构的指针)并明智地处理它们,但标准中没有任何内容要求他们这样做,并且一些编译器作者采取的态度是允许编译器以无意义的方式运行被解释为这样做的邀请。我认为即使对于 x==4
情况(这意味着编译器必须允许它修改 y
的可能性),行为也会被定义,如果数组写入是通过类似的方式处理的:(char*)(struct s1*)(p->arr)[x] = 2;
但标准并不清楚是否需要强制转换为 struct s1*
。
老实说,这样的代码有效还是会产生UB?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct __attribute__((__packed__)) weird_struct
{
int some;
unsigned char value[1];
};
int main(void)
{
unsigned char text[] = "Allie has a cat";
struct weird_struct *ws =
malloc(sizeof(struct weird_struct) + sizeof(text) - 1);
ws->some = 5;
strcpy(ws->value, text);
printf("some = %d, value = %s\n", ws->some, ws->value);
free(ws);
return 0;
}
我从不认为它对这样的事情有效,但似乎 SystemV 消息队列确实可以做到这一点:see the man page.
那么,如果 SysV 消息队列可以做到这一点,也许我也可以做到这一点?我想我会发现这对通过网络发送数据很有用(因此 __attribute__((__packed__))
)。
或者,也许这是 SysV 消息队列的特定保证,我不应该在其他地方做类似的事情?或者,也许这个技术可以使用,只是我做错了?我想我最好问一下。
malloc(sizeof(struct weird_struct) + sizeof(text) - 1)
中的这个- 1
是因为我考虑到由于unsigned char value[1]
无论如何分配了一个字节所以我可以从sizeof(text)
中减去它。
标准的 C 方法(自 C99
)执行此操作将使用 flexible array member。该结构的最后一个成员需要是不完整的数组类型,您可以在运行时分配所需的内存量。
类似
struct __attribute__((__packed__)) weird_struct
{
int some;
unsigned char value [ ]; //nothing, no 0, no 1, no nothing...
};
以后
struct weird_struct *ws =
malloc(sizeof(struct weird_struct) + strlen("this to be copied") + 1);
或
struct weird_struct *ws =
malloc(sizeof(struct weird_struct) + sizeof("this to be copied"));
会完成任务的。
相关,引用 C11
标准,章节 §6.7.2.1
As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. In most situations, the flexible array member is ignored. In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply. However, when a
.
(or->
) operator has a left operand that is (a pointer to) a structure with a flexible array member and the right operand names that member, it behaves as if that member were replaced with the longest array (with the same element type) that would not make the structure larger than the object being accessed; the offset of the array shall remain that of the flexible array member, even if this would differ from that of the replacement array. If this array would have no elements, it behaves as if it had one element but the behavior is undefined if any attempt is made to access that element or to generate a pointer one past it.
关于单元素数组的用法,来自online gcc manual page for zero-length array support option
struct line { int length; char contents[0]; }; struct line *thisline = (struct line *) malloc (sizeof (struct line) + this_length); thisline->length = this_length;
In ISO C90, you would have to give
contents
a length of 1, which means either you waste space or complicate the argument to malloc.
这也回答了 malloc()
参数中的 -1
部分,因为 sizeof(char)
在 C 中保证是 1
。
如果代码访问超出规定范围的数组对象,标准允许实现以他们认为合适的任何方式行事,即使代码拥有将被访问的存储空间。据我所知,这条规则旨在允许编译器给出如下内容:
struct s1 { char arr[4]; char y; } *p;
int x;
...
p->y = 1;
p->arr[x] = 2;
return p->y;
将其视为等同于:
struct s1 { char arr[4]; char y; } *p;
int x;
...
p->arr[x] = 2;
p->y = 1;
return 1;
避免额外的加载步骤,而不必悲观地考虑 x
可能等于 4 的可能性。高质量的编译器应该能够识别某些涉及访问超出其规定范围的数组的构造(例如那些涉及指向以单元素数组作为其最后一个元素的结构的指针)并明智地处理它们,但标准中没有任何内容要求他们这样做,并且一些编译器作者采取的态度是允许编译器以无意义的方式运行被解释为这样做的邀请。我认为即使对于 x==4
情况(这意味着编译器必须允许它修改 y
的可能性),行为也会被定义,如果数组写入是通过类似的方式处理的:(char*)(struct s1*)(p->arr)[x] = 2;
但标准并不清楚是否需要强制转换为 struct s1*
。