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

min*_*low 7 c optimization

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

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

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

  • 如果我没有预先分配字符串长度会有什么影响吗?
  • 如果我从IF内部表达式中删除括号,会有什么影响吗?
  • 如果我将操作数和运算符切换为 ,会有影响吗<

eca*_*mur 8

也许,取决于编译器。让我们看一些使用 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;
}
Run Code Online (Sandbox Code Playgroud)

clang 和 gcc 都将其优化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;
}
Run Code Online (Sandbox Code Playgroud)

这里 clang进行了优化mov eax, 1但 gcc 发出了一个循环。

最后,对于标记pure为告诉优化器它没有副作用的未知谓词:

__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;
}
Run Code Online (Sandbox Code Playgroud)

gcc 再次发出循环;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
Run Code Online (Sandbox Code Playgroud)
  • 如果我没有预先分配字符串长度会有什么影响吗?

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

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

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

  • 如果我将操作数和运算符切换为 ,会有影响吗<

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