哨兵可以更快

2020-09-10 04:30:32

编程中的一个标准技巧是使用“哨值”。这些是有效表示元数据的特殊值。

C语言将字符串表示为以空字符结尾的字符序列。空字符是指示字符串长度的前哨。例如,当使用C语言时,我的名字(“Lemire”)将在内存中表示为字符串“Lemire\0”,其中我将空字符表示为“\0”。我名字的长度(6个字符)从不显式存储。

如果您是一名长期的C程序员,您可能会认为空哨兵是一个“显而易见”的概念,但是编程新手常常觉得它有违直觉。这不是一个微不足道的想法。

在C中,空点是“空间高效的”。几乎可以肯定的是,其他编程语言对于每个字符串都有更多的内存开销。

然而,我记得从性能的角度来看,读到以NULL结尾的字符串是C语言中的一个设计缺陷。实际上,您经常需要知道字符串的长度,当您知道时,需要扫描所有字符串以找到空哨点并计算字符串长度。其他古老的编程语言,如Pascal,都有带长度前缀的字符串值。今天,我希望大多数编程语言都避免使用以NULL结尾的字符串。

但是,虽然您本身不需要空字符,但是使用哨兵也可以产生计算效率高的代码。让我来说明一下。

假设给了您一个字符串,并且您想跳过所有前导空格。如果只给出字符串的起点及其长度或终点,那么您可能会遇到如下代码:

Const char*SKIP_LEADING_SPACE(const char*start,const char*end){ WHILE((开始!=结束)&;&;(is_space(*start){ 开始++; } 返回启动; }

对于每个字符,您必须检查您是否仍在字符串中,并且必须检查它是否是空格。你最终会做两个比较!C++编程语言强烈倾向于这种构造。流行的函数式编程习惯用法通常构建在类似的代码之上。

当然,你可以变得更聪明。例如,您可以根据字符串大小进行分支,以尝试避免对每个字符执行一次检查。但如果字符串长度很难预测,最终可能会带来很小的好处和复杂的代码。

如果您知道字符串是以NULL结尾的,或者它至少包含一个可用作事实上的哨兵的非空格字符,该怎么办呢?然后,您可以使用更简单的代码,您只需加载一个新字符并检查它是否为空格:

常量字符*SKIP_LEADING_SPACE(常量字符*开始){ While(is_space(*start)){ 开始++; } 返回启动; }。

它可以快得多。我编写了一个小基准,在那里我生成具有随机数量的前导空格的随机字符串。在我的测试中,前哨方法快了40%。

当然,结果在很大程度上取决于您的数据和您试图解决的确切问题。然而,我认为当您需要编写高性能代码,并且您正在处理不规则数据时,将哨兵牢记在心是值得的。