C语言 编译器是否会优化“strnlen(mystring,32)> 2”以在运行长度超过2时立即停止循环?

cvxl0en2  于 11个月前  发布在  其他
关注(0)|答案(2)|浏览(86)

现代编译器(或者可能是自C89以来就存在的编译器)是否在条件表达式求值期间用 * 短路求值 * 代码代替下面的情况?

char mystring[32] = "this is a long line";
if((strnlen(mystring, 32)) > 2)
{
    return 1;
}

字符串
由于在处理strnlen(...)的过程中考虑了右操作数,并且当strnlen(...)内的C字符串的运行长度超过外部条件表达式的右操作数(本例中为2)时,strnlen(...)会中断?

  • 如果我没有预先指定字符串的长度,这会有影响吗?
  • 如果我从IF内部表达式中删除括号,这会有影响吗?
  • 如果我把操作数和运算符都换成了<,会有什么影响吗?
ycl3bljg

ycl3bljg1#

  • 也许 *,这取决于编译器。让我们看一些例子,用gcc 13.2.0和clang 17.0.1编译,都是在优化级别-O3,并启用了扩展(注意strnlen是POSIX,不是标准C)。
int p() {
    char mystring[32] = "this is a long line";
    return strnlen(mystring, 32) > 2;
}

字符串
clang和gcc都将optimize this转换为mov eax, 1; ret。这是因为它们知道strnlen的行为,并且可以替换调用的返回值,而无需在运行时对其进行评估。(在gcc中,这是通过__builtin_strnlen实现的)。
如果strnlen函数不是已知的内置函数,但可以内联:

inline size_t my_strnlen(char const* s, size_t n) {
    for (size_t i = 0; i != n; ++i)
        if (s[i] == 0)
            return i;
    return n;
}
int p() {
    char mystring[32] = "this is a long line";
    return my_strnlen(mystring, 32) > 2;
}


在这里将optimizes转换为mov eax, 1,但gcc发出一个循环。
最后,对于一个标记为pure的未知 predicate ,它告诉优化器它没有副作用:

__attribute__((pure)) int f(char);
inline size_t my_strnlen_f(char const* s, size_t n) {
    for (size_t i = 0; i != n; ++i)
        if (f(s[i]))
            return i;
    return n;
}
int p() {
    char mystring[32] = "this is a long line";
    return my_strnlen_f(mystring, 32) > 2;
}


gcc再次emits一个循环; clang发出一些相当笨拙的代码(ebx是怎么回事?),尽管如此,它仍然表明它知道f需要被调用不超过3次,前3个字符的字符代码-它优化了整个字符串:

p:                                      # @p
        push    rbx
        mov     edi, 116 # 't'
        call    f@PLT
        xor     ebx, ebx
        test    eax, eax
        je      .LBB2_1
.LBB2_3:
        mov     eax, ebx
        pop     rbx
        ret
.LBB2_1:
        mov     edi, 104 # 'h'
        call    f@PLT
        test    eax, eax
        jne     .LBB2_3
        mov     edi, 105 # 'i'
        call    f@PLT
        xor     ebx, ebx
        test    eax, eax
        sete    bl
        mov     eax, ebx
        pop     rbx
        ret

  • 如果我没有预先指定字符串的长度,这会有影响吗?

不,在这种情况下,C语言只会将缓冲区大小设置为字符串字面量的大小(字符串长度+ 1表示终止符)。

  • 如果我从IF内部表达式中删除括号,这会有影响吗?

不,优化器运行在不包括这些语法细节的程序表示上。

  • 如果我把操作数和运算符都换成了<,这会有什么影响吗?

几乎可以肯定不是,优化器能够理解它们是等价的。

0lvr5msh

0lvr5msh2#

它说strnlen是一个内置函数,所以gcc可以对strnlen的语义进行优化。
如果你的字符串是常量,它可以优化它(当然这只是一个有趣的事实;它并没有真正帮助你)。
如果编译器事先不知道你的字符串,gcc不会做任何优化。理论上,它可以内联函数并优化结果代码,但实际上,它不会发生。你可以自己实现这样的优化-据我所知,clang有一种方便的语言来描述优化。
我不能说其他编译器。
您可以使用专用的godbolt site来检查这些东西。

相关问题