精华内容
下载资源
问答
  • 深入理解JVM一字节码执行

    千次阅读 2017-04-16 22:29:30
    局部变量表使用索引定位的方式来读取slot,索引范围是从0到slot的最大个数,比如读(取)一个索引为n的数据,则就是读(取)第n个slot数据。而如果这个n索引的数据是个64bit的数据,那么读(取),就是要同时连续读...

    前言

    物理机对指令的执行建立在cpu、硬件、指令集、操作系统层面。而虚拟机对指令的执行可以自行实现,JVM Specification中定义了执行引擎这个概念模型作为JVM的统一Facade。通常会有解释器执行(逐条解释字节码并执行)、编译器执行(即时编译为本地后代码执行)两种执行字节码方式的执行引擎。

    栈帧结构

    每个方法调用开始到退出,都对应着一个“栈帧”进站与出站。
    运行时栈帧

    作为虚拟中中方法调用与方法执行的数据结构,它包含方法执行必备的局部变量表、操作数栈、动态链接与方法返回地址等信息
    一个方法的执行,可能存在着多层方法调用,对于执行引擎来说,只有当前方法对应的当前栈帧才有效,才是活动的,也就是位于栈顶的栈帧(栈后进先出LIFO)

    这里写图片描述

    这里写图片描述

    栈帧-局部变量表

    局部变量表是栈帧中的一部分,是一个变量值存储空间,主要存储方法中的局部变量、方法参数
    在class文件编译生成时,(Code属性)就决定了局部变量表的内容、以及最大容量:

     public class TestClass {
    
    	public static void main(String[] args) {
    		int m = 0;
    		inc(m);
    	}
    
    	public static int inc(int m) {
    		int f = 2;
    		return incNext(m + f);
    
    	}
    
    	public static int incNext(int m) {
    		int g = 2;
    		return m + g;
    	}
    }
    
    javap -c .\TestClass.class
    Compiled from "TestClass.java"
    public class com.zs.jvm.byteCode.TestClass {
      public com.zs.jvm.byteCode.TestClass();
        Code:
           0: aload_0
           // Method java/lang/Object."<init>":()V
           1: invokespecial #1                  
           4: return
    
      public static void main(java.lang.String[]);
        Code:
           0: iconst_0
           1: istore_1
           2: iload_1
           // Method inc:(I)I
           3: invokestatic  #2                  
           6: pop
           7: return
    
      public static int inc(int);
        Code:
           0: iconst_2
           1: istore_1
           2: iload_0
           3: iload_1
           4: iadd
           // Method incNext:(I)I
           5: invokestatic  #3                  
           8: ireturn
    
      public static int incNext(int);
        Code:
           0: iconst_2
           1: istore_1
           2: iload_0
           3: iload_1
           4: iadd
           5: ireturn
    }
    

    局部变量表中以“变量槽”(variable slot)作为存储数据的最小单位,并且每个slot都应该可以存储一个 byte、int、boolean、char、 short、 int、 float、 reference或returnAddress类型的数据(也就是除了double、long的基本变量类型数据和引用类型数据)。

    JVMhotSpot中,通常每个slot是32bit,对于double、long,64bit的数据,jvm会把这两种数据分配在两个连续slot中,每次读(写)会分配为两次读(写)单个slot完成,而且不允许读写操作只执行其中一个slot。另外,因为局部变量表示线程私有的,所以这里对64位数据读写不会有线程安全问题。

    JVM对reference类型的数据没有过多说明,但是一个Reference数据应该保证:
    1.通过这个Reference可以间接或者直接找到对应对象在heap中存放的起始地址。
    2.通过这个Reference可以间接或直接的找到这个对象在MethodArea区中存储的类型信息。

    局部变量表使用索引定位的方式来读取slot,索引范围是从0到slot的最大个数,比如读(取)一个索引为n的数据,则就是读(取)第n个slot数据。而如果这个n索引的数据是个64bit的数据,那么读(取),就是要同时连续读(取)第n个与第n+1个slot。

    对于对象实例类型的方法调用,通常局部变量表中第0位索引存放的是这个对象实例的引用,也就是this。

    returnAddress类型目前已经很少见了,可以忽略,不详述。

    栈帧-操作数栈(Operand Stack)

    jvm的解释执行引擎是基于栈的执行引擎,这句话中的栈其实就是指Operand Stack。

    是一个后进先出LIFO的数据模型,字节码指令执行时会不断的向操作数栈中插入数据、提取数据,称为出栈入栈。

    栈帧-动态连接

    每个栈帧都包含了一个常量池中的符号引用,这个符号引用指向这个栈帧所属的方法,而字节码方法调用会(如,invokespecial、invokestatic等方法调用指令,后文详细介绍)以这个符号引用作为参数。

    这一类型的符号引用一部分会在类加载(Linking—resolve,参考深入理解JVM一加载机制)时或者第一次使用之后转换为内存中的直接引用,这个种转换成为 静态解析;

    另一部分会在每一次运行期间(方法调用时)转换为直接引用,这部分称为动态连接。

    栈帧-方法返回地址

    就是决定当前方法调用退出后,应该返回的位置。
    通常会有两种方式结束一个方法调用:正常退出、异常退出。

    正常退出时,调用者的PC计数器的值可以作为返回地址,通常返回地址保持在栈帧中。
    异常退出时,返回地址是通过异常处理器来确定的,一般不会保存在栈帧中。

    一个方法的退出会恢复这个方法调用者的局部变量表、操作数栈,把返回值(如果有的话)压入调用者的操作数栈,让PC计数器执行下一条指令。

    方法调用

    方法调用是在运行时确定调用哪个方法的操作,是一个很频繁的操作。

    因为class在编译后操作指令都是一堆常量池中符号引用,并没有直接指向内存地址入口(直接引用)。这给java带了了强大的动态扩展空间,但是也带来了复杂度,通常是在类加载(Linking—resolve,参考深入理解JVM一加载机制)、甚至运行时才确定具体执行的是哪一个方法。

    解析

    字节码执行,其实主要是执行方法中指令,如果在编译期就可以确定要执行的具体方法,那么对这类方法的调用的确认成为解析。

    java代码编译后的字节码中,方法调用的代码都是一堆常量池中的符号引用,需要通过解析将符号引用转换为(包含内存的入口的)直接引用。

    在方法调用时有如下几种指令:

    //(私有方法、实例构造器、父类方法的调用)
    invokespecial
    //(静态方法调用)
    invokestatic
    //(虚方法调用\final修饰的方法调用)
    invokevirtual
    //(调用接口方法,会在运行时确认一个接口的实现;调用重载方法等产生多态选择的情况)
    invokeinterface
    //(、、、、、、、)
    invokedynamic
    

    示例:

    public class TestGCChild implements GCTest {
    
        static String TEST_NAME = "Test";
    
        static GCTest t = new TestGCChild();
    
        public static void main(String[] args) throws Exception {
    
            TestGCChild.easyStatic();// static mehotd
    
            t.easy();// interface implements
    
            new TestGCChild().easyUnStatic();// normal method
    
            new TestGCChild().easyFinal();// normal method
    
        }
    
        @Override
        public String easy() {
            return "easy";
        }
    
        public static String easyStatic() {
            return "easy";
        }
    
        public String easyUnStatic() {
            return "easy";
        }
    
        public final String easyFinal() {
            return "easy";
        }
    }
    
    Compiled from "TestGCChild.java"
    public class com.zs.test.TestGCChild implements com.zs.test.GCTest {
      static java.lang.String TEST_NAME;
    
      static com.zs.test.GCTest t;
    
      static {};
        Code:
           0: ldc           #14                 // String Test
           2: putstatic     #16                 // Field TEST_NAME:Ljava/lang/String;
           5: new           #1                  // class com/zs/test/TestGCChild
           8: dup
           9: invokespecial #18                 // Method "<init>":()V
          12: putstatic     #21                 // Field t:Lcom/zs/test/GCTest;
          15: return
    
      public com.zs.test.TestGCChild();
        Code:
           0: aload_0
           1: invokespecial #25                 // Method java/lang/Object."<init>":()V
           4: return
    
      public static void main(java.lang.String[]) throws java.lang.Exception;
        Code:
           0: invokestatic  #33                 // Method easyStatic:()Ljava/lang/String;
           3: pop
           4: getstatic     #21                 // Field t:Lcom/zs/test/GCTest;
           7: invokeinterface #37,  1           // InterfaceMethod com/zs/test/GCTest.easy:()Ljava/lang/String;
          12: pop
          13: new           #1                  // class com/zs/test/TestGCChild
          16: dup
          17: invokespecial #18                 // Method "<init>":()V
          20: invokevirtual #40                 // Method easyUnStatic:()Ljava/lang/String;
          23: pop
          24: new           #1                  // class com/zs/test/TestGCChild
          27: dup
          28: invokespecial #18                 // Method "<init>":()V
          31: invokevirtual #43                 // Method easyFinal:()Ljava/lang/String;
          34: pop
          35: return
    
      public java.lang.String easy();
        Code:
           0: ldc           #48                 // String easy
           2: areturn
    
      public static java.lang.String easyStatic();
        Code:
           0: ldc           #48                 // String easy
           2: areturn
    
      public java.lang.String easyUnStatic();
        Code:
           0: ldc           #48                 // String easy
           2: areturn
    
      public final java.lang.String easyFinal();
        Code:
           0: ldc           #48                 // String easy
           2: areturn
    }
    

    对于私有方法、实例构造器、父类方法、静态方法的调用,符合编译期可知,运行时不变的要求。(因为私有方法不会被覆盖或者改写,静态方法只属于当前类也不会被改写。)除此之外,被final修饰的方法,因为它无法被重写,所以也是确定的,它虽然使用了invokevirtual调用,但也是在编译期间即可确定的。这类在编译时即可确认实际调用实现的方法称为非虚方法,使用invokespeical、invokestatic指令、以及使用了final修饰的方法都属于非虚方法。除此之外都是虚方法,需要进行多态选择,后期绑定实际的方法。

    分派

    静态分派:
    我们先看一个小程序,请输出下列代码的执行结果:

    class Human {
    
    }
    
    class Man extends Human {
    
    }
    
    class Woman extends Human {
    
    }
    
    public class TestDispatch {
    
    	public void test(Human h) {
    		System.out.println("human");
    	}
    
    	public void test(Woman w) {
    		System.out.println("Woman");
    	}
    
    	public void test(Man m) {
    		System.out.println("Man");
    	}
    
    	public static void main(String[] args) {
    		Human human = new Human();
    		Human woman = new Woman();
    		Human man = new Man();
    		TestDispatch test = new TestDispatch();
    
    		test.test(man);
    		test.test(woman);
    		test.test(human);
    	}
    }
    

    //outpu:
    human
    human
    human

    如上边的示例,Human m(等号左边)称为静态类型(Static Type)或者称为显示类型(Apparent Type),等号右边部分称为实际类型(Actual Type),是真正初始化的对象。
    静态类型都是在编译期决定并不可改变的,而实际类型只能到运行时才能真正决定,在编译期(编译后的字节码)无法确认实际类型。

    因为重载,众多重载方法中具体执行哪一个方法是在编译期确定的(编译器会自动选择最合适的一个),所以产生了上边的代码执行结果。

    接着,我们引出静态分派(static dispatch)的概念:所有依赖静态类型来决定具体方法执行的分派动作(分派可理解为对多态的选择)称为静态分派。

    动态分派:

    动态分配最典型的例子就是“重写”(override).
    看一下这个例子:

    class Human {
    
        String say() {
            System.out.println("human");
            return "human";
        }
    }
    
    class Son extends Human {
    
        public String say() {
            System.out.println("Son");
            return "Son";
        }
    
    }
    
    class Father extends Human {
    
        public String say() {
            System.out.println("Father");
            return "Father";
        }
    }
    
    public class TestGC {
        static Human human = new Human();
    
        static Human father = new Father();
    
        static Human son = new Son();
    
        public static void main(String[] args) throws Exception {
            human.say();// human
            father.say();// Father
            son.say();// Son
        }
    }
    //output:
    //human
    //Father
    //Son
    

    可以看出来,运行的say()其实是具体实现类型的方法,并不是Human.say(),很明显无法根据静态编译来确定实际执行的方法。我们再看看这段代码的字节码,特别看一下main()中的指令:

    public class com.zs.test.TestGC {
      static com.zs.test.Human human;
    
      static com.zs.test.Human father;
    
      static com.zs.test.Human son;
    
      static {};
        Code:
           0: new           #12                 // class com/zs/test/Human
           3: dup
           4: invokespecial #14                 // Method com/zs/test/Human."<init>":()V
           7: putstatic     #17                 // Field human:Lcom/zs/test/Human;
          10: new           #19                 // class com/zs/test/Father
          13: dup
          14: invokespecial #21                 // Method com/zs/test/Father."<init>":()V
          17: putstatic     #22                 // Field father:Lcom/zs/test/Human;
          20: new           #24                 // class com/zs/test/Son
          23: dup
          24: invokespecial #26                 // Method com/zs/test/Son."<init>":()V
          27: putstatic     #27                 // Field son:Lcom/zs/test/Human;
          30: return
    
      public com.zs.test.TestGC();
        Code:
           0: aload_0
           1: invokespecial #31                 // Method java/lang/Object."<init>":()V
           4: return
    
      public static void main(java.lang.String[]) throws java.lang.Exception;
        Code:
           0: getstatic     #17                 // Field human:Lcom/zs/test/Human;
           3: invokevirtual #39                 // Method com/zs/test/Human.say:()Ljava/lang/String;
           6: pop
           7: getstatic     #22                 // Field father:Lcom/zs/test/Human;
          10: invokevirtual #39                 // Method com/zs/test/Human.say:()Ljava/lang/String;
          13: pop
          14: getstatic     #27                 // Field son:Lcom/zs/test/Human;
          17: invokevirtual #39                 // Method com/zs/test/Human.say:()Ljava/lang/String;
          20: pop
          21: return
    }
    

    我们看到,从静态编译的字节码中无法判定方法调用者的实际类型,那么jvm是如何知道具体的实现者是哪个呢?是如何进行动态选择的呢?关键就在invokevirtual指令:

    invokevirtual指令动态查找的过程如下:

    1.在操作栈的栈顶弹出首元素,将首元素所指向对象的实际类型记作C类型。
    2.在C类型中按方法描述符等常量查找匹配的方法,如果找到了,再进行访问权限的校验,如果校验通过允许访问,那么就直接返回这个方法的直接引用。如果访问权限校验不允许访问,抛出IllegalAccessError异常。
    3.如果在C类型中找不到匹配的方法,那么就从它的直接父类开始从下到上查找,找到后再进行访问权限校验,通过后返回。
    4.如果始终找不到匹配方法(在C类型、以及C的父类),那么久抛出AbstractMethodError异常。

    这样看来,如果子类中没有override父类中的方法,那么调用会直接执行父类方法。如下:

    class Human {
    
        String say() {
            System.out.println("human");
            return "human";
        }
    }
    
    class Son extends Human {
    
    //    public String say() {
    //        System.out.println("Son");
    //        return "Son";
    //    }
    
    }
    
    class Father extends Human {
    
        // public String say() {
        // System.out.println("Father");
        // return "Father";
        // }
    }
    
    public class TestGC {
        static Human human = new Human();
    
        static Human father = new Father();
    
        static Human son = new Son();
    
        public static void main(String[] args) throws Exception {
            human.say();// human
            father.say();// human
            son.say();// human
        }
    }
    
    //output:
    // human
    // human
    // human
    

    动态分配:在运行期间,根据对象的实际类型确认具体要执行方法的分配。

    待续…

    展开全文
  • 为什么一字节是8位?

    千次阅读 2015-05-18 11:09:54
    十进制整数范围[-128,127]或[0, 255] 最小的可寻址存储单元 在C语言中的体现 C标准规定char至少是8bit,1byte存储不了的字符使用wchar(宽字符)类型存储,占用2字节!此外,要想操作位有两种方式,一是...

    历史回溯

    回溯70年计算机发展史,多到64和少到1位的计算机都曾设计过,比如常见的1, 5, 6, 7, 8, 9, 12, 18, 20 和36 bits;

    1bit--这个最原始,设想一下只有1bit的指令,1bit的数据宽度,1bit的寄存器,一次只能操作1bit,比老爷机还要老爷机,会不会疯掉?好像不会存在的东西,但是Motorola MC14500B就是这么个玩意。虽然很简单,但是不要小看它!因为它才开启人类计算时代的新纪元,就像智人从森林走进草原开辟人类的新世纪一样。参考文献1bitwiki

    5bit--这个要来源于5-bit的 baudot codes(波多码),不过编码的字符有限,很快就被淘汰了!再到6bit,其表示的字符也是不够充足,再加上逻辑电路复杂性!也是没活多久!

    然后就是12, 18 (或 36) -bit在 60s, 70s 以及80s的某个时段的计算机架构中十分流行; 20-bit bytes在50s的"IAS machines"十分普遍 (具体细节就不罗列了,有兴趣自行wiki或google,关键字: x bit computer, 其中x是具体的bit位数)

    到目前为止,所有计算机都遵循“8bits/byte,byte作为最小可寻址存储单位”这一没有“明文”规定的规定!简单了解一下为什么!

    瞅瞅8bit/byte的来源:

    "The IBM System/360 introduced byte-addressable memory with 8-bit bytes, as opposed to bit-addressable or decimal digit-addressable or word-addressable memory, although its general purpose registers were 32 bits wide, and addresses were contained in the lower 24 bits of those addresses. Different models of System/360 had different internal data path widths; the IBM System/360 Model 30 (1965) implemented the 32-bit System/360 architecture, but had an 8 bit native path width, and performed 32-bit arithmetic 8 bits at a time.

    The first widely adopted 8-bit microprocessor was the Intel 8080, being used in many hobbyist computers of the late 1970s and early 1980s, often running the CP/M operating system; it had 8-bit data words and 16-bit addresses. The Zilog Z80 (compatible with the 8080) and the Motorola 6800 were also used in similar computers. The Z80 and the MOS Technology 6502 8-bit CPUs were widely used in home computers and second- and third-generation game consoles of the '70s and '80s. Many 8-bit CPUs or microcontrollers are the basis of today's ubiquitous embedded systems."——引自8bitwiki

    因此,所有体系结构最终都以IBM的先河,而以2的幂方以及bits包含在最小可寻址单元内byte内而统一起来!!!

    目前bit和byte的比较

    bit:

    • 计算机中的最小存储单元
    • 存储内容总是0或1
    • 所有二进制状态的实体都可以使用1bit表示
    • 8bits组成1byte
    • 不能够单独寻址

    byte:

    • 1byte包含8bits
    • 可以存储所有ASCII所有字符(这是它包含8bits的初衷)
    • 十进制整数范围[-128,127]或[0, 255]
    • 最小的可寻址存储单元

    在C语言中的体现

    C标准规定char至少是8bit,1byte存储不了的字符使用wchar(宽字符)类型存储,占用2字节!此外,要想操作位有两种方式,一是位域,二是寄存器操作!

    总结

    时代在前进,计算机在进化,为了更好的量化现实客观世界,导致计算机的bit不断再进化!IBM的先河,开辟了1byte-8bits的架构,而后纷纷效仿,一直延续至今!此外,为了表示世界语言,8bit是办不到的,目前的Unicode用2字节,16bits编码,即C中的wchar。


    展开全文
  • 关于带符号与无符号类型:整型int、...一字节表示八位,即1byte=8bit; int 4byte=32bit,有符号范围为-2^31-1~2^31-1,无符号范围为0~2^32-1; long 4byte=32bit,同int型; double,8byte=64bit,范围:1.79769e+308 ~

    关于带符号与无符号类型:整型int、short和long默认为带符号型,要获得无符号型必须规定该类型为unsigned。

    一字节表示八位,即1byte=8bit;

    int 4byte=32bit,有符号范围为-2^31-1~2^31-1,无符号范围为0~2^32-1;

    long 4byte=32bit,同int型;

    double,8byte=64bit,范围:1.79769e+308 ~ 2.22507e-308

    long double: 12 byte = 96 bit范围: 1.18973e+4932 ~ 3.3621e-4932

    float: 4 byte = 32 bit范围: 3.40282e+038 ~ 1.17549e-038

    int、unsigned、long、unsigned long 、double最大数量级为10亿,即表示十进制的位数不超过十个,

    可以保存所有九位整数,而short只能保存5位。

    展开全文
  • 一字节表示八位,即:1byte = 8 bit,bit存储内容是0和1 以byte来举例子,一个byte有8个bit,一共可以存储8个0或1,所以其最大值为8个bit都是1的时候。 1 1 1 1 1 1 1 1 也许你会有些疑惑,1 1 1 1 1 1 1 1转换成十...

    基本数据类型是怎么决定范围的呢?
    基本数据类型相信大家都很熟悉,但是基本数据类型怎么决定范围的呢?
    下面就让我带着大家了解一下基本数据类型是怎么决定范围的~
    在这里插入图片描述
    (:开口就老UC了

    ———————————————————正文分割线————————————————————
    相信很多人对下面的这张图并不陌生,详细的记录着每个数据类型的内存占用字节数和其取值的范围。

    在这里插入图片描述

    …那么他们是如何通过字节数来确定其范围的呢?

    我们只需记下面这句话:

    一字节表示八位,即:1byte = 8 bit,bit存储内容是0或1
    在这里插入图片描述

    以byte来举例子,一个byte有8个bit,一共可以存储8个0或1,所以其最大值为8个bit都是1的时候。

    1 1 1 1 1 1 1 1

    也许你会有些疑惑,1 1 1 1 1 1 1 1转换成十进制不是255吗?怎么byte的最大值是127呢?

    了解为什么之前,我们首先要有以下的概念:

    整数分为有符号和无符号两类,而在二进制中,由于只能使用0或1来进行表示,所以把最左边的这一位变成了符号位,1代表负数,0代表正数,剩下的就是这个数的绝对值部分。

    有符号数:
    1-------------------------------------------1 1 1 1 1 1 1
    (我是符号位,表示这个数是负数)

    0-------------------------------------------1 1 1 1 1 1 1
    (我是符号位,表示这个数是正数)

    无符号数:
    1 1 1 1 1 1 1 1(没有符号位,全都表示值)

    所以无符号数最大可以表示到255,最有符号数只能到127。

    由于byte为有符号数,可以表示正负,最大值其实就是0 1111111,转换成二进制就是127

    问题又来了,按照上面说法byte最小值应该是1 111 1111,最左边为符号位,那这个不是应该等于 - 127吗?

    我们先来看看byte正数所能表示的范围:
    0 000 0000 ----> 0 111 1111 == + 0 ----> + 127
    byte负数所能表示范围:
    1 000 0000 ----> 1 111 1111 == - 0 ----> - 127

    这个时候我们会发现 -0 和 +0其实都是同一个东西,都是0。所以计算机将1 000 0000定义为-128。

    在这里插入图片描述
    我们观察上面的表就能发现,凡是有正负范围的,负数最大范围都会比正数的多1,就是因为有 -0 的存在,让负数的范围可以比正数多 1。
    总结起来就是:

    补码比原码和反码多一位的原因是因为原码和补码是区分正0和负0的,而补码不需要区分,这个空出来的位置用来表示比正数范围大1的数。

    补充:在二进制中,负数要转换成十进制所要做的工作比正数多:

    原码: 计算机中将一个数字转换为二进制,并在其最高位加上符号的一种表示方法。
    反码:根据表示规定,正数的反码就是其本身,而负数的反码是符号位不变,其余各位取反。
    补码:根据表示规定,正数的补码就是其本身,而负数的补码是其反码+1。

    第一步:将负数的绝对值转为二进制原码
    第二步:求其原码的反码
    第三步:求得的补码即负数的二进制表示结果

    需要注意的是:-128没有原码和反码,因为超出了范围,只有补码。
    一个byte的原码和反码范围均为-127~+127
    在这里插入图片描述
    负数的补码除了上面的原码取反加1这种做法之外,还可以通过补码的定义:

    [ X ]补 = 模-| X |

    模是什么?

    由于计算机只有加法器,没有减法器,所以计算机内减法只有转化为加法才能存储。而用补码代替原码,可把减法转变为加法。而进行运算中出现的进位就是模,此时的进位,就应该忽略不计。

    正如我们熟悉的时钟,时钟显示共有十二个小时,时针旋转一周后回到原来的状态,实际的数学意义没有变化。在这里插入图片描述

    但是就单单论数学模型意义来讲,该计时系统的模为12,也就是说,时针顺时针转8圈和逆时针转4圈,效果是一样的。为了符合数学意义,顺时针为+8,逆时针为-4,综上所述: 得 |+8|+|-4|=12

    在二进制下,有多少位数参加运算,模就是在 1 的后面加上多少个 0。
    我们看回上面的式子:

    [ X ]补 = 模-| X |

    -128的补码为:
    1000 0000(补码) = 1 0000 0000(模) - 1000 0000(-128的绝对值)

    —————————————————————————————————
    (:如有错误敬请指出~
    本篇文章只是对概念进行简单的概括,我找到一篇十分详细的博客,有兴趣的同学可以读一下这篇关于 -128 ,+128,-0,+0,-1 的反码补码

    参考:
    https://blog.csdn.net/weixin_38357164/article/details/87912475
    https://zhidao.baidu.com/question/484367560.html
    https://www.bilibili.com/video/BV1rV411k7Xf?p=21

    展开全文
  • C/C++中各种类型int、long、double、char表示范围(最大最小值)
  • 各种类型数据表示范围 unsigned int 0~4294967295 int -2147483648~2147483647 unsigned long 0~4294967295long -2147483648~2147483647long long的最大值:9223372036854775807long long的最小值:-...
  • 详细介绍C/C++ int、long、double、char等各种内置数据类型的表示范围(最大 - 最小值) 执行结果如下: int、long、double、char等各种内置数据类型的表示范围(最大最小值)" TITLE="C/C++ int、long、...
  • 一字节表示八位,即:1byte = 8 bit; int: 4byte = 32 bit 有符号signed范围:2^31-1 ~ -2^31 即:2147483647 ~ -2147483648 无符号unsigned范围:2^32-1 ~ 0 即:4294967295 ~ 0 long: 4 byte = 32 bit 同...
  • 为什么一个字节的补码表示范围是-128~127

    万次阅读 多人点赞 2018-09-19 20:42:06
    我们要先区分一下原码、反码和补码的表示规则: 0的表示:  原码:有正零和负零之分,[+0]补=0000 0000,[-0]补=1000 0000;  反码:同样有两种表示方法,[+0]反=0000 0000 ,[-0]反=1111 1111;  补码:零...
  • 一个字节的表示范围

    千次阅读 2015-07-23 22:58:19
    byte类型的数据,其为8位,在计算机中以补码进行存,...所以byte类型的数据所表示范围为-128到127。 补充一点说明,byte,char,short类型的数据可以直接赋给int类型的数据,因为数据类型可以向精度更高的类型转。
  • 一字节表示八位,即:1byte = 8 bit; int: 4byte = 32 bit 有符号signed范围:2^31-1 ~ -2^31即:2147483647 ~ -2147483648无符号unsigned范围:2^32-1 ~ 0即:4294967295 ~ 0 long: 4 byte = 32 bit 同int型 ...
  • 放到数据类型上也是一样的,一个无符号数据类型假如占据一个字节,那么表示范围就是0~255(2的8次方减1),两个字节就是0~65535(2的16次方减1)。如果是有符号类型,则需要一位(最高位)表示符号位,一字节范围为-...
  • 1个字节表示数的范围

    千次阅读 2014-09-06 17:35:45
    在计算机中,是用补码形势表示二进制数。正数的补码是其本身;负数的补码:符号位为1,其余位为该数绝对值的原码按位取反,然后加1。所以最小数 1 0 0 0 0 0 0 0 是补码形式;它的数值绝对值应该
  • C++ int,float,double,long表示范围

    千次阅读 2014-10-08 20:47:45
    一字节表示八位,即:1byte = 8 bit; int: 4byte = 32 bit 有符号signed范围:2^31-1 ~ -2^31 即:2147483647 ~ -2147483648 无符号unsigned范围:2^32-1 ~ 0 即:4294967295 ~ 0 long: 4 byte = 32...
  • int, short, long, long long类型的范围

    千次阅读 2018-10-29 10:40:02
    关于带符号与无符号类型:整型 int、short 和 long 都默认为带符号型。要获得无符号型则必须制定该类型为unsigned,比如unsigned long。...一字节表示八位,即:1byte = 8 bit; 《C和指针》中写过:long与int:...
  • int 和long的数据范围表示相同

    千次阅读 2015-01-18 15:50:09
    int 和long的表示范围是完全相同的。之前我的错误认识:int 是32位,4byte。而long占64bit , 8byte。 转载一个类型表示范围的博客:http://blog.csdn.net/xuexiacm/article/details/8122267 [cpp] view ...
  • 一字节表示八位,即:1byte = 8 bit; int: 4byte = 32 bit 有符号signed范围:2^31-1 ~ -2^31 即:2147483647 ~ -2147483648 无符号unsigned范围:2^32-1 ~ 0 即:4294967295 ~ 0 long: 4 byte = 32 bit ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,939
精华内容 2,375
关键字:

一字节表示范围