【问题标题】:Can I specify an integer constant by its bytes?我可以通过字节指定一个整数常量吗?
【发布时间】:2014-01-15 13:21:18
【问题描述】:

我在我的一个项目中使用 C++11,想知道如何最好地表示 ELF 幻数。我不喜欢十六进制文字,所以我在寻找比以下更好的东西:

const uint32 ELF_MAGIC_NUMBER = 0x7F454c46; // 0x7F, E, L, F

所以,我试着写:

const uint32 ELF_MAGIC_NUMBER = { 0x7F, 'E', 'L', 'F' };

但是编译器抱怨初始化器列表中的项目太多,这可以理解,虽然很烦人。

有什么方法可以根据字节来编写整数文字吗?我觉得第一个选项虽然有效,但第二个选项不那么可读。

【问题讨论】:

  • 第一个不必是可读的。这就是它的评论和名称的用途。我可能只是这么说,因为我认为你不会比十六进制文字更好。
  • 是的,在转换中编码可能会花费与自己进行转换并包含评论一样多的努力。
  • const union { uint8_t c[4]; uint32 u; } ELF_MAGIC_NUMBER = { 0x7F, 'E', 'L', 'F' };,但这过于复杂了。
  • 或者const uint32 ELF_MAGIC_NUMBER = 0x7f << 24 | 'E' << 16 | 'L' << 8 | 'F';,但同样值得付出努力吗?
  • @chris 对,但我必须查找 'E' => 45、'L' => 4c 和 'F' => 46 的映射。最好不要必须像那样打破我的流程,并且不要记住 ASCII 表。诚然,我只需要这样做一次,但许多幻数都是这样工作的。

标签: c++ c++11 elf


【解决方案1】:

怎么样:

const uint32 i = 0x1000000 * 0x7F
               + 0x10000 * 'E'
               + 0x100 * 'L'
               + 0x1 * 'F';

可能更具可读性(见仁见智):

const uint32 i = 0x01000000 * 0x7F
               + 0x00010000 * 'E'
               + 0x00000100 * 'L'
               + 0x00000001 * 'F';

编辑:我会修改我自己的答案,即即使您采用这样的方法,您也可能希望在代码中包含十六进制版本的文字作为注释,为了人们,例如以十六进制形式搜索幻数,或在其他地方看到它。考虑到这一点,因为无论如何最好有十六进制版本,最好就像其他人所说的那样以十六进制形式定义数字并添加关于它代表什么的评论,即使用您的原始版本。

【讨论】:

  • @nightcracker 它可能比原版更糟糕,但它仍然构成原版的替代品,这正是 OP 所要求的。
  • 我想这就是为什么我在之后立即删除了我的反对票。
【解决方案2】:

许多编译器都有这个鲜为人知的特性:多字符字面量。

uint32 ELF_MAGIC_NUMBER = '\177ELF';

恐怕你必须对非字符使用八进制数。

啊,差点忘了!那是编译器依赖的意思,所以我不会这样做。

但如果你可以使用 C++11,你可以使用 constexpr 和用户定义的文字:

constexpr uint32_t operator "" _mc (const char *str, size_t len)
{
    return len==4? 
         (str[0] << 24) | (str[1] << 16) | (str[2] << 8) | str[3] : 
         throw "_mc literal must be of length 4";
}

constexpr uint32_t ELF_MAGIC_NUMBER = "\177ELF"_mc;

这有一个很好的功能,你可以使用字符串连接来使用十六进制字符:

constexpr uint32_t ELF_MAGIC_NUMBER = "\x7F""ELF"_mc;

【讨论】:

  • 如果你跳过了 tue 字符串连接怎么办?
  • @Yakk: octal-escape-sequence 定义为 1、2 或 3 个八进制数字,但 hexadecimal-escape-sequence 可以有任何十六进制数字的数量,因此“\x7FELF”将被解析为三个字符:'\x7FE', 'L', 'F'。然后,由于operator"" 正在处理char 并且CHAR_BITS 很可能8 第一个字符将被截断为0xFE。幸运的是,我的len 检查会发现它并给出编译器错误。
【解决方案3】:

既然你买得起 C++11,你可以定义一个小的 constexpr 帮助器,它会启用编译时评估:

#include <cstdint>

constexpr std::uint32_t from_bytes(char b1, char b2, char b3, char b4)
{
    return b4 + 
           (static_cast<std::uint32_t>(b3) << 8) + 
           (static_cast<std::uint32_t>(b2) << 16) + 
           (static_cast<std::uint32_t>(b1) << 24);
}

这样,您的代码看起来与原始版本没有太大区别:

const std::uint32_t ELF_MAGIC_NUMBER = from_bytes(0x7F, 'E', 'L', 'F');

int main()
{
    static_assert(ELF_MAGIC_NUMBER == 0x7F454c46, "!");
}

这是live example

【讨论】:

  • 对不起,我可能偏向 C,但我觉得没有比这更简单的宇宙了。 FROM_BYTES 的 C 风格宏版本会更简单 imo。我也不主张这样做。
  • @Andrey:我尽量避免使用宏,但好吧,我想这是个人喜好问题。
  • @Andrey:我只想写std::uint32_t(b3),ctor 风格。我同意演员表不利于可读性。但我仍然会使用一个函数来保证类型安全。
  • 我喜欢!编译时评估,我可以按照自己的意愿编写代码。
  • @TheMask:应该不会太难。例如,基于可变参数模板的版本可能类似于this。通过std::enable_if 进行类型限制作为练习留给读者;)
【解决方案4】:

这个使用用户定义文字的解决方案太丑了,我可能会哭:

#include <string>
#include <sstream>
#include <cstdint>
#include <cstddef>
#include <cassert>

uint32_t operator"" _u32s(const char* str, std::size_t size)
{
    std::istringstream ss(std::string(str, size));
    std::string token;
    int shift = 24;
    uint32_t result = 0;
    while (std::getline(ss, token, ',') && shift >= 0) {
        int value = 0;
        if (token.substr(0,2) == "0x") {
            std::stringstream hexss;
            hexss << std::hex << token;
            hexss >> value;
        } else if (token.length() == 1) {
            value = token[0];
        }
        result |= (value << shift);
        shift -= 8;
    }
    return result;
}

int main() {
    assert("0x7F,E,L,F"_u32s == 0x7F454c46);
}

基本上,您现在可以使用文字"0x7F,E,L,F"_u32s。显然它远没有使用编译时解决方案那么好,但这是一个有趣的实验。

【讨论】:

  • 我从没想过要这样做......这真的很酷! +1 独创性 :)
【解决方案5】:

使用模板元编程

执行相同的任务

metafunction idiom

template <char a,char b,char c , char d>
struct  MAGIC_NUMBER {
    enum { value =d + 
           (static_cast<uint32_t>(c) << 8) + 
           (static_cast<uint32_t>(b) << 16) + 
           (static_cast<uint32_t>(a) << 24)  };
};




/*
 * usage
 */  
    const  uint32_t ELF_MAGIC_NUMBER = MAGIC_NUMBER<0x7F, 'E', 'L', 'F'>::value;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-26
    • 2015-07-31
    • 1970-01-01
    • 1970-01-01
    • 2018-12-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多