Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/66.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/oracle/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 分配太少的空间是否安全(如果您知道您不需要它)?_C_Malloc_Flexible Array Member - Fatal编程技术网

C 分配太少的空间是否安全(如果您知道您不需要它)?

C 分配太少的空间是否安全(如果您知道您不需要它)?,c,malloc,flexible-array-member,C,Malloc,Flexible Array Member,因此,C99为常用的“灵活数组成员”黑客提供了便利,允许我们制作structs,以满足我们的大小要求。我怀疑在大多数理智的实现中这样做是完全安全的,但是如果我们知道在某些情况下不需要结构的某些成员,那么在C中“分配不足”是否合法 抽象例子 假设我有一个类型: struct a { bool data_is_x; void * data; size_t pos; }; 如果data\u是x,则data的类型是需要使用pos成员的类型。否则,使用此struct的函数将不需要此str

因此,C99为常用的“灵活数组成员”黑客提供了便利,允许我们制作
struct
s,以满足我们的大小要求。我怀疑在大多数理智的实现中这样做是完全安全的,但是如果我们知道在某些情况下不需要结构的某些成员,那么在C中“分配不足”是否合法

抽象例子 假设我有一个类型:

struct a {
  bool   data_is_x;
  void * data;
  size_t pos;
};
如果
data\u是x
,则
data
的类型是需要使用
pos
成员的类型。否则,使用此
struct
的函数将不需要此
struct
的特定副本的
pos
成员。本质上,
struct
携带关于它是否有
pos
成员的信息,并且该信息在
struct
的生命周期内不会改变(除了邪恶的恶作剧,它几乎可以破坏任何东西)。可以说:

struct a *a = malloc(data_is_x ? sizeof(struct a) : offsetof(struct a, pos));
只有在需要时,哪个会为
pos
成员分配空间?或者使用对
struct
指针来说太小的强制转换空间是否违反了约束,即使您从未使用过相关的成员

具体例子 我的实际用例有点复杂;这主要是为了让你明白我为什么要这么做:

typedef struct {
  size_t size;
  void * data;
  size_t pos;
} mylist;
mylist *list = malloc(size ? sizeof(mylist) : offsetof(mylist, pos));
mylist\u create
的代码指定,对于
size>0
data
是一个连续数据数组,其长度为
size
项(无论项是什么),但对于
size==0
则是包含项的双链接列表的当前节点。使用
mylist
s的所有函数都将检查
size==0
。如果是这样,他们将把数据作为一个链表处理,“当前”索引是
data
指向的节点。如果没有,他们将以数组的形式处理数据,并将“当前”索引存储在
pos

现在,如果
size==0
我们并不真正需要
pos
成员,但是如果
size>0
我们会。所以我的问题是,这样做合法吗:

typedef struct {
  size_t size;
  void * data;
  size_t pos;
} mylist;
mylist *list = malloc(size ? sizeof(mylist) : offsetof(mylist, pos));

如果我们保证(对未定义行为的惩罚)当
size==0
时,我们不会尝试(或需要)访问
pos
成员?或者它是否在标准的某个地方说,它甚至可以考虑这样做?

malloc
本身根本不关心为一个结构分配多少内存,未定义的是块外内存的解引用。从C99
6.5.3.2地址和间接运算符

如果为指针指定了无效值,则一元*运算符的行为未定义

7.20.3内存管理功能中,我们发现(我的斜体):

如果分配成功,则返回的指针将适当对齐,以便可以将其分配给指向任何类型对象的指针,然后用于访问所分配空间中的此类对象或此类对象的数组(直到空间被显式释放)

因此,您可以执行以下操作:

typedef struct { char ch[100]; } ch100;
ch100 *c = malloc (1);
而且,如果您只尝试使用
c->ch[0]
做任何事情,这是完全可以接受的


对于您的具体示例,假设您关心的是存储空间,我不太确定我是否会那么担心。如果您出于其他原因而担心,请忽略这一点,特别是因为标准没有强制要求包含其中的假设

根据我的理解,你有一个结构:

typedef struct {
  size_t size;
  void * data;
  size_t pos;
} mylist;
如果只想使用
数据
,其中
大小
为0,并且
数据
pos
都大于0。这就排除了将
数据
pos
放在一个联合体中的使用

大量的
malloc
实现会将您请求的空间四舍五入到16字节的倍数(或更大的2次方),以缓解内存碎片问题。这当然不是标准所要求的,但它是相当常见的

假设(例如)32位指针和
size\t
,12个字节的结构很可能占用16字节的arena头和16字节的数据块。即使您只要求8个字节(即没有
pos
),该块仍然是16字节

如果您有64位指针和
size\t
类型,则可能会有所不同-有
pos
的为24字节,没有的为16字节


但即便如此,除非您分配了大量这些结构,否则这可能不是问题。

这是完全合法的,但您可能应该通过使用两个结构来减少其模糊性,当您阅读时:

struct leaf_node {
    size_t size;
    void *data;
    size_t pos;
};
struct linked_node {
    size_t size;
    void *next;
};

void *in = ...;

if (*(size_t*)(in) == 0) {
    struct leaf_node *node = in;
    ...
} else {
    struct linked_node *node = in;
    ....
}
这与paxdiablo引用的标准更加密切相关,即可以将指针强制转换为任何数据指针。如果这样做,您还将始终确保将其转换为适合分配的缓冲区的结构(这是一项不必要但方便的壮举)

paxdiablo所说的在32位系统上最小大小为16字节的说法通常是正确的,但您仍然可以分配大块来解决这个问题

在32位系统上,链接的_节点将使用8个字节。您必须使用池来从您试图做的事情中获益

struct leaf_node *leaf_pool = malloc(N*sizeof(struct leaf_node));
struct linked_node *linked_pool = malloc(N*sizeof(struct linked_node));
当然,您永远不应该重新定位池,而是根据需要分配新池并重用节点。在这种情况下,单个
leaf\u节点将使用12个字节

同样的情况也适用于
链接的_节点
,如果在池中分配它们,将使用8个字节而不是16个字节

只要您的结构没有在GCC中使用
\uuuuu属性((打包))
,就不会出现性能瓶颈,在这种情况下,您的结构可能会非常不协调。特别是如果您的结构中有一个额外的字符,使其大小为13字节

现在如果w