我有一个没有MMU的微控制器,但我们使用的是C和C ++。
我们避免使用所有动态内存(即没有new SomeClass()
或malloc()
)和大多数标准库。
半问题0:
根据我的理解std::array
不使用任何动态内存,因此它的用法应该没问题(它只在堆栈上)。查看std::array
源代码,它看起来很好,因为它创建了一个c风格的数组,然后在该数组周围包装了功能。
我们使用的芯片有1MB闪存用于存储代码。
问题1:
我担心在std::array
中使用模板会导致二进制文件变大,这可能会导致二进制文件超过1MB的代码内存限制。
我认为如果你创建一个std::array< int, 5 >
的实例,那么对std::array
上的函数的所有调用都会占用一定数量的代码内存,比如X字节的内存。
如果你创建了std::array< SomeObject, 5 >
的另一个实例,那么调用函数std::array
,这些函数现在会在二进制文件中重复,从而占用更多的代码内存吗? X字节的内存+ Y字节的内存。
如果是这样,您认为在有限的代码内存容量下生成的代码量是一个问题吗?
问题2:
在上面的示例中,如果您创建了第二个std::array< int, 10 >
实例,对函数的调用是否也会复制生成的代码中的函数调用?即使两个实例属于同一类型,int
?
答案 0 :(得分:6)
std::array
被认为是零成本抽象,这意味着编译器应该可以相当优化。
对于任何零成本抽象,它可能会导致较小的编译时间,并且如果所需的优化成本确实不支持零成本,那么它可能会产生小尺寸或运行时间的问题。
但请注意,编译器可以在结构的末尾自由添加填充。由于std::array
是一个结构,你应该检查你的平台如何处理std::array
,但我非常怀疑你的情况。
拿这个数组和std::array
案例:
#include <numeric>
#include <iterator>
template<std::size_t n>
int stuff(const int(&arr)[n]) {
return std::accumulate(std::begin(arr), std::end(arr), 0);
}
int main() {
int arr[] = {1, 2, 3, 4, 5, 6};
return stuff(arr);
}
#include <numeric>
#include <iterator>
#include <array>
template<std::size_t n>
int stuff(const std::array<int, n>& arr) {
return std::accumulate(std::begin(arr), std::end(arr), 0);
}
int main() {
std::array arr = {1, 2, 3, 4, 5, 6};
return stuff(arr);
}
Clang很好地支持这个案子。具有std::array
或原始数组的所有情况都是以相同的方式处理:
-O2
/ -O3
数组和std::array
与clang:
main: # @main
mov eax, 21
ret
但是,对于std::array
和原始数组的情况,GCC似乎在优化它时遇到了问题:
-O3
使用GCC进行数组和std::array
:
main:
movdqa xmm0, XMMWORD PTR .LC0[rip]
movaps XMMWORD PTR [rsp-40], xmm0
mov edx, DWORD PTR [rsp-32]
mov eax, DWORD PTR [rsp-28]
lea eax, [rdx+14+rax]
ret
.LC0:
.long 1
.long 2
.long 3
.long 4
然后,在原始数组的情况下似乎使用-O2
进行了更好的优化,并使用std::array
失败:
-O2
GCC std::array
:
main:
movabs rax, 8589934593
lea rdx, [rsp-40]
mov ecx, 1
mov QWORD PTR [rsp-40], rax
movabs rax, 17179869187
mov QWORD PTR [rsp-32], rax
movabs rax, 25769803781
lea rsi, [rdx+24]
mov QWORD PTR [rsp-24], rax
xor eax, eax
jmp .L3
.L5:
mov ecx, DWORD PTR [rdx]
.L3:
add rdx, 4
add eax, ecx
cmp rdx, rsi
jne .L5
rep ret
-O2
GCC原始数组:
main:
mov eax, 21
ret
在最新的版本中,似乎GCC错误可以优化-O3
,但-O2
成功。
在陈述所有这些情况后,您可以看到一个共同的模式:在二进制文件中没有输出有关std::array
的信息。没有构造函数,没有operator[]
,甚至没有迭代器,也没有算法。一切都是内联的。编译器擅长内联简单的功能。 std::array
成员函数通常非常简单。
如果你创建另一个std :: array&lt;的实例SomeObject,5&gt;然后调用函数到那个std :: array,这些函数现在都会在二进制文件中被复制,从而占用更多的闪存吗? X字节的内存+ Y字节的内存。
嗯,您更改了数组包含的数据类型。如果你手动添加所有函数的重载来处理这个额外的情况,那么是的,所有这些新函数可能会占用一些空间。如果您的功能很小,那么它们很有可能被内联并占用更少的空间。正如您在上面的示例中所看到的,内联和常量折叠可能会大大降低您的二进制大小。
在上面的例子中,如果你创建了第二个std :: array实例,对函数的调用是否也会复制闪存中的函数调用?尽管两个实例的类型相同,但是int?
再取决于它。如果您有许多以数组大小模板化的函数,std::array
和原始数组都可以“创建”不同的函数。但同样,如果他们被内联,那就没有重复可以担心了。
两者都是原始数组和std::array
,您可以将指针传递给数组的开头并传递大小。如果您发现这更适合您的情况,那么使用它,但仍然是原始数组和std::array
可以做到这一点。对于原始数组,它会隐式衰减到指针,而使用std::array
,您必须使用arr.data()
来获取指针。