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

时间:2015-05-11 07:43:57

标签: c pointers struct

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},
};

我正在使用微控制器,我不想使用malloc。另外,对我而言,收集结构中的所有内容看起来更好,而不是将存储作为Context之外的单独变量。

[EDIT1] 我已经对它进行了测试,它编译并运行,如my_context.my_storagemy_context.buffer.storage的指针相同,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] 在后来删除的答案中,我被引用到C99标准部分6.7.8-19"初始化应在初始化器列表顺序中进行..."这是否意味着

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

保证安全吗?我这样解释。

[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

2 个答案:

答案 0 :(得分:2)

是的,这很好。假设my_context具有自动存储持续时间,其生命周期从进入相关块开始,并且在其生命周期内具有恒定地址( 6.2.4对象的存储持续时间 / 2)。 (如果它具有静态或线程存储持续时间,则其生命周期分别延长整个程序或线程的持续时间。)

因此my_context.my_storagemy_context的生命周期内也有一个常量地址,所以将其地址(通过数组到指针衰减)用于my_context.buffer.storage的初始化将给出与my_context初始化完成后的值相同。

另请注意,my_context范围从其声明完成的点开始,它刚好在初始化程序的=之前,因此请在其中引用它它的初始化程序也没问题。

答案 1 :(得分:1)

这与指定的初始值设定项和初始化顺序没有任何关系。你实际问的是这样的事情是否明确定义:

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

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

是的,我不明白为什么不应该这样。在尝试初始化之前,编译器必须在内存中的地址上分配s。分配或初始化结构成员的顺序无关紧要。

但是,编写一个结构的取决于同一结构的另一个的初始化列表是不安全的! C11 6.7.9 / 23说:

  

初始化列表表达式的评估是   不确定地相互之间进行排序,从而对   发生任何副作用的顺序是未指定的。

为变量赋值是“副作用”。所以这样的代码是不安全的:

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

struct_t s = {0, s.val1};

因为编译器可以评估初始化列表的两个表达式,如下所示:

  • s.val1 - &gt;评估val1的内容(垃圾,尚未初始化)
  • 0 - &gt;评估为0
  • 将评估值(0)写入val1,保证在之前发生:
  • 将评估值(垃圾)写入val2

因此,即使初始化顺序得到保证,初始化列表的评估顺序也不是。虽然编译器当然可能已经决定首先评估0表达式,然后一切都会正常工作。

最重要的是,不要写出模糊的初始化列表。