发新话题
打印

【讨论】-1 左移 32 位结果还是 -1?

不过,我用objdump出的和gcc -S出的不一样?      
上帝说,有问题,找GOOGLE 写程序是很神圣的事情!同样只是装系统,卖菜的大娘会的事情不见得就跟卖菜一样了。

TOP

看了看 80386 的指令系统, 有这么一段话:
The [B]8086 does not mask the shift count[/B]. However, [B]all other[/B] Intel Architecture processors (starting with the Intel 286 processor) do mask the shift count to [B]5 bits[/B], resulting in a maximum count of 31. This masking is done in all operating modes (including the virtual-8086 mode) [B]to reduce the maximum execution time of the instructions[/B].
      
'
◆ 发帖时请【突出主题】, 以便您的问题能够及时得到回复
◆ 发帖时请将您的【代码】或者【脚本】写在 [code] 和 [/code] 中间

TOP

[QUOTE=DarkSpy]32 位的机器上, 左移或者右移 32 bit 编译器将会做取模运算,然后再次移动, 所以左移和右移 32bit 是没有意义的...[/QUOTE]/ P* S8 J! Y* M7 v+ u  p

/ O- d& Q% A7 q0 I( n习惯性说编译器了,说错了.1 k, \& z9 e, F, W4 Z" i$ f
应该是硬件的做法      
-----------------------------------------
http://www.darkspy.org/blog

TOP

-1用补吗表示就是11111111111111111111111111111111,左移当然还是-1了。      
白金之心

TOP

-1<<n汇编出来的是
4 P8 q7 E( R  S/ ~" i        movl    $-1, %eax+ u1 o( p. ]) M. m
        sall    %cl, %eax
( y! o7 T; B& n% n
6 R9 ]- Q7 C3 f3 O! ?6 F如果是逻辑左移的话就是00 Q0 B( O' {) G' Q$ W6 Q! M* X
     即:' l% |7 [0 R6 g# _4 G5 O+ M, }
         movl    $-1, %eax
/ q! T' s- {# q, Y        shll    %cl, %eax
% _2 u" j  J! |1 r没有什么怪的,这时gcc 的处理,看一下汇编就知道了      

TOP

高人高见       
'
◆ 发帖时请【突出主题】, 以便您的问题能够及时得到回复
◆ 发帖时请将您的【代码】或者【脚本】写在 [code] 和 [/code] 中间

TOP

[QUOTE=sof_ljh]-1<<n汇编出来的是3 O. r" v% k  v7 J! i0 c
        movl    $-1, %eax% o0 z0 A. N: N0 a; }4 K) g# S
        sall    %cl, %eax+ G/ x. i* ~; I) r0 _7 C0 s

