C++数据类型及其对可执行大小的影响 我基本上是新的C++,除了10年前尝试学习语言和放弃,因为我没有真正的项目来激励我…不管怎样,我只是说我是一个N100B到C++,让你们/ GALS知道我目前的知识水平。也就是说,我相当精通Python和PHP。因为这两种语言都是松散类型的,所以我不太熟悉C++中的冲击类型的铸造,如果有的话,可以执行。
我正在编写一个Arduino程序,从两个超声波距离传感器获取一些数据,并将这些数据应用到伺服控制算法中。这没有问题,但我现在正在尝试优化我的代码,因为我正接近Arduino Micro的28672字节限制。我的第一个想法是尽可能地将我的数据类型更改为short int和char之类的类型,期望它要么没有效果,要么稍微减小可执行文件的大小。我发现,在这些更改之后,可执行文件的大小实际上增加了几百字节 能比我更了解C++知识的人能帮助我理解这个原因吗?为什么我应该或者不应该,甚至费心尝试为我的变量选择最小的可能的数据类型?很明显,结果决定了我应该在这里做什么,但我真的很想了解事情背后的“原因”,在谷歌搜索了一些之后,我仍然不确定C++数据类型及其对可执行大小的影响 我基本上是新的C++,除了10年前尝试学习语言和放弃,因为我没有真正的项目来激励我…不管怎样,我只是说我是一个N100B到C++,让你们/ GALS知道我目前的知识水平。也就是说,我相当精通Python和PHP。因为这两种语言都是松散类型的,所以我不太熟悉C++中的冲击类型的铸造,如果有的话,可以执行。,c++,types,casting,arduino,microcontroller,C++,Types,Casting,Arduino,Microcontroller,我正在编写一个Arduino程序,从两个超声波距离传感器获取一些数据,并将这些数据应用到伺服控制算法中。这没有问题,但我现在正在尝试优化我的代码,因为我正接近Arduino Micro的28672字节限制。我的第一个想法是尽可能地将我的数据类型更改为short int和char之类的类型,期望它要么没有效果,要么稍微减小可执行文件的大小。我发现,在这些更改之后,可执行文件的大小实际上增加了几百字节 能比我更了解C++知识的人能帮助我理解这个原因吗?为什么我应该或者不应该,甚至费心尝试为我的变量选
而且,如果要求不太多的话;有没有人有一些技巧,或者是一些关于优化有限内存微控制器(如ARDUINO?< P>)的C++代码的链接,一般来说,你必须区分代码大小和数据大小。优化数据大小可能会增加代码大小,也会降低速度,因为编译器需要在代码中放入更多指令,以便在各种可能的数据大小之间来回转换 因此,根据经验法则:对任何值使用默认数据大小,例如int,该值最多出现在数据中几次。另一方面,如果您有大数组,请设置最佳数据大小,例如short,前提是该值保证在-32768.范围内。。32767可以在运行时大大减少应用程序的内存占用
在您没有太多数据的情况下,请更加关注优化代码大小:减少使用的库的数量,避免包装等。pp.通常,您必须区分代码大小和数据大小。优化数据大小可能会增加代码大小,也会降低速度,因为编译器需要在代码中放入更多指令,以便在各种可能的数据大小之间来回转换 因此,根据经验法则:对任何值使用默认数据大小,例如int,该值最多出现在数据中几次。另一方面,如果您有大数组,请设置最佳数据大小,例如short,前提是该值保证在-32768.范围内。。32767可以在运行时大大减少应用程序的内存占用
在您没有太多数据的情况下,请将更多精力放在优化代码大小上:减少使用的库的数量,避免使用包装器等。pp.首先,查看和。另外,请看。首先,请看和。另外,看看。你问了很多问题,但可以用一个例子来回答: 我发现,在这些更改之后,可执行文件的大小实际上增加了几百字节 。。。请帮助我了解原因 通常,您无法预测较小的数据类型是好是坏,下面的一小段代码将对此进行演示 要查看发生了什么,您必须查看编译器生成的汇编代码。AVR工具链有一个组件可以生成这样的列表,通常是一个.LSS文件。我认为Arduino不支持这一点。下面的程序集清单是通过Eclipse生成的,默认情况下,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);
第一列是代码空间地址,第二列是实际代码字节,最后一列是程序集的可读形式。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>
没有
需要了解类型转换的程序集才能看到效果。较小的数据类型产生了更多的代码
那么这意味着什么呢?较小的数据类型既减少了代码大小,又增加了代码大小。除非你能在头脑中编译代码,否则你无法通过检查来理解这一点,你只能尝试一下。你问了很多问题,但可以用一个例子来回答: 我发现,在这些更改之后,可执行文件的大小实际上增加了几百字节 。。。请帮助我了解原因 通常,您无法预测较小的数据类型是好是坏,下面的一小段代码将对此进行演示 要查看发生了什么,您必须查看编译器生成的汇编代码。AVR工具链有一个组件可以生成这样的列表,通常是一个.LSS文件。我认为Arduino不支持这一点。下面的程序集清单是通过Eclipse生成的,默认情况下,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);
第一列是代码空间地址,第二列是实际代码字节,最后一列是程序集的可读形式。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>
无需了解类型转换的程序集即可看到效果。较小的数据类型产生了更多的代码
那么这意味着什么呢?较小的数据类型既减少了代码大小,又增加了代码大小。除非你能在头脑中编译代码,否则你无法通过检查来理解这一点,你只能尝试一下。最大的内存消耗之一是RAM和闪存中的浮点数。Ram,因为类型大于整数;闪存,因为Arduino没有浮点单元。因此,所有浮点操作都将产生更大的可执行文件 还要注意,使用库可能会链接许多不需要的东西,这些东西会占用大量内存
话虽如此:如果没有更多关于代码的细节,很难确定为什么会有如此大的内存占用。最大的内存消耗之一是RAM和闪存中的浮点数。Ram,因为类型大于整数;闪存,因为Arduino没有浮点单元。因此,所有浮点操作都将产生更大的可执行文件 还要注意,使用库可能会链接许多不需要的东西,这些东西会占用大量内存
话虽如此:如果没有关于代码的更多细节,很难确定为什么会有如此大的内存占用。您在编译时是否尝试过优化大小?GCC - OS…我是C++的人,但是对于这样一个大小受限的环境,我只会使用C.@ DavidBrown:我是新的ARDUNO、C++和微控制器。因此,我还不确定如何用另一种语言编写代码,然后手动将可执行文件上传到Arduino,他们的IDE只需点击几下即可编译和上传。虽然我相信我能把这些都弄清楚,但我想C语言缺少Arduino库会让这比我想象的更具挑战性,哈哈。谢谢你的评论。@millsj:这是个好主意!我肯定会更仔细地看的!非常感谢。您在编译时是否尝试过优化大小?GCC - OS…我是C++的人,但是对于这样一个大小受限的环境,我只会使用C.@ DavidBrown:我是新的ARDUNO、C++和微控制器。因此,我还不确定如何用另一种语言编写代码,然后手动将可执行文件上传到Arduino,他们的IDE只需点击几下即可编译和上传。虽然我相信我能把这些都弄清楚,但我想C语言缺少Arduino库会让这比我想象的更具挑战性,哈哈。谢谢你的评论。@millsj:这是个好主意!我肯定会更仔细地看的!非常感谢。链接不是答案。这里需要一些实际的信息。@cHao:通常我会同意,但我确实在我的帖子底部要求提供这样的链接。因此,我认为否决票有点不公平。我非常感谢这里提供的链接!谢谢。链接是n
ot回答。这里需要一些实际的信息。@cHao:通常我会同意,但我确实在我的帖子底部要求提供这样的链接。因此,我认为否决票有点不公平。我非常感谢这里提供的链接!谢谢,非常感谢!你的评论很有见地,很有用。我很感谢你花时间帮我多了解一些事情。这可能是一个愚蠢的问题,但当我使用图书馆时;库中我没有使用的东西会影响可执行文件的大小吗?也就是说,将库剥离到项目帮助所需的程度是否会有帮助?或者,从库中获取代码片段并直接使用它们,而不是链接库,是否会减少可执行文件的大小?或者这一切都是毫无意义和反作用的,这是我的直觉?图书馆的内存占用很大程度上取决于它们的类型:像STL容器的C++模板库往往会增加代码大小,因为它们不能被预编译。另一方面,如果不使用模板,C库或C++库不会增加可执行文件的大小,如果使用动态链接的话。我不确定Arduino是否会进行动态链接,但我认为不会。话虽如此,使用经典数组代替std::vector或使用自写链接LSIT代替std::list可能会显著减少可执行文件的大小。编译器为源文件中的所有内容生成代码,并生成目标文件。链接器获取所有的对象文件并进行组合以生成可执行文件。库中任何未在代码中引用的函数都不会包含在二进制文件中。因此,剥离库对最终二进制大小没有好处。你可以通过在你的程序中粘贴一大块从未调用过的代码来轻松测试这一点-你会看到二进制大小一个字节都没有改变。非常感谢!你的评论很有见地,很有用。我很感谢你花时间帮我多了解一些事情。这可能是一个愚蠢的问题,但当我使用图书馆时;库中我没有使用的东西会影响可执行文件的大小吗?也就是说,将库剥离到项目帮助所需的程度是否会有帮助?或者,从库中获取代码片段并直接使用它们,而不是链接库,是否会减少可执行文件的大小?或者这一切都是毫无意义和反作用的,这是我的直觉?图书馆的内存占用很大程度上取决于它们的类型:像STL容器的C++模板库往往会增加代码大小,因为它们不能被预编译。另一方面,如果不使用模板,C库或C++库不会增加可执行文件的大小,如果使用动态链接的话。我不确定Arduino是否会进行动态链接,但我认为不会。话虽如此,使用经典数组代替std::vector或使用自写链接LSIT代替std::list可能会显著减少可执行文件的大小。编译器为源文件中的所有内容生成代码,并生成目标文件。链接器获取所有的对象文件并进行组合以生成可执行文件。库中任何未在代码中引用的函数都不会包含在二进制文件中。因此,剥离库对最终二进制大小没有好处。你可以通过在你的程序中粘贴一大块从未调用过的代码来轻松测试这一点——你会看到二进制大小一个字节也不会改变。好吧,我知道我不应该在这里回答只是说“谢谢”,但神圣的汇编之神@jdr5ca,这是一个非常有洞察力的解释,它确实帮助我探索了整个问题!非常感谢你!好吧,我知道我不应该只是回答“谢谢”,但是神圣的集会之神,@jdr5ca,这是一个非常有洞察力的解释,真的帮助我解决了整个问题!非常感谢你!我知道为什么我有一个大内存占用-大量的代码!哈哈。这对Arduino Micro来说太多了。在我发布这篇文章之前,它并没有得到很好的优化。我一直在努力以更直接的方式优化代码,我只是不理解类型设置/二进制大小的问题,但是这里的一些伟大的帖子澄清了这一点!关于Arduino上的FP,这是一件值得记住的事情,我确实有一些第三方使用FP的代码片段,我已经将它们转换为整数。你说得对,这对我很有帮助!我知道为什么我有一个大内存占用-大量的代码!哈哈。这对Arduino Micro来说太多了。在我发布这篇文章之前,它并没有得到很好的优化。我一直在努力以更直接的方式优化代码,我只是不理解类型设置/二进制大小的问题,但是这里的一些伟大的帖子澄清了这一点!关于Arduino上的FP,这是一件值得记住的事情,我确实有一些第三方使用FP的代码片段,我已经将它们转换为整数。你说得对,这对我很有帮助!