输入强制文字是否有意义?

时间:2018-02-08 15:50:36

标签: c++ c embedded

所以我正在研究内存受限的嵌入式系统,并希望尽可能多地保存字节。如果我在我的代码中有如下声明:

b = a << 1;

b += 1;

其中abuint8_t。将文字类型转换为相同类型或者由编译器完成是否有益:

b = a << (uint8_t) 1;

b += (uint8_t) 1;

5 个答案:

答案 0 :(得分:4)

  

将文字转换为相同类型或者由编译器完成是否有益:

编译器在如何将常量值存储在它创建的二进制文件中时受到编译器的支配。没有特别的理由认为像你提议的演员会改变所使用的表示,因为他们名义上表达运行时转换(在这种情况下来自int)。而且,在没有详细说明的情况下,C指定算术运算的操作数将被提升为至少与int一样宽的类型,以便计算算术运算的结果。编译器可能会合理地预先计算出这样的转换,从而有效地使你的演员阵容无效。

但是,如果它们碰巧阻止编译器识别机会以避免存储常量值,那么你的演员实际上可能会更糟。例如,推测性地,如果您的目标CPU有一个特定的指令来将寄存器的值递增1,那么它可能会用它来实现

b += 1;

...但未能认识到它可以用

做同样的事情
b += (uint8_t) 1;

...由于(uint8_t) 1是非主要表达。

谨慎使用强制转换,并仅在必要时描述程序的语义。依靠你的编译器来做好工作,如果它没有找到更好的编译器。通常可以依赖嵌入式环境的编译器来理解最小化代码大小的重要性,甚至通用平台的编译器通常也可以选择优化最小代码大小。

答案 1 :(得分:3)

在这种情况下,它并不重要,因为任何类型的排名低于int的整数值在表达式中使用时会自动提升为int

答案 2 :(得分:2)

在提供的示例中,好处是维护签署协议而不是类型协议。在这种情况下,类型协议在任何情况下都会被类型促销规则所取消;在转换为uint8_t之后,字面操作数将被提升为unsigned int。如果没有强制转换,它将被提升为int并且在某些表达式中可能会产生意外或未定义的结果(尽管不在这些示例中 - 例如右移对于负的有符号值是不明确的)。

维护签名协议的更好方法是使用无符号文字后缀u

b = a << 1u ;
b += 1u ;

答案 3 :(得分:0)

您的示例中的文字不可能作为单独的数字常量存储,但会集成在机器代码指令中。在您的情况下,结果将是一些指令,如&#34;逻辑左移位寄存器x,1&#34;。无论更高级别的C语言是什么,数字都将与特定指令所需的数量一样大。

这些是您应该留给编译器的优化。这适用于整数常量(&#34;文字&#34;),#define个,通常也适用于枚举。

但是,如果您将常量用作const变量,则可以手动选择所需的最小类型以保存闪存。如果您声明类似static const int x = 123;之类的内容,那么您可能会阻止编译器使用任何比int更小的类型。

stdint.h中的类型uint_leastn_t旨在用于内存优化。如果你写static const uint_least8_t = 123;,那么编译器将选择最小的可用类型,至少8个字节。 (在大多数系统中,最有可能产生与uint8_t完全相同的结果。)

答案 4 :(得分:0)

我在 Compiler Explorer 上做了一些实验。

#include <cstdint>

uint64_t A()
{
    return uint64_t{1} << 4;
}

uint64_t B()
{
    return static_cast<uint64_t>(1) << 4;
}

uint64_t C()
{
    return uint64_t(1) << 4;
}

// uint64_t D()
// {
//     return uint8_t{256}; <--- Compile time error
// }

uint64_t E()
{
    return static_cast<uint8_t>(256);
}

uint64_t F()
{
    return uint8_t(256);
}

带有 -std=c++20 -O1 的 Clang 11.0.1 生成了以下汇编程序:

A():                                  # @A()
        mov     eax, 16
        ret
B():                                  # @B()
        mov     eax, 16
        ret
C():                                  # @C()
        mov     eax, 16
        ret
E():                                  # @E()
        xor     eax, eax
        ret
F():                                  # @F()
        xor     eax, eax
        ret

以及 D()

的以下错误
<source>:20:20: error: constant expression evaluates to 256 which cannot be narrowed to type 'uint8_t' (aka 'unsigned char') [-Wc++11-narrowing]
    return uint8_t{256};
                   ^~~
<source>:20:20: note: insert an explicit cast to silence this issue
    return uint8_t{256};
                   ^~~
                   static_cast<uint8_t>( )
<source>:20:20: warning: implicit conversion from 'int' to 'uint8_t' (aka 'unsigned char') changes value from 256 to 0 [-Wconstant-conversion]
    return uint8_t{256};
                  ~^~~
1 warning and 1 error generated.
Compiler returned: 1

带有 -std=c++20 -O1 的 GCC 10.2 生成了以下汇编器:

A():
        mov     eax, 16
        ret
B():
        mov     eax, 16
        ret
C():
        mov     eax, 16
        ret
E():
        mov     eax, 0
        ret
F():
        mov     eax, 0
        ret

以及 D()

的以下错误
<source>: In function 'uint64_t D()':
<source>:20:23: error: narrowing conversion of '256' from 'int' to 'uint8_t' {aka 'unsigned char'} [-Wnarrowing]
   20 |     return uint8_t{256};
      |                       ^
Compiler returned: 1

结论

  • 担心编译器在测试之前无法识别主表达式是过早的优化
  • 使用 {} 优于 ()static_cast,因为它确保我们的数字文字安全地在我们的目标类型范围内。
  • 如果我们想做一些 {} 不允许的事情,我们将被迫向编译器和其他开发者说明我们的意图(请参阅 clangs 编译错误 note: insert an explicit cast to silence this issue