代码之家  ›  专栏  ›  技术社区  ›  frank_010

键入强制转换文本有意义吗?

  •  4
  • frank_010  · 技术社区  · 7 年前

    所以我正在研究内存受限的嵌入式系统,希望尽可能节省字节。如果,在我的代码中,如果我有如下语句:

    b = a << 1;
    

    b += 1;
    

    哪里 a b uint8_t . 将文本类型转换为相同的类型是有益的还是由编译器完成的:

    b = a << (uint8_t) 1;
    
    b += (uint8_t) 1;
    
    5 回复  |  直到 7 年前
        1
  •  4
  •   John Bollinger    7 年前

    将文本强制转换为相同类型是有益的还是由编译器完成的:

    编译器如何将常量值存储在它创建的二进制文件中,这取决于编译器。没有特别的理由认为,像你提议的演员阵容会改变他们名义上所表达的表达方式 运行时 转换(从 int ,在本例中)。此外,在不深入讨论的情况下,C指定算术运算的操作数将升级为至少与 内景 用于计算算术运算的结果。编译器可能会合理地预先计算这样的转换,从而有效地完全取消您的强制转换。

    然而,如果强制转换碰巧阻止编译器识别避免存储常量值的机会,那么您的强制转换实际上可能会更糟。例如,假设您的目标CPU有一条特定的指令将寄存器的值精确递增1,那么它可能会使用该指令来实现

    b += 1;
    

    ... 但是没有意识到它可以对

    b += (uint8_t) 1;
    

    ... 考虑到 (uint8_t) 1 作为非主表达式。

    谨慎使用类型转换,仅在必要时描述 语义学 你的计划。依靠您的编译器来做好工作,如果没有,请寻找更好的编译器。嵌入式环境的编译器通常可以用来理解最小化代码大小的重要性,甚至通用平台的编译器通常也可以选择请求优化最小代码大小。

        2
  •  3
  •   dbush    7 年前

    在这种情况下,这并不重要,因为任何类型的整数值的秩都低于 int 自动升级为 内景 在表达式中使用时。

        3
  •  3
  •   Clifford    7 年前

    在提供的示例中,好处是 签署协议 而不是 类型协议 . 在这种情况下,类型协议在任何情况下都会被类型提升规则击败;转换为后的文字操作数 uint8_t 将升级为 unsigned int . 如果没有演员阵容,它将被提升为 int 并且在某些表达式中可能会产生意外或未定义的结果(尽管在这些示例中不是这样-例如,右移对于负号值来说是不明确的)。

    可以说是更好的维护 签署协议 是使用无符号文字后缀 u :

    b = a << 1u ;
    b += 1u ;
    
        4
  •  0
  •   Lundin    7 年前

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

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

    但是,如果将常量用作 const 变量,您可以手动选择所需的最小类型以保存flash。如果您声明如下内容,例如 static const int x = 123; 那么您就有可能阻止编译器使用任何小于 int 碰巧是。

    类型 uint_leastn_t 来自stdint。h用于内存优化。如果你写信 static const uint_least8_t = 123; 然后编译器将选择可用的最小类型,至少8个字节。(最有可能产生与 uint8_t 在大多数系统上。)

        5
  •  0
  •   Troyseph    4 年前

    我做了一些实验 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);
    }
    

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

    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
    

    GCC 10.2带 -std=c++20-O1 生成了以下汇编程序:

    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 compile error note: insert an explicit cast to silence this issue )