zoukankan      html  css  js  c++  java
  • C语言常用数据类型说明

     1、取值范围:

       short一般占两个字节,取值范围:-32768 - 32767
       int一般占两个或四个字节,取值范围:-2147483648 - 2147483647

       unsigned int一般占四个字节,取值范围:0 - 4294967295

       long long一般占8个字节,取值范围:-9223372036854775808 - 9223372036854775807
       float一般占4个字节,取值范围:1.17549 e-038 - 3.40282 e+038
       double一般占8个字节,取值范围:2.22507 e-308 - 1.79769e+308

       对unsigned类型进行取负操作是无意义的,因为得到的数还是unsigned,比如这个代码中的n永远不会是负数:int n = -sizeof(DataType)。

       不要将unsigned类型与signed类型进行运算或者比较操作,因为默认的类型转换会发生不可预期的结果,如下所示:

      unsigned int n = 0;
      long long i = -1 + n; //-1默认转换为了unsigned int类型,导致i的结果为4294967295
      bool b = n > -1; //-1默认转换为了unsigned int类型,导致b为fasle

       可以使用std::numeric_limits<int>::max()、std::numeric_limits<double>::min()、std::numeric_limits<double>::lowest()(对于浮点型lowest比min获得的最小值还要小)来获得各类型的最小值。

       isnan()方法可以用来判断一个浮点型变量是否是一个数值,isinf()可以判断一个浮点型变量是否是一个无穷大值(正无穷大或负无穷大)。

      %d: int
      %l : long
      %lld : long long
      %f,%lf : printf中%f对应float和double,在scanf中%f对应float,%lf对应double。
      %u : unsigned int
      %lu : unsigned long
      %llu : unsigned long long
      %e/%E: 浮点数的科学计数法
      %g 自动选择%f或%e
      %x 无符号十六进制整数

        %5.2:指定字符串宽度的最小值为5,字符串宽度达不到的话使用空格补充;对于%f指定小数位数为2,对于%d指定指定输出数字的最小位数为2(达不到2位的话补0),对于%s指定输出字符的最大个数。

        %-:字符串左对齐,默认为右对齐。

        %+:显示正负号。

        %05:指定字符串宽度最小为5,字符串宽度达不到的话使用0补充。

        对于格式化输出,%f、%lf 默认输出的小数位数是6位:

        double dd = 123.45678912;
        printf("%lf", dd); //输出为123.456789
    
        double d = 1234.56;
        char buf[100] = { 0 };
        sprintf_s(buf, "%f", d); //buf为1234.560000
    
        d = 123.123456789;
        memset(buf, 0, 100);
        sprintf_s(buf, "%lf", d); //buf为123.123457

        对于浮点型推荐使用double来代替float。以下是浮点型数值的一些截取方法:

        double num1 = std::floor(13.9); // 13,地板
        double num2 = std::ceil(13.1); // 14,天花板
        double num3 = std::round(14.5); // 15,四舍五入
        double num4 = std::rint(14.5); // 14,与round不同的是如果小数部分是0.5的话取最接近的偶数

        后缀意义:F表示float,U表示unsigned int,L表示long, LL表示long long。

        前缀意义:0b为二进制表示,0为八进制表示,0x为十六进制表示。

    2、由于各数据类型所占字节数与编译器和CPU有关,所以我们永远不要想当然的认为int大小为4(16位下int大小为2)、指针大小为4(64位程序下指针大小为8)、short大小为2等来使用,稳妥的方法是使用sizeof()。
    3、一般情况下我们使用int来存储整型,因为它既可以满足4字节对齐,也可以满足我们存储日常使用的数字,但当数值可能超过十亿这种等级的时候我们应该选用long long。

    4、关于移位运算

     右移>>的话,对于左边空出来的位补上与符号位相同的值。

     对于short、char等低于int的类型会先自动转换为int后再移位。

     对于int类型的移位,移动的位数超出31位的话实际移动位数是对32进行的求余结果,如 3 >> 32 相当于 3 >> 0,3 >> 33相当于 3 >> 1。 

    5、运算符的结合性与优先级

      结合性为从右向左运算的有三种:赋值=、单目运算符、三目运算符,各运算符的优先级为:

      

    6、内存中存储方式

    字符型:
    char占一个字节,最高位用来表示正负,取值范围为-2^7到2^7-1(-128-127),故可表示2* 22^7个数值,即-128到127。对应ascii取值范围为。
    unsigned char占一个字节,由于其没有符号位,取值范围为0到2^8-1(0-255),故可表示2^8个数值,即0到255。

    整型:
    int在32位系统下占4个字节,最高位用来表示正负,故可表示2* 2^31个数值,取值范围为-2^31-2^31-1。

    最小取值问题:
    有一个问题就是char跟int的最小取值为什么是-2^7和-2^31而不是2^7-1和-2^31-1?以下答案部分转载和参考CSDN网友daiyutage的文章。
    拿char类型来说,一共8位,1位为符号位,所以剩下7位来表示数值,根据排列组合得出char可表示的数值总数为2^7 * 2,它们就是+0到127和-0到-127,那么已经看出问题来了:出现了两个0,一正一负,原码分别为0000 0000、1000 0000,我们知道,0其实既不是正数也不是负数,所以0只用原码0000 0000来表示,而0的补码为全部位数取反后再加一即1 0000 0000,忽略溢出的1比特(0是唯一计算补码过程中会出现溢出的数字),得出0的补码跟原码相同。那么就多出了一个-1的原码1000 0000,其补码也是全部位数取反后加一,得到跟原码相同的补码1000 0000。使用这个多出来的-1的原码可以用来表示-128,那么为什么它用来表示-128而不是其它值呢?理论上-128的原码应为1 1000 0000,最高位为符号位,取反加一后的补码也为1 1000 0000,可以看出-128的原码跟补码丢弃最高位后与-0的相同,而且即使截断后的-128和char 型范围的其他数(-127~127)运算也不会影响结果, 所以才可以用-0来表示-128。简而言之就是:因为有一个符号位,所以就会出现+0和-0,而用-0就用来表示-2^7即-128了。

    负数:
    在计算机中,数值是用补码来存储的,正数的补码是其原码,负数的补码是原码除符号位外各位取反后加1。

    例1:
    char a = 1; //a的补码:0000 0001
    char b = -1; //b的补码:1111 1111
    char c = a ^ b; //c的补码即为1111 1110, 而原码为1000 0010即-2
    printf("%d ", c);//所以输出为-2

    例2:

    int A = 468; //a的二进制:0000 0001 1101 0100
    char B = A; //int赋给char,舍去高位,只剩低位:1101 0100,符号位是1,所以是负数的补码,再减1取反后得1010 1100即-44
    printf("%d", B); //所以输出为-44

    浮点型:
    浮点型在内存中是以二进制的科学计数法形式存储的,比如86.5的十进制科学计数法为8.65*e1,86.5的二进制表示为1010110.1,使用二进制的科学计数法则为1.0101101e6,符号位为0,指数位为6,有效数字为1.0101101。在内存中一个float使用32位存储,一个double使用64位存储,如下图所示的float,符号位S占一位,指数位E占8位,有效数字M占23位。

    float:

    double:

    符号位为0表示正数,1表示负数。

    指数位对于float类型的话,其值范围为0-255,而指数有正有负,所以指数位上存储的值为指数值再加127,比如上面的86.5F的二进制指数为6,则指数位其值为6+127,当读取指数的时候再减去127,这样就能存取负数了。对于float来说,其指数的范围是-127~128。

    有效数字因为小数点前肯定为1,所以不用存储它,所以对于上面的86.5F的有效数字1.0101101,实际存储的值为0101 1010 0000 0000 0000 000(23位)。

    对于float类型的有效数字,其使用23位存储,再加上有效数字前面固定为1不用存储,所以float的二进制值其有效位数是24位,而如果是float的十进制值的话那么有效位数就是7-8位(2e24 = 16777216,10e7 < 16777216 < 10e8),比如对于12.34567899F,12.34567之后的数字是不精确的。所以对于float来说,1.192092896e-7(FLT_EPSILON)是能精确的最小数。

    由于浮点型使用二进制的指数形式来保存,所以很多浮点型的数值是存在误差的,如下所示的d5,即0.3表示成二进制的话是永远无法凑整或者即便能凑整其有效数字也超出了位数,所以0.3在内存中就是0.2999999... :

    double d1 = 0.5; //2^-1,二进制为0.1
    double d2 = 0.25; //2^-2, 二进制为0.01
    double d3 = 0.125; //2^-3; 二进制为0.001
    double d4 = 0.875; //2^-1 + 2^-2 + 2^-3, 二进制为0.111

    double d5 = 0.3; //2^-2 + 2^-5 + 2^-6......,二进制为0.010011...

    由于浮点型的值可能存在误差,所以我们判断两个浮点型是否相等的话直接使用==是不准确的,如果两个double之差的绝对值小于DBL_EPSILON的话就可以认为是相等的:

        float f = 0.7F; // 0.699999988
        double d = f;
        double _d = 0.7; // 0.69999999999999996
    
        if (d == _d)
        {
            int a = 0; //不会进入
        }
        if (abs(d - _d) < FLT_EPSILON)
        {
            int a = 0; //会进入
        }
    
        double d1 = 0.1; //0.10000000000000001
        double d2 = 0.1;
        double d3 = 0.1;
        double dSum= d1 + d2 + d3; //0.30000000000000004
        double ddd = 0.3; //0.29999999999999999
        if (dSum == ddd)
        {
            int a = 0; //不会进入
        }
        if (abs(ddd - 0.3) < DBL_EPSILON)
        {
            int a = 0; //会进入
        }

     虽然我们可以使用FLT_EPSILON或DBL_EPSILON来判断两个浮点数是否相等,但是浮点数的误差还会引起多个浮点数相加的结果与预期不符问题。比如下面的float类型的f,有效位数为7-8位,所以其实际值为0.000234559993,这里就有了误差,而下面的123将其相加后理论上sum实际值应该为123.00023455993,但因为小数点前面的整数123又占用了三个有效位数,所以小数位数只剩下了四位有效数字,所以sum的实际值为123.000237,与我们期许的sum的准确值123.00023456存在0.00000244的误差:

        float f = 0.00023456; 
        float sum = 123.0F + f;

     而如果相加的浮点数不包含整数,全部是小数的话也会因为浮点数特殊的存储方式或者精度问题而产生误差:

        double d = 0.3; // 0.29999999999999999
        double sum = 0.0;
        for (int i = 0; i < 10; i++)
        {
            sum += d; // 2.9999999999999996
        }
    
        if (abs(sum - 3.0) < DBL_EPSILON)
        {
            int a = 0;
        }
        else
        {
            int a = 0; //会进入这里
        }

     对于上面的问题,可以将浮点型转换为整型使用来解决这个问题,如下所示,比如我们业务上仅会使用两位小数的话,那么可以将该浮点数乘100后使用整形保存。Java的话可以使用BigDecimal类型解决这个问题:

        double d = 0.03; //0.029999999999999999
        long long l = d * 100; //3
        
        long long sum = 0.0;
        for (int i = 0; i < 10; i++)
        {
            sum += l;
        }
        
        double _d = sum / 100.0; //0.29999999999999999
        if (abs(_d - 0.3) < DBL_EPSILON)
        {
            int a = 0; //进入这里
        }
        else
        {
            int a = 0;
        }

      上面的方法适用于业务上对于小数位数使用确定的情况,比如对于股价来说其最小单位为分,而对于小数位数无限制的业务情况,会出现问题,如下所示:

    int main()
    {
        double d = 0.009;
        long long l = d * 100;
    
        long long sum = 0.0;
        for (int i = 0; i < 10; i++)
        {
            sum += l;
        }
    
        double _d = sum / 100.0;
        if (abs(_d - 0.0) < DBL_EPSILON)
        {
            int a = 0; //进入这里
        }
        else
        {
            int a = 0;
        }
    }
  • 相关阅读:
    C++中的类模板详细讲述
    IE6
    Active Driectory 操作(转来放起来,不要丢了)
    The length of the query string for this request exceeds the configured maxQueryStringLength value
    试一下用word发布一篇文章
    各种分享api
    汇编语言程序设计 检测点1.1
    Windows下配置使用MemCached
    chrome
    ASP.NET 把集合导出为Excel的一个助手类
  • 原文地址:https://www.cnblogs.com/milanleon/p/5900446.html
Copyright © 2011-2022 走看看