C ++数据类型及其对可执行文件大小的影响

时间:2013-08-12 17:05:15

标签: c++ types casting arduino microcontroller

我基本上是C ++的新手,除了试图学习10年前的语言并放弃,因为我没有真正有一个项目来激励我......不管怎样,我只是在说我对于C ++来说,让你们/ gals知道我目前的知识水平几乎是一个n00b。也就是说,我对Python和PHP相当熟练。由于这两种语言都是松散类型的,所以我并不熟悉C ++中的影响类型转换对可执行文件的大小(如果有的话)。

我正在编写一个Arduino程序,从一些超声波距离传感器中获取一些数据并将数据应用于伺服控制算法。没有问题,但我现在正在尝试优化我的代码,因为我接近Arduino Micro的28,672字节的限制。我的第一个想法是尽可能地将我的数据类型更改为short int和char's,期望它没有效果,或者稍微减少我的可执行文件大小。我发现,在这些更改之后,可执行文件的大小实际上增加了几百个字节。

具有更多C ++知识的人能否帮助我理解其原因,以及为什么我应该或者不应该为我的变量选择尽可能小的数据类型?显然结果决定了我应该在这里做什么,但我真的很想理解事情背后的'为什么',经过一些谷歌搜索,我仍然不确定。

另外,如果问的话不是太多;有没有人有一些提示,或链接到优化有限内存微控制器(如Arduino)的C ++代码的一些信息?

4 个答案:

答案 0 :(得分:2)

答案 1 :(得分:2)

你问很多事情,但这可以用一个例子来回答:

  

我发现在执行这些更改之后,可执行文件的大小实际上增加了几百个字节。

     

...帮助我理解这个原因...

通常,您无法预测较小的数据类型是好还是坏,下面的一小段代码将证明这一点。

要查看发生了什么,您必须查看编译器生成的汇编代码。 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);

为了演示,明亮的变量在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

注意SUBI 255的奇怪而不是添加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>

无需了解类型转换的程序集即可查看效果。较小的数据类型产生了更多代码。


那是什么意思?较小的数据类型既减小了代码大小又增加了代码大小。除非你能在头脑中编译代码,否则你无法通过检查来解决这个问题,你必须尝试一下。

答案 2 :(得分:1)

通常,您必须区分代码大小和数据大小。优化数据大小可能会增加代码大小(并减慢速度),因为编译器需要在代码中添加更多指令,以便在各种可能的数据大小之间进行转换。

因此,根据经验:使用默认数据大小(例如&#34; int&#34;)表示数据中最多出现几次的任何值。另一方面,如果你有大型阵列,设置最佳数据大小(例如&#34;短&#34;,如果值保证在-32768 ... 32767范围内)可以大大减少内存占用你的应用程序在运行时。

在您的情况下,如果您没有太多数据,请更多地关注优化代码大小:减少使用的库数量,并避免包装等.pp。

答案 3 :(得分:0)

最大的内存消费者之一是浮点数(在RAM和FLASH中)。 Ram因为类型大于整数和Flash,因为Arduino没有浮点单元。因此,所有浮点运算都将导致更大的可执行文件。

还要注意使用库可能会链接许多不需要的东西,这些东西会消耗大量的内存。

话虽如此:如果没有关于代码的更多细节,很难确定为什么你有这么大的内存占用。