将成员地址分配给结构中的其他成员
Assign member address to other member in 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。另外,对我来说,收集结构中的所有内容比将存储作为上下文之外的单独变量更好。
[编辑1]
我已经测试过它并且它可以编译和工作,因为指向 my_context.my_storage
和 my_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
上
[编辑2]
在后来被删除的答案中,我提到了 C99 标准第 6.7.8-19 节 "The initialization shall occur in initializer list order..." 这是否意味着
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
是的,这很好。假设 my_context
具有自动存储持续时间,其生命周期从进入关联块时开始,并且在其生命周期内具有恒定地址(6.2.4 对象的存储持续时间/2 ). (如果它具有静态或线程存储持续时间,则其生命周期分别延长整个程序或线程的持续时间)。
由此可见 my_context.my_storage
在 my_context
的生命周期内也有一个常量地址,因此将其地址(通过数组到指针衰减)用于 my_context.buffer.storage
的初始化将给出与 my_context
初始化完成后相同的值。
另请注意,my_context
的 scope 开始于其声明完成的点,即在初始化程序的 =
之前,所以在它的初始化器中引用它也是可以的。
这与指定初始化器和初始化顺序没有任何关系。您实际上要问的是这样的事情是否定义明确:
typedef struct
{
int* ptr;
int val;
} struct_t;
struct_t s = {&s.val, 0};
是的,我不明白为什么不应该这样。在尝试初始化之前,编译器必须在内存中的一个地址分配 s
。分配或初始化结构成员的顺序无关紧要。
However,编写初始化列表,其中结构的一个 value 依赖于另一个 value相同结构的是不安全的! 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.
给变量赋值是"side effect"。所以像这样的代码是不安全的:
typedef struct
{
int val1;
int val2;
} struct_t;
struct_t s = {0, s.val1};
因为编译器可能会像这样计算初始化列表的两个表达式:
- s.val1 -> 计算出val1的内容(垃圾,还没有初始化)
- 0 -> 评估为 0
- 将评估值 (0) 写入 val1,保证发生在:
- 将评估值(垃圾)写入val2
所以即使初始化顺序是有保证的,初始化列表的求值顺序也不是。当然,编译器可能已经决定首先评估 0 表达式,然后一切都会正常工作。
最重要的是,不要编写晦涩难懂的初始化列表。
下面的 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。另外,对我来说,收集结构中的所有内容比将存储作为上下文之外的单独变量更好。
[编辑1]
我已经测试过它并且它可以编译和工作,因为指向 my_context.my_storage
和 my_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
[编辑2] 在后来被删除的答案中,我提到了 C99 标准第 6.7.8-19 节 "The initialization shall occur in initializer list order..." 这是否意味着
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
是的,这很好。假设 my_context
具有自动存储持续时间,其生命周期从进入关联块时开始,并且在其生命周期内具有恒定地址(6.2.4 对象的存储持续时间/2 ). (如果它具有静态或线程存储持续时间,则其生命周期分别延长整个程序或线程的持续时间)。
由此可见 my_context.my_storage
在 my_context
的生命周期内也有一个常量地址,因此将其地址(通过数组到指针衰减)用于 my_context.buffer.storage
的初始化将给出与 my_context
初始化完成后相同的值。
另请注意,my_context
的 scope 开始于其声明完成的点,即在初始化程序的 =
之前,所以在它的初始化器中引用它也是可以的。
这与指定初始化器和初始化顺序没有任何关系。您实际上要问的是这样的事情是否定义明确:
typedef struct
{
int* ptr;
int val;
} struct_t;
struct_t s = {&s.val, 0};
是的,我不明白为什么不应该这样。在尝试初始化之前,编译器必须在内存中的一个地址分配 s
。分配或初始化结构成员的顺序无关紧要。
However,编写初始化列表,其中结构的一个 value 依赖于另一个 value相同结构的是不安全的! 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.
给变量赋值是"side effect"。所以像这样的代码是不安全的:
typedef struct
{
int val1;
int val2;
} struct_t;
struct_t s = {0, s.val1};
因为编译器可能会像这样计算初始化列表的两个表达式:
- s.val1 -> 计算出val1的内容(垃圾,还没有初始化)
- 0 -> 评估为 0
- 将评估值 (0) 写入 val1,保证发生在:
- 将评估值(垃圾)写入val2
所以即使初始化顺序是有保证的,初始化列表的求值顺序也不是。当然,编译器可能已经决定首先评估 0 表达式,然后一切都会正常工作。
最重要的是,不要编写晦涩难懂的初始化列表。