: ^4 k9 N) C! i" S, z如果是逻辑左移的话就是09 s  }4 i" K1 k8 ?! `' Y9 C2 w
     即:. ?; M/ Y/ O% E* p* v# z
         movl    $-1, %eax; I) \0 x, Z) t( @: F+ d1 G, \# q
        shll    %cl, %eax! A0 S% A0 }; \' m% a: F1 a
没有什么怪的,这时gcc 的处理,看一下汇编就知道了[/QUOTE]5 R3 {6 z, M3 o$ s# s

1 j5 w# W0 Z( Y好像你自己没有仔细看汇编书啊。
( T) M- ?2 r: |3 ?: {5 v2 R" F" O( V- ]. K! K6 n: |* Z, O
汇编指令 sal 和 shl 是完全等价的,它们只是助记符不同罢了,其操作码完全一样。因此算术左移和逻辑左移是一样的。移位以后,都是用 0 填充腾空的位。7 u0 P1 \1 U$ k- b2 M( X
! D+ y  O. ?5 g0 y) d$ T
只有算术右移和逻辑右移才是不同的。算术右移用符号位填补腾空的位,逻辑右移用 0 填补腾空位。. l! o2 C& c5 Y
# u7 @+ b  e# H
前面有人已经解释得很清楚了,-1 左移32位,对于 386 以后的CPU来说,就是左移 0 位(假定是在运行期间由机器的 CPU 来执行 sal %cl, %eax 或者等价的 shl %cl, %eax),当然不变了。" O5 X" v: {! k& H; |6 [) s' E. ~

5 f7 v% s, n* }如果用常量表达式 -1 << 32 ,那么编译器会计算出这是一个 0。0 k7 V  o8 H0 _9 z5 ^

/ z, h$ i7 k3 a# l7 F另外个人认为, 386 以后的硬件把移位对字长进行取模的做法,没有利,只有弊。还是 -1 左移 32 位之后变成 0 比较合理。      
渴望春天 come, sweet May!

TOP

[QUOTE=yangkwch]-1用补吗表示就是11111111111111111111111111111111,左移当然还是-1了。[/QUOTE]
3 m/ }& K1 Z4 }; _6 O8 e/ c* O' }* O; Q8 x! ]8 a2 u
显然这个解释是不对的。
6 a; n% U1 f0 ]6 K% Y6 X" y7 N) S& Q7 |, y% U
按照这个逻辑,-1 左移 1 位仍然是  -1 了。这肯定不对吧?
' b% a+ o5 N1 o( t) M& [
7 H$ I, \" ?1 T) j8 W左移之后,腾空的位是用 0 来填充的,因此,如果不是左移了 0 位,-1 左移之后就不会等于 -1 了。5 X7 |7 ?% M1 T; q

% ~. f/ m! N9 J- l# H* |1 Q前面有人已经解释了,左移 32 位正好就是左移了 0 位(CPU 把左移的位数 32 对于字长 32 进行取模运算,得到 0),因此 -1 左移 32 位仍然是 -1。
& ?+ ^# E, y( g. g
& f- K9 Y$ ~( v另外,-1 循环左移或者循环右移任意位,仍然是 -1,这是正确的。**移位** 和 **循环移位** 是不同的汇编指令。C 语言中的移位(就是 “<<” 和 “>>”)大概都编译成汇编的“移位”了,不生成汇编的“循环移位”。
. e. w; e4 Q5 a2 l
0 R2 P; T6 V: n3 z% b" D5 g9 S其实在汇编中,循环移位又分为带进位的和不带进位的两种。刚才说的 -1 循环左移或者循环右移任意位,仍然是 -1,其实是对不带进位的循环移位指令来说的。而对于带进位的循环指令,“-1 循环左移或者循环右移任意位,仍然是 -1” 这句话也是不能普遍成立的(如果再加上进位标志 CF=1 这个条件,就成立了)。      
渴望春天 come, sweet May!

TOP

good       
'
◆ 发帖时请【突出主题】, 以便您的问题能够及时得到回复
◆ 发帖时请将您的【代码】或者【脚本】写在 [code] 和 [/code] 中间

TOP

这个帖子似乎应该是了结了,虽然楼上说看汇编代码能够明白了。5 M$ l' u( c1 a( c
不过我还是想灌次水。
5 I: p5 z% _2 _/ d2 @! \ ( f; b! X% H6 V1 e& Y4 `6 [6 N
这是C99标准里
" l; h" a! W1 i" z1 v  O 6.5.7 Bitwise shift operators
1 c4 U5 Y% K& L 一节中提到的:
# f, @( q# o& U+ u* ~" K6 R/ j Semantics
0 i7 a4 ?2 e; g; e     ……* ?6 V3 o6 i4 Y0 R# }/ s8 q. I, P- X
4 The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with
0 g/ o# t. e; b: f   zeros. If E1 has an unsigned type, the value of the result is E1 × 2E2 , reduced modulo- `) v8 h& k' G0 M. o+ H7 w
   one more than the maximum value representable in the result type. If E1 has a signed
7 X# N+ I# s2 |% r   type and nonnegative value, and E1 × 2E2 is representable in the result type, then that is
/ {% ^3 j" i* W0 ]3 Z+ `$ l. B% v  ? 3 R* ^" p% \# A4 U6 q; h9 C
在GCC参考手册
) Y. D) K1 I" W$ ]* d& F9 H 4.5 Integers (Page 207)有这样一段话:
8 @. w+ @8 a: Y4 d9 b6 F) b
6 U* g. F5 x% d5 [ • The results of some bitwise operations on signed integers (C90 6.3, C99 6.5).
: w+ B& k8 t( h; m* I# s3 p; m   Bitwise operators act on the representation of the value including both the sign and, F0 \7 F7 [6 [$ I1 Z
   value bits, where the sign bit is considered immediately above the highest-value value
) j6 W% z9 X4 K$ L" _( [   bit. Signed ‘>>’ acts on negative numbers by sign extension.8 ~' w4 T) ~4 s4 E& D
   GCC does not use the latitude given in C99 only to treat certain aspects of signed ‘<<’( s* |; ~1 }6 _/ f( b
   as undefined, but this is subject to change.# w+ Y6 N6 m, W1 B, K' v
( H* h3 _7 E- \$ y2 Q
我不想翻译,很容易理解,我不知道版主讨论这个是不是因为GCC没有遵循标准,还是其他什么原因引来一群人的争吵?
4 }6 Q# d0 y! l1 {- `. ~! c 另外,我也想顺便问下,(但我不想发新帖了)为什么我的编译器汇编后的代码总是比别人的长,是不是被SUSE修改过的GCC版本。4 r) ~" n" u+ g+ ^/ Y2 a
我用的是SUSE自带的GCC,比如上面的汇编代码,在我机器中出来的是
复制内容到剪贴板
代码:
BirdSky:/home/souldump/bin # gcc -S main.c
main.c:10:2: warning: no newline at end of file
BirdSky:/home/souldump/bin # cat main.s
         .file   "main.c"
         .section        .rodata
.LC0:
         .string "%d\n"
         .text
.globl main
         .type   main, @function
main:
         leal    4(%esp), %ecx
         andl    $-16, %esp
         pushl   -4(%ecx)
         pushl   %ebp
         movl    %esp, %ebp
         pushl   %ecx
         subl    $36, %esp
         movl    $32, -8(%ebp)
         movl    -8(%ebp), %ecx
         movl    $-1, %eax
         sall    %cl, %eax
         movl    %eax, 4(%esp)
         movl    $.LC0, (%esp)
         call    printf
         movl    $0, %eax
         addl    $36, %esp
         popl    %ecx
         popl    %ebp
         leal    -4(%ecx), %esp
         ret
         .size   main, .-main
         .ident  "GCC: (GNU) 4.1.0 (SUSE Linux)"
         .section        .note.GNU-stack,"",@progbits
长了好多,很多次用自己的汇编代码和别人的比较都会长一些。      

TOP

发新话题