Clang 抱怨:"pointer is initialized by a temporary array"
Clang complains: "pointer is initialized by a temporary array"
我有一个不同长度的(指向)数组的数组,我了解到我可以使用复合文字来定义它:
const uint8_t *const minutes[] = {
(const uint8_t[]) {END},
(const uint8_t[]) {1, 2, 3, 4, 5 END},
(const uint8_t[]) {8, 9, END},
(const uint8_t[]) {10, 11, 12, END},
...
};
gcc 很好地接受了这个,但是 clang 说:pointer is initialized by a temporary array, which will be destroyed at the end of the full-expression
。这是什么意思?代码 似乎 可以工作,但是话又说回来,许多东西 似乎 在指向不再分配的内存时可以工作。这是我需要担心的事情吗? (最终我真的只需要它与 gcc 一起工作。)
更新:发生了一些可疑的事情。它说 here 那:
Compound literals yield lvalues. This means that you can take the address of a compound literal, which is the address of the unnamed object declared by the compound literal. As long as the compound literal does not have a const-qualified type, you can use the pointer to modify it.
`struct POINT *p;
p = &(struct POINT) {1, 1};
这个示例代码似乎正在做我想做的事情:一个指向由复合文字定义的东西的指针。那么clang报错信息合法吗?当使用 clang 或 gcc 编译时,这最终会指向未分配的内存吗?
更新 2:
发现一些 documentation:“在 C 中,复合文字指定一个具有静态或自动存储持续时间的未命名对象。在 C++ 中,复合文字指定一个临时对象,它只存在到其结束时full-expression." 所以 clang 发出警告似乎是正确的,gcc 可能也应该这样做,但即使 -Wall -Wextra
.
我无法猜出为什么从 C++ 中删除了一个有用的 C 特性,并且没有提供完成相同事情的优雅替代方法。
嗯,这意味着,这个表达式
(const uint8_t[]) {1, 2, 3, 4, 5 END},
创建一个临时对象——临时的,因为它没有任何名称可以持续到它所属的表达式之外——它在完整表达式的末尾被销毁,这意味着:
};
定义"the full expression",此时所有临时对象都将被销毁,指针数组minutes
保存指向destroyed 个对象,这就是编译器发出警告的原因。
希望对您有所帮助。
更新:感谢 deniss 指出原方案中的缺陷。
static constexpr uint8_t END = 0xff;
template<uint8_t...x>
const uint8_t* make()
{
static const uint8_t _[] = { x..., END };
return _;
}
const uint8_t* const array[] = {
make<>(),
make<1, 2, 3, 4, 5>(),
make<8, 9>(),
make<10, 11, 12>()
};
嗯,clang说的对,就这样吧:
namespace elements
{
const uint8_t row1[] = {END};
const uint8_t row2[] = {1, 2, 3, 4, 5, END};
...
}
const uint8_t *const minutes[] = {
elements::row1,
elements::row2,
...
};
你可以想到更多的 C++ 解决方案,比如使用 std::tuple
:
#include <tuple>
constexpr auto minutes = std::make_tuple(
std::make_tuple(),
std::make_tuple(1,2,3,4,5),
std::make_tuple(8,9,10));
#include <iostream>
#include <type_traits>
int main() {
std::cout << std::tuple_size<decltype(minutes)>::value << std::endl;
std::cout << std::tuple_size<std::remove_reference_t<decltype(std::get<1>(minutes))>>::value << std::endl;
}
我有一个不同长度的(指向)数组的数组,我了解到我可以使用复合文字来定义它:
const uint8_t *const minutes[] = {
(const uint8_t[]) {END},
(const uint8_t[]) {1, 2, 3, 4, 5 END},
(const uint8_t[]) {8, 9, END},
(const uint8_t[]) {10, 11, 12, END},
...
};
gcc 很好地接受了这个,但是 clang 说:pointer is initialized by a temporary array, which will be destroyed at the end of the full-expression
。这是什么意思?代码 似乎 可以工作,但是话又说回来,许多东西 似乎 在指向不再分配的内存时可以工作。这是我需要担心的事情吗? (最终我真的只需要它与 gcc 一起工作。)
更新:发生了一些可疑的事情。它说 here 那:
Compound literals yield lvalues. This means that you can take the address of a compound literal, which is the address of the unnamed object declared by the compound literal. As long as the compound literal does not have a const-qualified type, you can use the pointer to modify it.
`struct POINT *p; p = &(struct POINT) {1, 1};
这个示例代码似乎正在做我想做的事情:一个指向由复合文字定义的东西的指针。那么clang报错信息合法吗?当使用 clang 或 gcc 编译时,这最终会指向未分配的内存吗?
更新 2:
发现一些 documentation:“在 C 中,复合文字指定一个具有静态或自动存储持续时间的未命名对象。在 C++ 中,复合文字指定一个临时对象,它只存在到其结束时full-expression." 所以 clang 发出警告似乎是正确的,gcc 可能也应该这样做,但即使 -Wall -Wextra
.
我无法猜出为什么从 C++ 中删除了一个有用的 C 特性,并且没有提供完成相同事情的优雅替代方法。
嗯,这意味着,这个表达式
(const uint8_t[]) {1, 2, 3, 4, 5 END},
创建一个临时对象——临时的,因为它没有任何名称可以持续到它所属的表达式之外——它在完整表达式的末尾被销毁,这意味着:
};
定义"the full expression",此时所有临时对象都将被销毁,指针数组minutes
保存指向destroyed 个对象,这就是编译器发出警告的原因。
希望对您有所帮助。
更新:感谢 deniss 指出原方案中的缺陷。
static constexpr uint8_t END = 0xff;
template<uint8_t...x>
const uint8_t* make()
{
static const uint8_t _[] = { x..., END };
return _;
}
const uint8_t* const array[] = {
make<>(),
make<1, 2, 3, 4, 5>(),
make<8, 9>(),
make<10, 11, 12>()
};
嗯,clang说的对,就这样吧:
namespace elements
{
const uint8_t row1[] = {END};
const uint8_t row2[] = {1, 2, 3, 4, 5, END};
...
}
const uint8_t *const minutes[] = {
elements::row1,
elements::row2,
...
};
你可以想到更多的 C++ 解决方案,比如使用 std::tuple
:
#include <tuple>
constexpr auto minutes = std::make_tuple(
std::make_tuple(),
std::make_tuple(1,2,3,4,5),
std::make_tuple(8,9,10));
#include <iostream>
#include <type_traits>
int main() {
std::cout << std::tuple_size<decltype(minutes)>::value << std::endl;
std::cout << std::tuple_size<std::remove_reference_t<decltype(std::get<1>(minutes))>>::value << std::endl;
}