C++数据类型及其对可执行文件大小的影响
C++ data-types, and their effects on executable size
我基本上是C++的新手,除了10多年前尝试学习这门语言并放弃之外,因为我没有真正的项目来激励我……无论如何,我只是想说,我对C++来说几乎是个新手,让你们知道我目前的知识水平。也就是说,我对Python和PHP相当精通。由于这两种语言都是松散类型的,所以我不太熟悉C++中的类型转换对可执行文件大小的影响(如果有的话)。
我正在编写一个Arduino程序,从几个超声波距离传感器中获取一些数据,并将这些数据应用于伺服控制算法。这没有问题,但我现在正在努力优化我的代码,因为我已经接近Arduino Micro 28672字节的限制。我的第一个想法是尽可能将我的数据类型更改为短int和char之类的类型,希望它要么没有效果,要么稍微减小我的可执行文件大小。我发现,在这些更改之后,可执行文件的大小实际上增加了几百个字节。
有没有比我更懂C++的人能帮助我理解原因,以及为什么我应该或不应该为我的变量选择尽可能小的数据类型?显然,结果决定了我在这里应该做什么,但我真的很想了解事情背后的"原因",在谷歌上搜索了一番之后,我仍然不确定。
此外,如果要求不过分;有没有人有一些技巧,或者链接到一些关于优化有限内存微控制器(如Arduino)的C++代码的信息?
你问了很多问题,但可以用一个例子来回答:
我发现,在这些更改之后,可执行文件的大小实际上增加了几百个字节。
帮我理解原因。。。
通常,您无法预测较小的数据类型是好是坏,下面的一小段代码将说明这一点。
要了解发生了什么,您必须查看编译器生成的汇编代码。AVR工具链有一个组件将生成这样的列表,通常是一个.LSS文件。我认为Arduino不支持这一点。下面的程序集清单是通过默认情况下驱动扩展清单的Eclipse提供的。
这里有一个LED闪烁程序的小部分,可以用来展示你的困惑。它有一个亮度值,设置为循环中的LED:
boolean fadein = true;
int bright = 0; // we will change this data type int <-> int8_t
void loop() {
// adjust brightness based on current direction
if(fadein) {
bright += 1;
}
else {
bright -= 1;
}
// apply current light level
analogWrite(13,bright);
为了演示,bright变量在1字节和2字节int之间进行了更改,我们比较了程序集列表:
比较增量线
以下是带有两种数据类型的增量行的列表:
// int bright - increment line - must load and store 2 bytes
// 18 bytes of code
bright += 1;
18a: 80 91 02 01 lds r24, 0x0102
18e: 90 91 03 01 lds r25, 0x0103
192: 01 96 adiw r24, 0x01 ; 1
194: 90 93 03 01 sts 0x0103, r25
198: 80 93 02 01 sts 0x0102, r24
第一列是代码空间地址,第二列是实际代码字节,最后一列是汇编人类可读形式。LDS是从内存加载的,ADIW是加法,STS是存储回内存的
以下是较小的数据类型,具有预期结果:
// int8_t bright - increment line - only load and store 1 byte
// 10 bytes of code
bright += 1;
18a: 80 91 02 01 lds r24, 0x0102
18e: 8f 5f subi r24, 0xFF ; 255
190: 80 93 02 01 sts 0x0102, r24
注意SUBI255而不是加1的怪异之处——这是编译器开发人员的技巧。
因此,较小的数据类型会产生您所期望的较小代码。你是对的!哦,等等,你已经告诉你哪里不正确了。。。
比较函数调用
但是函数调用呢?analogWrite()方法需要一个int,因此如果需要,编译器将被迫创建一个转换
// int - needs no type conversion, can directly load value
// from addresses 0x0102 and 0x0103 and call
// 16 bytes code
// apply current light level
analogWrite(13,bright);
1b0: 20 91 02 01 lds r18, 0x0102
1b4: 30 91 03 01 lds r19, 0x0103
1b8: 8d e0 ldi r24, 0x0D ; 13
1ba: b9 01 movw r22, r18
1bc: 0e 94 87 02 call 0x50e ; 0x50e <analogWrite>
LDI正在加载常量,MOVW正在移动变量以准备调用。
// int8_t - needs a type conversion before call
// 20 bytes code
// apply current light level
analogWrite(13,bright);
1a0: 80 91 02 01 lds r24, 0x0102
1a4: 28 2f mov r18, r24
1a6: 33 27 eor r19, r19
1a8: 27 fd sbrc r18, 7
1aa: 30 95 com r19
1ac: 8d e0 ldi r24, 0x0D ; 13
1ae: b9 01 movw r22, r18
1b0: 0e 94 76 02 call 0x4ec ; 0x4ec <analogWrite>
无需了解类型转换的程序集即可查看效果。较小的数据类型生成了更多的代码。
那么这意味着什么呢?较小的数据类型既减少了代码大小,又增加了代码大小。除非你能在脑子里编译代码,否则你无法通过检查来解决这个问题,你只能尝试一下。
首先,看看如何优化你的Arduino内存使用和优化Arduino的内存使用。此外,请注意节省RAM空间。
通常,您必须区分代码大小和数据大小。优化数据大小可能会增加代码大小(也会减慢速度),因为编译器需要在代码中放入更多指令,以便在各种可能的数据大小之间来回转换。
因此,经验法则是:对数据中最多出现几次的任何值使用默认数据大小(例如"int")。另一方面,如果你有大数组,设置最佳数据大小(例如"短",如果值保证在-32768…32767范围内)可以大大减少应用程序在运行时的内存占用。
在您没有太多数据的情况下,请更多地关注优化代码大小:减少使用的库的数量,避免使用包装器等。
最大的内存消耗之一是浮点数(RAM和FLASH中都有)。Ram是因为类型大于整数,Flash是因为Arduino没有浮点单元。因此,所有浮点运算都将产生更大的可执行文件。
还要注意,使用库可能会链接大量不需要的东西,这些东西会消耗大量内存。
话虽如此:如果没有关于代码的更多细节,很难确定为什么您有这么大的内存占用。
- 读取某些文件时出现分段错误,似乎与文件大小无关
- 构建挂起,即使是适度的文件大小
- 如何在Windows内核中获取文件大小
- 为什么 QFileSystemWatcher 会发出多个信号?而 QFileInfo 首次写入零文件大小
- 使用 C++ iOS::ate 获取错误的文件大小
- 相同的源代码,不同的可执行文件大小?
- 为什么未关闭的文件大小为 4 字节
- 读取二进制文件大小的短自上周以来停止工作
- 如何减小C++标准库libstdc++.so文件大小
- 文件大小和缓冲区过冲
- 如何从范围 v3 的istream_range中检索文件大小?
- 如何使用直接IO编写一个带有扩展文件大小的小文件
- 如何使用文件系统库获取"true"文件大小?(C++17)
- 如何限制文件大小,以便我的程序在变大之后创建一个新文件?并编辑新创建的文件的名称
- 如何使用C 17获取文件大小
- HttpQueryInfo 获取文件大小
- 二进制文件大小大于预期的 c++
- 如何计算预期的核心文件大小
- C++数据类型及其对可执行文件大小的影响
- fputc如何影响文件大小