【问题标题】:Efficiency of strings as constants?字符串作为常量的效率?
【发布时间】:2012-12-10 00:09:25
【问题描述】:

我是一个相对的 C/C++ 菜鸟,但在 C# 和许多其他语言方面经验丰富,目前正在用 C++ 构建游戏引擎。

对于输入系统(键/鼠标/操纵杆),我想到了使用字符串文字来简化游戏特定输入事件的配置,而不是枚举。但我不完全确定 C 编译器将如何优化它 - 每次进行逐个字符的字符串比较可能效率低下。

这是基本思想 - 从设备事件到“游戏特定事件”的映射将在 std::map 中,带有 int/enum 键和 const char * 值:

Mappings[ KEY_X ] = "Jump";
Mappings[ KEY_Y ] = "Shoot";
Mappings[ MOUSE_1 ] = "Shoot";

这些可以是硬编码的,可以从配置文件中加载等等(字符串可以简化解析和保存,以及添加新的事件类型)

然后在游戏中,我可以只处理游戏特定的事件:

if ( IsDown( "Jump" ) ) {
}
if ( IsSinglePress( "Shoot" ) ) {
}

问题是,我所有的字符串文字和 const char * 会被神奇地优化掉并最终像枚举或整数常量一样高效吗?例如

if ( IsDown( GAMEKEY_SHOOT ) ) {
   // ...
}

还是会进行字符串比较等?我希望编译器会看到相同字符串文字的实例,将其存储一次,并在整个过程中使用单个指针值,但不确定。

【问题讨论】:

    标签: c++ c string


    【解决方案1】:

    如果所有字符串常量都在同一个文件中,它们可能会被折叠到同一个内存位置。如果它们位于单独编译的多个文件中,或者如果您动态读取它们,则它们不会。

    由于您正在使用 C(++),因此您可以通过两种方式比较字符串(并且您在问题中没有提及任何内容):指针标识 (==) 或逐字符比较(strncmp)。如果你做的是前者,它是非常不可靠的,因为它取决于编译器的变化无常(并且在第二种情况下肯定会失败);如果是后者,那么您知道将进行字符串比较。

    免去您的麻烦,并以您已经知道正确的方式去做:枚举或常量。在输入/输出处转换一次,然后您可以在内部将它们作为数字处理,既快速又安全。

    【讨论】:

    • 嗯 - 谢谢 - 听起来字符串的想法不会很好。我认为 C++ 中没有可以将枚举标识符转换为字符串以简化加载/保存映射的反射类型功能是否正确?
    • 一份清单总比散落一地好。
    • @QuadrupleA:正确;在 C++ 中enums 完全是一个编译时概念。
    【解决方案2】:

    您多次提到在枚举和字符串之间进行转换。这是我的做法。 (我认为这个想法归功于 gcc。)创建一个类似 action.def 的文件

    ENUM(Jump)
    ENUM(Shoot)
    

    在头文件中定义这样的枚举

    #define ENUM(a) a,
    Enum Actions {
    #include "action.def"
    };
    #undef ENUM
    

    在需要将枚举成员映射到字符串的 .cpp 文件中,执行此操作

    struct EnumMap {
        int value;
        const char * name;
    }
    #define ENUM(a) { (int) a, #a },
    struct EnumMap {
    #include "action.def"
    } ActionMap[];
    #undef ENUM
    

    然后您的代码使用 ActionMap[] 将枚举值转换为字符串并返回。

    【讨论】:

    • 有趣 - 通常不喜欢预处理器黑客,但像这样在一个地方定义东西会很酷。
    • 这是我想到的枚举/字符串链接;这是一种相当流行的技术。
    【解决方案3】:

    不管怎样,多次写出相同的字符串字面量是容易出错的——如果你输错了其中一个怎么办?

    如果您使用全局变量,您可以确保它们具有相同的地址,因为您指的是完全相同的变量:

    char const * const Jump = "Jump";
    

    (或在标头中声明并在单个 .cpp 中初始化实际字符串值)

    并用作:

    Mappings[ KEY_X ] = Jump;
    ...
    if ( IsDown( Jump ) ) {
        ...
    

    我不会假装这是很棒的风格(它不是),如果你只是想要一个枚举和字符串表示之间的内置链接,有更好的方法来获得它。

    【讨论】:

    • 嗯,这实际上可能是一个不错的方法 - 如果字符串在头文件中是常量,则比较都将是指针,如此快速且低存储 - 我可以直接使用它们进行解析在保存/加载期间。
    • 指针是 4 或 8 字节数字。枚举成员通常是 1 字节整数。大多数机器都有更快地处理小整数的指令。
    【解决方案4】:

    就标准而言,编译器可能会也可能不会使同一字符串文字的各种实例指向同一内存位置

    是否所有字符串文字都是不同的(即存储在不重叠的对象中)是由实现定义的。

    (C++11,§2.14.5 ¶12)

    另外,如果这些值是动态分配的,来自某个缓冲区,......它们肯定有与字符串文字不同的地址;所以检查一个是否等于另一个的唯一明智的方法是比较字符串的字节(以strcmp-like 方式),这显然比指针比较慢。

    此外,我看不出在这些设置中使用字符串的意义:只需使用enums,它的执行速度至少一样快(可能更快)并且更不容易出错(如果你写了错误的枚举value 你会得到一个编译错误,如果你在字符串常量中打错字,它会被静默接受)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-17
      • 1970-01-01
      • 2020-01-21
      相关资源
      最近更新 更多