在条件表达式求值过程中,现代编译器(或者这些编译器从C89开始就存在了)是否在short circuit evaluated个代码中替换了下面这样的情况?

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

与在处理strnlen(...)期间考虑右操作数一样,并且当strnlen(...)内的C字符串的游程长度超过外部条件表达式的右操作数(在本例中为2)时,strnlen(...)将爆发?

  • 如果我没有预先指定字符串长度,这会有什么关系吗?
  • 如果我go 掉了IF个内部表达式中的括号,会有什么关系吗?
  • 如果我将操作数和运算符切换为<,会有什么关系吗?

推荐答案

Maybe,具体取决于编译器.让我们来看一些例子,它们是用GCC 13.2.0和clang 17.0.1编译的,都是在优化级别-O3with extensions enabled(注意,strnlen是POSIX,而不是标准C).

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

Cang和GCC都将其优化为mov eax, 1; ret.这是因为它们知道strnlen的行为,并且可以替换调用的返回值,而不需要在运行时计算它.(在GCC,这个数字是implemented via __builtin_strnlen).

如果strnlen函数不是已知的内置函数,但可以内联:

inline int my_strnlen(char const* s, int n) {
    for (int 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;
}

在这里,叮当优化到mov eax, 1,但GCC发出一个循环.

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

__attribute__((pure)) int f(char);
inline int my_strnlen_f(char const* s, int n) {
    for (int 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再次发出一个循环;clang发出一些相当笨拙的代码,但这表明它知道调用f不超过3次:

p:                                      # @p
        push    rbx
        mov     edi, 116
        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
        call    f@PLT
        test    eax, eax
        jne     .LBB2_3
        mov     edi, 105
        call    f@PLT
        xor     ebx, ebx
        test    eax, eax
        sete    bl
        mov     eax, ebx
        pop     rbx
        ret
  • 如果我没有预先指定字符串的长度,这会有影响吗?

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

  • 如果我从if内部表达式中删除了圆括号,会有什么关系吗?

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

  • 如果我将操作数和运算符切换为<,会有什么关系吗?

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

C++相关问答推荐

如何在C中只使用一个带双方括号([i][j])访问语法的malloc来分配动态大小的2d数组?

如何正确地索引C中的 struct 指针数组?

难以理解Makefile隐含规则

减法运算结果的平方的最快方法?

以下声明和定义之间的区别

C++中矢量类型定义和数据保护的高效解决方案

为静态库做准备中的奇怪行为

Sizeof(&Q;字符串&Q;)的正确输出是什么?

S和查尔有什么不同[1]?

C";中的ANN运行时判断失败#2-变量outputLayer;周围的堆栈已损坏.运行后出错

从CentOS 7到Raspberry PI 2B的交叉编译-无法让LIBC和System Include标头一起工作

为什么我的半数组测试和奇数组测试不起作用?(我使用Assert进行调试)

如何在C中定义指向函数的指针并将该指针赋给函数?

与外部SPI闪存通信时是否应禁用中断?

为什么我在我的代码中得到错误和退出代码-1073741819(0xC0000005),但如果我添加了一个不相关的打印语句,它仍然有效?

按字典顺序打印具有给定字符的所有可能字符串

根据输入/输出将 C 编译过程分为预处理、编译、汇编和链接步骤

#define X Defined(Y) 是有效的 C/C++ 宏定义吗?

如何用用户输入的多个字符串填充数组?

Zig 中 C 的system函数的惯用替代方案