简体   繁体   English

malloc 的这种使用会导致“可选”结构字段吗?

[英]Could this use of malloc lead to an “optional” struct field?

I was implementing a structure in which I needed (at runtime) to have an optional field.我正在实现一个结构,我需要(在运行时)有一个可选字段。
So I thought about this:所以我想到了这个:

//...
#include <stdlib.h>

struct test {
  int x;  // Must be
  int y;  // Optional (Must be the last field..(?))
};

int main(int argc, char **argv) {
    // With the optional field
    struct test *a = malloc(sizeof(*a));
    a->x = 11;
    a->y = 55;
    
    // Without the optional field
    struct test *b = malloc(sizeof(*b) - sizeof(int));
    b->x = 22;
    
    // ...
    
    free(a);
    free(b);
    return 0;
}

Could this code do what I ask?这段代码可以按我的要求做吗?
Possibly adding a bit field to check if there is the optional field or not.可能添加一个位字段来检查是否有可选字段。

Also, if the proposed solution works, if this were implemented for a list of multiple items (> 100000), would it be better to do it to save memory?此外,如果建议的解决方案有效,如果这是针对多个项目(> 100000)的列表实施的,那么保存 memory 会更好吗?

Could this code do what I ask?这段代码可以按我的要求做吗?

Well, it could , but you cannot rely on that.好吧,它可以,但你不能依赖它。 Do not do this;不要这样做; it is not a way to write correct programs.这不是编写正确程序的方法。

When you write b->x = 22;当你写b->x = 22; , the compiler is entitled to behave as if there were a whole struct test at b . ,编译器有权表现得好像在b处有一个完整的struct test You may be thinking, “I am just putting 22 in the bytes for the member x ,” but the compiler may use a “store eight bytes” instruction:您可能会想,“我只是将 22 放入成员x的字节中”,但编译器可能会使用“存储 8 个字节”指令:

  • Consider some architecture where memory is organized into eight-byte groups.考虑一些架构,其中 memory 被组织成八字节组。 The bus can only read and write whole eight-byte chunks.总线只能读取和写入整个 8 字节块。
  • Since there is no way to write four bytes in hardware, writing four bytes to memory requires reading eight bytes, manipulating them in processor registers to insert the desired values in four of the bytes, and writing eight bytes back to memory.由于无法在硬件中写入四个字节,因此将四个字节写入 memory 需要读取八个字节,在处理器寄存器中操作它们以在其中四个字节中插入所需的值,然后将八个字节写回 memory。
  • The compiler wants to optimize b->x = 22;编译器要优化b->x = 22; , and it knows y has not been set yet, so it is allowed to have any value. ,并且它知道y尚未设置,因此允许它具有任何值。 So, instead of using an inefficient write-four-byte sequence, the compiler generates an eight-byte store that puts 22 in b->x and 0 in b->y .因此,编译器不会使用低效的写入四字节序列,而是生成一个八字节存储,将 22 放入b->x并将 0 放入b->y

Then this fails because the compiler has just written 0 to memory that might be in use for something else because it is not part of the space you allocated for b .然后这会失败,因为编译器刚刚将 0 写入 memory 可能正在用于其他用途,因为它不是您为b分配的空间的一部分。

“If you lie to the compiler, it will get its revenge.” “如果你对编译器撒谎,它就会报仇雪恨。” — Henry Spencer — 亨利·斯宾塞

What you're attempting doesn't conform to the C standard because you're attempting to use an object of type struct test that doesn't have enough memory allocated for it, even though you're only accessing the fields for which memory was allocated. What you're attempting doesn't conform to the C standard because you're attempting to use an object of type struct test that doesn't have enough memory allocated for it, even though you're only accessing the fields for which memory was分配。 It might work but you can't rely on that.可能有效,但你不能依赖它。

What you can do is make use of a flexible array member :可以做的是使用灵活的数组成员

struct test {
  int x;
  int y[];
};

In a struct like this, sizeof(struct test) doesn't include the last member.在这样的结构中, sizeof(struct test)不包括最后一个成员。 You can use such a struct by allocating space for the struct plus as many array elements of the last member that you want.您可以通过为结构分配空间以及所需的最后一个成员的尽可能多的数组元素来使用这样的结构。 For example:例如:

struct test *b = malloc(sizeof(*b) + sizeof(int));
b->x = 1;
b->y[0] = 2;

You'll need to use array indexing to access the last member, but this is a way to do what you want in a standard-conforming manner.您需要使用数组索引来访问最后一个成员,但这是一种以符合标准的方式执行所需操作的方法。

Then in the case you don't want the last member, you do this:然后,如果您不想要最后一个成员,请执行以下操作:

struct test *b = malloc(sizeof(*b));
b->x = 1;

I think your proposed solution is dangerous.我认为您提出的解决方案很危险。 Use two different structs:使用两种不同的结构:

struct test_x {
   int x;
};

struct test_xy {
  int x;
  int y;
};

Either have two arrays or store a void * to either along with a discriminator (tagged pointer for instance).要么有两个 arrays 要么将 void * 与鉴别器(例如标记指针)一起存储到其中一个。 The other option is use a pointer for the optional element but sizeof(int *) is the same as sizeof(int) at least on my box, so that only makes things larger.另一个选项是对可选元素使用指针,但 sizeof(int *) 至少在我的盒子上与 sizeof(int) 相同,这样只会使事情变大。

Consider a column layout if all the y members are optional, or you can sort the data so all the xy elements comes first:如果所有 y 成员都是可选的,请考虑列布局,或者您可以对数据进行排序,以便所有 xy 元素排在第一位:

struct test_column {
  int *x;
  int *y;
};

struct test_column t = {
  .x = malloc(100000 * sizeof(int)),
  .y = 0

It doesn't help you in case but unions are the standard way to two structs share memory so size of each element is max(sizeof(test_xy), sizeof(test_x)) instead of sizeof(test_xy) + sizeof(test_x).它对您没有帮助,但联合是两个结构共享 memory 的标准方式,因此每个元素的大小是 max(sizeof(test_xy), sizeof(test_x)) 而不是 sizeof(test_xy) + sizeof(test_x)。

Finally, consider compression especially if you use the test_column format.最后,考虑压缩,特别是如果您使用 test_column 格式。

I state that I am not an expert in C, but I am studying it.我 state 我不是 C 方面的专家,但我正在研究它。
I was implementing a structure in which I needed (at runtime) to have an optional field.我正在实现一个结构,我需要(在运行时)有一个可选字段。
So I thought about this:所以我想到了这个:

//...
#include <stdlib.h>

struct test {
  int x;  // Must be
  int y;  // Optional (Must be the last field..(?))
};

int main(int argc, char **argv) {
    // With the optional field
    struct test *a = malloc(sizeof(*a));
    a->x = 11;
    a->y = 55;
    
    // Without the optional field
    struct test *b = malloc(sizeof(*b) - sizeof(int));
    b->x = 22;
    
    // ...
    
    free(a);
    free(b);
    return 0;
}

Could this code do what I ask?这段代码可以按我的要求做吗?
Possibly adding a bit field to check if there is the optional field or not...可能添加一个位字段来检查是否有可选字段...


Also, if the proposed solution works, if this were implemented for a list of multiple items (> 100000), would it be better to do it to save memory?此外,如果建议的解决方案有效,如果这是针对多个项目(> 100000)的列表实施的,那么保存 memory 会更好吗?


Thanks everyone, in advance.谢谢大家,提前。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM