为什么编译器被unsigned int混淆为SIGSEGV?

1 c unsigned gcc segmentation-fault

我已经提炼了一个问题,这让我弄清楚发生了什么,但仍然不是原因.

int main() {
    unsigned int a = 2;
    char c[2] = {};
    char* p = &c[1];
    return p[1 - a];
}
Run Code Online (Sandbox Code Playgroud)

重写最后一行时更清楚一点.

    return *(p + (1 - a));      /* equivalent */
    return *(p + 1 - a);        /* works */
    return *(p + (1 - (int)a)); /* works */
Run Code Online (Sandbox Code Playgroud)

我很惊讶编译器没有在内部删除括号.而且更多的是它显然试图保持类型的暂时负面结果unsigned int.除非这不是分段错误的原因.在汇编程序输出中,带括号和不带括号的代码之间的差别很小.

-   movl    $1, %eax
-   subl    -12(%rbp), %eax
-   movl    %eax, %edx
+   movl    -12(%rbp), %eax
+   movl    $1, %edx
+   subq    %rax, %rdx
Run Code Online (Sandbox Code Playgroud)

Pau*_*l R 5

问题是在表达式中1 - a,1被提升为unsigned int,所以你有1U - 2U下溢UINT_MAX.这里带回家的消息是,在同一表达式中混合有符号和无符号整数时,您必须非常小心.

如果您当然启用了警告,那么好的编译器可能不会警告您这种用法:

main.c: In function 'int main()':
main.c:5:19: warning: '*((void*)& c +4294967296)' is used uninitialized in this function [-Wuninitialized]
     return p[1 - a];
Run Code Online (Sandbox Code Playgroud)