问题 编译时“strlen()”有效吗?


有时需要将字符串的长度与常量进行比较。
例如:

if ( line.length() > 2 )
{
    // Do something...
}

但我试图避免在代码中使用“魔术”常量。
通常我使用这样的代码:

if ( line.length() > strlen("[]") )
{
    // Do something...
}

由于函数调用,它更具可读性,但效率不高。
我写了模板函数如下:

template<size_t N>
size_t _lenof(const char (&)[N])
{
    return N - 1;
}

template<size_t N>
size_t _lenof(const wchar_t (&)[N])
{
    return N - 1;
}

// Using:
if ( line.length() > _lenof("[]") )
{
    // Do something...
}

在发布版本(VisualStudio 2008)中,它生成了非常好的代码:

cmp    dword ptr [esp+27Ch],2 
jbe    011D7FA5 

好的是编译器在二进制输出中不包含“[]”字符串。

它是特定于编译器的优化还是常见行为?


11163
2017-09-08 02:39


起源

你可以使用一个模板用于所有数组类型,这是这样的: template<class T, size_t N> size_t _lenof(const T (&)[N]) { return N - 1; },应该仍然像你的例子一样工作。 - Evan Teran
@Evan Teran:好主意,但这些函数只对字符串(char / wchar_t数组)有意义,因为终止'\ 0'。你的函数适用于int [10]并返回9 - 我认为它没有意义;) - Dmitriy
@Dmitriy:的确如此 - Evan Teran
@Evan Teran:N == 0?怎么样?甚至_lenof(“”)N == 1.函数返回0 - 它是正确的。 - Dmitriy
我认为gcc的机器非常有可能解决这个问题。我的意思是,如果它关心消除 sin(1)那么该死也请 strlen("foo") :) - Johannes Schaub - litb


答案:


内联函数调用的能力是特定于编译器的优化  一种常见的行为。也就是说,许多编译器都可以这样做,但它们并不是必需的。


5
2017-09-08 02:46



期望的优化不(仅)需要内联。它要求在编译时计算字符串的长度。 - Steve Jessop
不过,这不是真正的优化。长度不会在运行时计算,仍然会调用任何长度 _lenof 功能。不标准 要求 实现为字符串文字提供类型 const char[N]?并且不是使编译器推导出模板函数的参数所需的这种类型的值 N? - Rob Kennedy
对不起,我误解了你的答案所指的是什么 - 由于某种原因,我在谈论“由于[strlen]函数调用而效率不高”。如果编译器不能内联_lenof,那么它可能无法内联任何内容,并且通常是一个非常糟糕的C ++编译器。任何严肃的模板使用都会是噩梦...... - Steve Jessop


为什么不

sizeof“[]” -  1;

(减去一个尾随空值。你可以 sizeof“[]” - sizeof'\ 0',但sizeof'\ 0' 通常是C中的sizeof(int),而“ - 1”是 完全可读。)


12
2017-09-08 02:48



是不适用于宽字符串(例如,L“[]”)。 - Dmitriy
可以固定宽字符串。就像是: (sizeof(L"[]") / sizeof(L"")) - 1 - Evan Teran
@Evan Teran:是的,但你应该使用宏来使它更具可读性。恕我直言,它更C风格,但不是C ++ - Dmitriy
我同意,只是指出这种技术可用于宽字符串。我没有赞同它:-P。 - Evan Teran


我认为大多数编译器都会优化它 何时启用优化。如果他们被禁用,可能会使您的程序速度降低得多。

我更喜欢你的模板功能,因为他们保证不会打电话 strlen 在运行时。 当然,而不是为其编写单独的函数 char 和 wchar_t,你可以添加另一个模板参数,并获得适用于任何类型的函数:

template <typename Char_t, int len>
int static_strlen(const Char_t (&)[N] array){
  return len / sizeof(Char_t) - 1;
}

(正如在评论中已经提到的,如果传递了一组int,这会产生有趣的结果,但是你可能会这样做吗?毕竟这是为了字符串)

最后一点,这个名字 _strlen 是 。命名空间作用域中以下划线开头的所有名称都保留给实现。你冒着一些讨厌的命名冲突的风险。

顺便说一下,为什么“[]”比2更少的神奇常数?

在这两种情况下,如果与其进行比较的字符串格式发生变化,则必须更改文字。


3
2017-09-08 11:01



无论出于何种原因,您的功能似乎不比使用strlen快。但是,它确实比使用std :: char_traits <Char_t> :: length更快,所以它仍然有用,因为strlen只适用于char数组。 - leetNightshade


#define TWO 2
#define STRING_LENGTH 2
/* ... etc ... */

说真的,为什么要经历所有这些麻烦只是为了避免键入2?老实说,我认为你的代码可读性较差,而其他程序员会盯着你,就像你从过滤器中抽出旧咖啡一样。


-7
2017-09-08 02:49



这只是一个例子。在实际代码中,它看起来像“一些字符串”。在这种情况下你要计算字符数吗? :) - Dmitriy
对,我是。我会的。我做到了。 - Jed Smith
@Jed Smith::)如果字符串发生变化,您确定不要忘记更改宏定义吗? - Dmitriy
@Jed Smith:“2”如何比sizeof(“[]”更易读/可理解? - Malkocoglu
@Jed:嘿,如果它只有2,那么为什么要打扰?当它是“某些字符串,我可能会改变,我不想打扰眼睛的长度”这是一个不同的故事。 OTOH,strlen()只是一个问题,当它真的是一个问题。假设它是一个问题是一种猜测形式。 - Mike Dunlavey