字符串作为常量的效率

Efficiency of strings as constants?

本文关键字:效率 常量 字符串      更新时间:2023-10-16

我是一个相对的C/C++noob,但在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" ) ) {
}

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

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

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

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

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

省得头疼,用你已经知道的正确方法来做:枚举或常量。在输入/输出时转换一次,然后您可以在内部将它们作为数字处理,这既快速又安全。

您提到过几次枚举和字符串之间的转换。以下是我的做法。(我认为这个想法应该归功于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[]将枚举值转换为字符串并返回。

无论如何,多次写出相同的字符串文字都很容易出错——如果您错误地键入了其中一个呢?

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

char const * const Jump = "Jump";

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

并用作:

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

我不会假装这是一种很棒的风格(事实并非如此),如果你只是想在枚举和字符串表示之间建立一个内置链接,有更好的方法可以获得它

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

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

(C++11,§2.14.5¶12)

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

此外,我不认为在这些设置中使用字符串有什么意义:只需使用enums,它的执行速度至少同样快(可能更快),而且它不太容易出错(如果你写错了枚举值,就会出现编译错误,如果你在字符串常量中键入了错误,它会被默默接受)。