![](https://csdnimg.cn/release/download_crawler_static/768598/bg10.jpg)
4 Chapter 1: Bit wizardry
1.1.2 Size of pointer is size of long
On sane architectures a pointer fits into a type long integer. When programming for a 32-bit architecture
(where the size of int and long coincide) casting pointers to integers (and back) will work. The same
code will fail on 64-bit machines. If you have to cast pointers to an integer type, cast them to long. For
portable code better avoid casting pointers to integer types.
1.1.3 Shifts and division
With two’s complement arithmetic (that is: on likely every computer you’ll ever touch) division and
multiplication by powers of two is right and left shift, respectively. This is true for unsigned types and
for multiplication (left shift) with signed types. Division with signed types rounds toward zero, as one
would expect, but right shift is a division (by a power of two) that rounds to minus infinity:
int a = -1;
int c = a >> 1; // c == -1
int d = a / 2; // d == 0
The compiler still uses a shift instruction for the division, but with a ‘fix’ for negative values:
9:test.cc @ int foo(int a)
10:test.cc @ {
285 0003 8B442410 movl 16(%esp),%eax // move argument to %eax
11:test.cc @ int s = a >> 1;
289 0007 89C1 movl %eax,%ecx
290 0009 D1F9 sarl $1,%ecx
12:test.cc @ int d = a / 2;
293 000b 89C2 movl %eax,%edx
294 000d C1EA1F shrl $31,%edx // fix: %edx=(%edx<0?1:0)
295 0010 01D0 addl %edx,%eax // fix: add one if a<0
296 0012 D1F8 sarl $1,%eax
For unsigned types the shift would suffice. One more reason to use unsigned types whenever possible.
The assembler listing was generated from C code via the following commands:
# create assembler code:
c++ -S -fverbose-asm -g -O2 test.cc -o test.s
# create asm interlaced with source lines:
as -alhnd test.s > test.lst
There are two types of right shifts: a so-called logical and an arithmetical shift. The logical version
(shrl in the above fragment) always fills the higher bits with zeros, corresponding to division of unsigned
types. The arithmetical shift (sarl in the above fragment) fills in ones or zeros, according to the most
significant bit of the original word.
Computing remainders modulo a power of two with unsigned types is equivalent to a bit-and:
ulong a = b % 32; // == b & (32-1)
All of the above is done by the compiler’s optimization wherever possible.
Division by (compile time) constants can be replaced by multiplications and shifts. The magic machinery
inside the compiler does it for you. A division by the constant 10 is compiled to:
5:test.cc @ ulong foo(ulong a)
6:test.cc @ {
7:test.cc @ ulong b = a / 10;
290 0000 8B442404 movl 4(%esp),%eax
291 0004 F7250000 mull .LC33 // value == 0xcccccccd
292 000a 89D0 movl %edx,%eax
293 000c C1E803 shrl $3,%eax
Thereby it is sometimes reasonable to have separate code branches with explicit special values. Similar
optimizations can be used for the modulo operation if the modulus is a compile time constant. For
example, using modulus 10,000:
8:test.cc @ ulong foo(ulong a)
9:test.cc @ {
53 0000 8B4C2404 movl 4(%esp),%ecx
[fxtbook draft of 2008-November-04]