简体   繁体   English

将成员地址分配给struct中的其他成员

[英]Assign member address to other member in struct

Is the following safe in C? C中的以下是安全的吗?

struct Buffer {
  size_t size;
  int8_t *storage;
};

struct Context {
  struct Buffer buffer;
  int8_t my_storage[10];
};

struct Context my_context = {
  .buffer = {
    .size = 0,
    .storage = my_context.my_storage,
  },
  .my_storage = {0},
};

I am working with a micro controller and I don't want to have to use malloc. 我正在使用微控制器,我不想使用malloc。 Also, to me it looks better to collect everything in the struct rather than have the storage as a separate variable outside the Context. 另外,对我而言,收集结构中的所有内容而不是将存储作为Context之外的单独变量看起来更好。

[edit1] I have tested it and it compiles and works, as in the pointers to my_context.my_storage and my_context.buffer.storage are the same, with gcc (Debian 4.7.2-5) 4.7.2 on Linux ... 3.2.0-4-amd64 #1 SMP Debian 3.2.65-1+deb7u2 x86_64 GNU/Linux [edit1]我已经测试了它并且它编译和工作,就像指向my_context.my_storagemy_context.buffer.storage的指针一样,在Linux ... 3.2.0-4-amd64 #1 SMP Debian 3.2.65-1+deb7u2 x86_64 GNU/Linux上使用gcc (Debian 4.7.2-5) 4.7.2 Linux ... 3.2.0-4-amd64 #1 SMP Debian 3.2.65-1+deb7u2 x86_64 GNU/Linux

[edit2] In an answer that was later deleted I was referred to C99 standard section 6.7.8-19 "The initialization shall occur in initializer list order..." Would that mean that [edit2]在后来删除的答案中,我被引用到C99标准第6.7.8-19节“初始化应在初始化程序列表顺序中发生......”这是否意味着

struct Context my_context = {
  .my_storage = {0},
  .buffer = {
    .size = 0,
    .storage = my_context.my_storage,
  },
};

Is guaranteed to be safe? 保证安全吗? I interpret it that way. 我这样解释。

[edit3] Below is a full working example. [edit3]以下是一个完整的工作示例。

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>

struct Buffer {
  size_t size;
  int8_t *storage;
};

struct Context {
  struct Buffer buffer;
  int8_t my_storage[10];
};

struct Context my_context = {
  .buffer = {
    .size = 0,
    .storage = my_context.my_storage,
  },
  .my_storage = {0},
};

int
main(void)
{
  printf ("ptr points to: %" PRIXPTR "\n", (uintptr_t)my_context.buffer.storage);
  printf ("storage is at: %" PRIXPTR "\n", (uintptr_t)my_context.my_storage);
}

>> ./test
ptr points to: 600950
storage is at: 600950

Yes, this is fine. 是的,这很好。 Assuming my_context has automatic storage duration, its lifetime begins on entry to the associated block, and during its lifetime it has a constant address ( 6.2.4 Storage durations of objects /2). 假设my_context具有自动存储持续时间,其生命周期从进入相关块开始,并且在其生命周期内具有恒定地址( 6.2.4对象的存储持续时间 / 2)。 (If it has static or thread storage duration instead, then its lifetime extends for the duration of the entire program or thread respectively). (如果它具有静态或线程存储持续时间,则其生命周期分别延长整个程序或线程的持续时间)。

It follows that my_context.my_storage also has a constant address over the lifetime of my_context , so taking its address (via array-to-pointer decay) for the initialization of my_context.buffer.storage will give the same value as it would after the initialization of my_context is complete. 因此my_context.my_storagemy_context的生命周期内也有一个常量地址,因此将其地址(通过数组到指针衰减)用于初始化my_context.buffer.storage将得到与初始化后相同的值。 my_context已完成。

Also note that the scope of my_context begins at the point its declaration is complete, which is just prior to the = of the initializer, so referring to it within its initializer is also fine. 还要注意my_context范围从其声明完成的点开始,它刚好在初始化器的=之前,所以在它的初始化器中引用它也没关系。

This doesn't really have anything to do with designated initializers and the order of initialization. 这与指定的初始化程序和初始化顺序没有任何关系。 What you are actually asking is if something like this is well-defined: 您实际要问的是,这样的事情是否定义明确:

typedef struct
{
  int* ptr;
  int  val; 
} struct_t;

struct_t s = {&s.val, 0};

And yes, I don't see why it shouldn't be. 是的,我不明白为什么不应该这样。 The compiler has to allocate s at an address in memory before attempting to initialize it. 在尝试初始化之前,编译器必须在内存中的地址处分配s The order in which the struct members is allocated or initialized shouldn't matter. 分配或初始化结构成员的顺序无关紧要。


However , writing initialization lists where one value of a struct depends on another value of the same struct is not safe! 但是 ,编写初始化列表,其中struct的一个依赖于同一结构的另一个是不安全的! C11 6.7.9/23 says: C11 6.7.9 / 23说:

The evaluations of the initialization list expressions are indeterminately sequenced with respect to one another and thus the order in which any side effects occur is unspecified. 初始化列表表达式的评估相对于彼此不确定地排序,因此未指定任何副作用发生的顺序。

Assignment of a value to a variable is a "side effect". 将值赋值给变量是“副作用”。 So code like this is unsafe: 所以这样的代码是不安全的:

typedef struct
{
  int  val1; 
  int  val2;
} struct_t;

struct_t s = {0, s.val1};

Because the compiler may evaluate the two expressions of the initializer list like this: 因为编译器可能会像这样评估初始化列表的两个表达式:

  • s.val1 -> evaluate to the contents of val1 (garbage, it has not been initialized) s.val1 - >计算val1的内容(垃圾,尚未初始化)
  • 0 -> evaluate to 0 0 - >评估为0
  • write evaluated value (0) to val1, guaranteed to happen before: 将评估值(0)写入val1,保证在之前发生:
  • write evaluated value (garbage) to val2 将评估值(垃圾)写入val2

So even though the initialization order is guaranteed, the order of evaluation of the initialization list is not. 因此,即使初始化顺序得到保证,初始化列表的评估顺序也不是。 Though of course the compiler may have decided to evaluate the 0 expression first and then everything would have worked fine. 虽然编译器当然可能已经决定首先评估0表达式,然后一切都会正常工作。

The bottom line is, don't write obscure initialization lists. 底线是,不要写出模糊的初始化列表。

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

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