C#解惑27: 变幻莫测的i值

谜题27: 变幻莫测的i值

你的任务仍旧是要指出这个程序将打印什么。

class Shifty

{

static void Main()

{

int i = 0;

while (-1 << i != 0)

i++;

System.Console.WriteLine(i);

}

}

解惑27: 变幻莫测的i值

常量-1是所有32位都被置位的int数值(0xffffffff)。左移操作符将0移入到由移位所空出的右边最低位,因此表达式(-1 << i)将最右边的i位设置为0,并保持其余的32-i位为1。很明显,这个循环将完成32次迭代,因为(-1 << i)对任何小于32的i来说都不等于0。你可能期望在i等于32时终止条件测试返回false,从而使程序打印32,但是它打印的并不是32。实际上,它不会打印任何东西,而是进入了一个无限循环。

问题在于(-1 << 32)等于-1而不是0,因为移位操作符只使用其右操作数的低5位作为移位长度。或者是低6位,如果其左操作数是一个long类型数值[C#语言规范 7.8]。这条规则作用于全部的两个移位操作符:<<和>>。移位长度总是介于0到31之间,如果左操作数是long类型,则介于0到63之间。这个长度是对32取余的,如果左操作数是long类型的,则对64取余。如果试图对一个int数值移位32位,或者是对一个long数值移位64位,都只能返回这个数值本身。没有任何移位长度可以让一个int数值丢弃其所有的32位,或者是让一个long数值丢弃其所有的64位。

幸运的是,有一个非常容易的方法能够修正该问题。我们不是让-1重复地移位不同的移位长度,而是将前一次移位操作的结果保存起来,并且让它在每一次迭代时都向左再移1位。下面这个版本的程序就可以打印我们所期望的32:

class Shifty

{

static void Main()

{

int distance = 0;

for (int val = -1; val != 0; val <<= 1)

distance++;

System.Console.WriteLine(distance);

}

}

这个修正过的程序说明了一条普遍的原则:如果可能的话,移位长度应该是常量。如果移位长度紧盯着你不放,那么你让其值超过31,或者如果左操作数是long类型的,让其值超过63的可能性就会大大降低。当然,并不总是可以使用常量的移位长度。当必须使用一个非常量的移位长度时,请确保你的程序可以应付这种容易产生问题的情况,或者根本不会碰到这种情况。

前面提到的移位操作符的行为还有另外一个令人震惊的结果。很多程序员都希望具有负移位长度的右移操作符可以起到左移操作符的作用,反之亦然。但是情况并非如此。右移操作符总是起到右移的作用,而左移操作符也总是起到左移的作用。负的移位长度通过只保留低5位而去除其他位的方式被转换成了正的移位长度——如果左操作数是long类型的,则保留低6位。因此,如果要将一个int数值左移,其移位长度为-1,那么移位的效果是它被左移了31位。

总之,移位长度是对32取余的,或者如果左操作数是long类型的,则对64取余。因此,使用任何移位操作符和移位长度,都不可能将一个数值的所有位全部移走。同时,我们也不可能用右移操作符来执行左移操作,反之亦然。如果可能的话,请使用常量的移位长度,如果移位长度不能设为常量,那么就要千万小心。

语言设计者可能应该考虑将移位长度限制在从0到以位为单位的类型长度的范围内,并且修改移位长度为类型长度时的语义,让其返回0。尽管这可以避免在本谜题中所展示的混乱情况,但是它可能会带来负面的执行结果,因为C#的移位操作符的语义正是许多处理器上的移位指令语义。

版权声明:本文为博主http://www.zuiniusn.com原创文章,未经博主允许不得转载。

时间: 2024-08-28 12:10:06

C#解惑27: 变幻莫测的i值的相关文章

谜题27:变幻莫测的i值

与谜题26中的程序一样,下面的程序也包含了一个记录在终止前有多少次迭代的循环.与那个程序不同的是,这个程序使用的是左移操作符(<<).你的任务照旧是要指出这个程序将打印什么.当你阅读这个程序时,请记住 Java 使用的是基于2的补码的二进制算术运算,因此-1在任何有符号的整数类型中(byte.short.int或long)的表示都是所有的位被置位: public class Shifty { public static void main(String[] args) { int i = 0;

Map集合中,关于取值和遍历的相关操作

这是自己的关于map集合的相关操作的小研究,分享给大家. 主要代码内容包含以下: 1,map集合的遍历 2,根据key值获取value值 3,根据value值获取key值 4,返回最大value值对应的key值 5,获取最大key值,最小key值,最大value值,最小value值 上代码: 1 @Test 2 public void bb1(){//测试代码 3 Integer value=0; 4 Map<Integer,Integer> map=new HashMap<Intege

理解java移位运算符

  移位运算符操作的对象就是二进制的位,可以单独用移位运算符来处理int型整数.  运算符       含义       例子       << 左移运算符,将运算符左边的对象向左移动运算符右边指定的位数(在低位补0) x<<3 >> "有符号"右移运算 符,将运算符左边的对象向右移动运算符右边指定的位数.使用符号扩展机制,也就是说,如果值为正,则在高位补0,如果值为负,则在高位补1. x>>3 >>> "无符

java线程安全问题之静态变量、实例变量、局部变量

Java多线程编程中,存在很多线程安全问题,至于什么是线程安全呢,给出一个通俗易懂的概念还是蛮难的,如同<java并发编程实践>中所说: 写道 给线程安全下定义比较困难.存在很多种定义,如:"一个类在可以被多个线程安全调用时就是线程安全的". 此处不赘述了,首先给出静态变量.实例变量.局部变量在多线程环境下的线程安全问题结论,然后用示例验证,请大家擦亮眼睛,有错必究,否则误人子弟! 静态变量:线程非安全. 静态变量即类变量,位于方法区,为所有对象共享,共享一份内存,一旦静态

Python之路【第九篇】:Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy

Python之路[第九篇]:Python操作 RabbitMQ.Redis.Memcache.SQLAlchemy Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度.Memcached基于一个存储键/值对的hashmap.其守护进程(daemon )是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信. Memc

Python--环境配置、编码风格、基础概念、基本数据类型

#######python######## python的基本 [[email protected] ~]# yum install python -y [[email protected] ~]# python -V    ##查看python版本 Python 2.7.5 [[email protected] ~]# python --version Python 2.7.5 为什么用/usr/bin/python 关于python脚本中的第一行内容 : #!/usr/bin/python

人事管理系统——数据库操作类

连接数据库类主要代码: 1 package PersonSystem; 2 3 import java.sql.*; 4 /** 5 * 6 * 连接数据库的类 7 * 8 */ 9 public class Database 10 { 11 private Statement stmt = null; 12 ResultSet rs = null; 13 private Connection conn = null; 14 String sql; 15 String strurl = "jdb

java9-3 返回类型

1. 返回值类型 基本类型:(基本类型简单) 引用类型: 类:返回的是该类的对象 1 class Student2 { 2 public void study() { 3 System.out.println("Good Good Study,Day Day Up"); 4 } 5 } 6 7 class StudentDemo1 { 8 public Student2 getStudent() { 9 return new Student2(); //相当于 得到 new Stude

【C#公共帮助类】 WebHelper帮助类

如果你是一个新手,如果你刚接触MVC,如果你跟着置顶的那个项目,我们肯定会用到这里面的几个帮助类 它们都在Common类库下,大家一定要记住要点:取其精华去其糟粕,切勿拿来主义~ ApplicationCache.cs 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Web; 6 7 namespace Common 8 { 9