因此,我正在学习C,并且目前正在学习《计算机系统:程序员的观点》第三版和相关实验室。我现在正在做的第一个实验,我必须为此实现以下功能。
/*
* fitsBits - return 1 if x can be represented as an
* n-bit, two's complement integer.
* 1 <= n <= 32
* Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 15
* Rating: 2
*/
int fitsBits(int x, int n) {
int sign_bit = (x >> 31 & 1);
int minus_one = ~1+1;
int n_minus_one = n + minus_one;
return (!(x >> n_minus_one) & !sign_bit)
| (!(~(x >> n_minus_one)) & sign_bit);
}
该函数将针对以下测试函数运行_a_lot_的测试用例。
int test_fitsBits(int x, int n)
{
int TMin_n = -(1 << (n-1));
int TMax_n = (1 << (n-1)) - 1;
return x >= TMin_n && x <= TMax_n;
}
现在,这是发生奇怪的事情的地方:默认情况下,代码使用以下标志编译-O -Wall -m32
针对测试功能运行我的代码会产生以下断言失败:错误:测试fitsBits(-2147483648 [0x80000000],32 [0x20])失败... ...给出1 [0x1]。应该为0 [0x0]
看来我的代码是正确的,而testcode是伪造的。经过进一步调查,似乎test_function产生以下中间结果:
> Tmin:-2147483648
> TMax_n:2147483647
> x: -2147483648
> x >= TMin_n: 1
> x <= TMax_n: 0
> result: 0
显然-2147483648 <= 2147483647,但是比较以某种方式产生0。
如果我在不带-O标志的情况下编译该程序,则所有测试均成功通过。有人可以阐明这种行为吗?
编辑:对不起,汇编代码是可怕的布局,不知道确切地如何快速修复
Assembly Code without -O;
.section __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 11
.globl _test_fitsBits
.align 4, 0x90
_test_fitsBits: ## @test_fitsBits
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp0:
.cfi_def_cfa_offset 16
Ltmp1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp2:
.cfi_def_cfa_register %rbp
xorl %eax, %eax
movb %al, %cl
movl $1, %eax
xorl %edx, %edx
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -8(%rbp), %esi
subl $1, %esi
movb %cl, -17(%rbp) ## 1-byte Spill
movl %esi, %ecx
## kill: CL<def> ECX<kill>
movl %eax, %esi
shll %cl, %esi
subl %esi, %edx
movl %edx, -12(%rbp)
movl -8(%rbp), %edx
subl $1, %edx
movl %edx, %ecx
## kill: CL<def> ECX<kill>
shll %cl, %eax
subl $1, %eax
movl %eax, -16(%rbp)
movl -4(%rbp), %eax
cmpl -12(%rbp), %eax
movb -17(%rbp), %cl ## 1-byte Reload
movb %cl, -18(%rbp) ## 1-byte Spill
jl LBB0_2
## BB#1:
movl -4(%rbp), %eax
cmpl -16(%rbp), %eax
setle %cl
movb %cl, -18(%rbp) ## 1-byte Spill
LBB0_2:
movb -18(%rbp), %al ## 1-byte Reload
andb $1, %al
movzbl %al, %eax
popq %rbp
retq
.cfi_endproc
.globl _main
.align 4, 0x90
_main: ## @main
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp3:
.cfi_def_cfa_offset 16
Ltmp4:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp5:
.cfi_def_cfa_register %rbp
xorl %eax, %eax
movl $0, -4(%rbp)
movl %edi, -8(%rbp)
movq %rsi, -16(%rbp)
popq %rbp
retq
.cfi_endproc
.subsections_via_symbols
带有-O的汇编代码:
.section __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 11
.globl _test_fitsBits
.align 4, 0x90
_test_fitsBits: ## @test_fitsBits
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp0:
.cfi_def_cfa_offset 16
Ltmp1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp2:
.cfi_def_cfa_register %rbp
## kill: ESI<def> ESI<kill> RSI<def>
leal -1(%rsi), %ecx
movl $1, %eax
## kill: CL<def> CL<kill> ECX<kill>
shll %cl, %eax
movl %eax, %ecx
negl %ecx
cmpl %edi, %eax
setg %al
cmpl %ecx, %edi
setge %cl
andb %al, %cl
movzbl %cl, %eax
popq %rbp
retq
.cfi_endproc
.globl _main
.align 4, 0x90
_main: ## @main
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp3:
.cfi_def_cfa_offset 16
Ltmp4:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp5:
.cfi_def_cfa_register %rbp
xorl %eax, %eax
popq %rbp
retq
.cfi_endproc
.subsections_via_symbols
oauh的答案解决了为什么这是不确定的行为,但没有解决问题仅出现于的原因-O
。
-O
使编译器更加努力地寻找要优化的东西。在这种情况下,似乎已经意识到x <= (1 << (n-1)) - 1
与x < (1 << (n-1))
-确实相同,因此可以删除- 1
。
但是,这仅等效于旧代码1 << (n-1)
,不会溢出。无论如何,编译器都会进行此优化,因为如果发生溢出,则结果将是错误的-这是因为如果发生溢出,则将允许任何结果,因为这是未定义的行为。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句