如果一个函数 returns 一个引用数组,它是指针的衰减吗?
If a function returns an array by reference is it a decay to pointer?
在下面的示例代码中,ComputeSomething() return是对数组的引用。
我被要求使用 C++ 核心指南(MSVC 工具链上的 NuGet 包)作为额外的静态分析工具。
在 ComputeSomething()
的 return 行,静态分析工具警告说有一个指针衰减数组。我假设意图是更改它以使衰减明确(类似于 &(computed_values[0])
),但这将破坏 return 通过引用数组的意义。
对于return引用数组的这个特定用例,这是一个有效的警告还是噪音?
(假定 C++98 约束)
float (&ComputeSomething( const seed_t(&seed_data)[num_of_elems_seeded] ))[num_of_elems_calculated]{
static float computed_values[num_of_elems_calculated];
// do something...
return computed_values;
}
是的。
在ComputeSomething
函数中,computed_values
是一个数组。和往常一样,它可以自然地衰减到指向其第一个元素的指针。
所以ComputeSomething
函数可以简化为
const float* ComputeSomething(...){ ... }
到 "preserve" 大小信息,然后才能像现在一样使用函数,调用者需要知道大小 anyway。它不是自动转移的东西。所以使用指针不会有太大变化,除非你绝对想在数组上使用 sizeof
,但是可以通过使用你必须在变量声明中提供的大小来回避。使用指针肯定会简化语法,因此也会使代码更具可读性和可维护性。
您也可以使用 std::vector
,它内置了尺寸信息。或者强制升级到可以使用 C++11 的更现代的编译器,然后使用 std::array
如果你想要 compile-time fixed-size 数组。
我不确定 std::pair
是否存在于 C++98 中,或者它是否出现在 C++03 中,但是作为某种 middle-ground 你可以使用 std::pair<float*, size_t>
到 return 指针 和 大小。
最后,您始终可以使用 typedef
为您的数组类型创建别名。那么就不用到处显式指定size了,语法会简化不少:
typedef computed_values_type[num_of_elems_calculated];
....
computed_values_type& ComputeSomething(...) { ... }
...
computed_values_type& computed_values = ComputeSomething(...);
从编译的二进制文件的角度来看,对数组的引用只是指向第一个元素的旧指针。它与原始指针的区别仅在于类型本身,后者包含有关数组大小的信息。例如,它可以用于 compile-time 大小检查、编译器警告或 range-based for
循环
对于以下方法:
float (&ComputeSomething())[num_of_elems_calculated]{
static float computed_values[num_of_elems_calculated];
// do something...
return computed_values;
}
与静态分析工具中可能发生的情况不同,指针衰减的潜在数组不是在方法内部确定的,而是在调用者调用函数时确定的:
// expected to decay
float *some_computation = ComputeSomething();
// not expected to decay to pointer
float (&safer_computation)[num_of_elems_calculated] = ComputeSomething();
感谢@Galik 对此的贡献。
在下面的示例代码中,ComputeSomething() return是对数组的引用。
我被要求使用 C++ 核心指南(MSVC 工具链上的 NuGet 包)作为额外的静态分析工具。
在 ComputeSomething()
的 return 行,静态分析工具警告说有一个指针衰减数组。我假设意图是更改它以使衰减明确(类似于 &(computed_values[0])
),但这将破坏 return 通过引用数组的意义。
对于return引用数组的这个特定用例,这是一个有效的警告还是噪音?
(假定 C++98 约束)
float (&ComputeSomething( const seed_t(&seed_data)[num_of_elems_seeded] ))[num_of_elems_calculated]{
static float computed_values[num_of_elems_calculated];
// do something...
return computed_values;
}
是的。
在ComputeSomething
函数中,computed_values
是一个数组。和往常一样,它可以自然地衰减到指向其第一个元素的指针。
所以ComputeSomething
函数可以简化为
const float* ComputeSomething(...){ ... }
到 "preserve" 大小信息,然后才能像现在一样使用函数,调用者需要知道大小 anyway。它不是自动转移的东西。所以使用指针不会有太大变化,除非你绝对想在数组上使用 sizeof
,但是可以通过使用你必须在变量声明中提供的大小来回避。使用指针肯定会简化语法,因此也会使代码更具可读性和可维护性。
您也可以使用 std::vector
,它内置了尺寸信息。或者强制升级到可以使用 C++11 的更现代的编译器,然后使用 std::array
如果你想要 compile-time fixed-size 数组。
我不确定 std::pair
是否存在于 C++98 中,或者它是否出现在 C++03 中,但是作为某种 middle-ground 你可以使用 std::pair<float*, size_t>
到 return 指针 和 大小。
最后,您始终可以使用 typedef
为您的数组类型创建别名。那么就不用到处显式指定size了,语法会简化不少:
typedef computed_values_type[num_of_elems_calculated];
....
computed_values_type& ComputeSomething(...) { ... }
...
computed_values_type& computed_values = ComputeSomething(...);
从编译的二进制文件的角度来看,对数组的引用只是指向第一个元素的旧指针。它与原始指针的区别仅在于类型本身,后者包含有关数组大小的信息。例如,它可以用于 compile-time 大小检查、编译器警告或 range-based for
循环
对于以下方法:
float (&ComputeSomething())[num_of_elems_calculated]{
static float computed_values[num_of_elems_calculated];
// do something...
return computed_values;
}
与静态分析工具中可能发生的情况不同,指针衰减的潜在数组不是在方法内部确定的,而是在调用者调用函数时确定的:
// expected to decay
float *some_computation = ComputeSomething();
// not expected to decay to pointer
float (&safer_computation)[num_of_elems_calculated] = ComputeSomething();
感谢@Galik 对此的贡献。