C语言常用数据类型说明

时间:2023-12-28 12:16:50

 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)。

可以使用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 : float、double
  %u : unsigned int
  %lu : unsigned long
  %llu : unsigned long long
  %e/%E: 浮点数的科学计数法
  %g 自动选择%f或%e
  %x 无符号十六进制整数

需要注意的是在printf中%f对应float和double,在scanf中%f对应float,%lf对应double。

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

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

%+:显示正负号。

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

对于%f,默认输出的小数位数是6位:

  double d = 12.34;
  char buf[100] = { 0 };
  sprintf_s(buf, "%f", d); //buf为12.340000

  d = 12.123456789;
  memset(buf, 0, 100);
  sprintf_s(buf, "%f", d); //buf为12.123457

由于float的精度为6到7位数,所以对于float来说小数位数最多可以精确到6位,float是否等于0:if(abs(f) <= 0.000001),比较两个float是否相等的方法为:if(fabs(f1 - f2) < 0.000001)。

double精度为15到16位数,其小数位数最多可以精确到15位数,double是否等于0:if(abs(f) <= 0.000000000000001), 比较两个double是否相等的方法为:if(fabs(f1 - f2) < 0.000000000000001)。

对于浮点型推荐使用double来代替float。

后缀意义: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、运算符的结合性与优先级

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

  C语言常用数据类型说明

6、浮点型的精度

float虽然取值范围很大但其精度只有6-7位,所以对于浮点型数值我们最好是使用double(15-16位精度)来保存。C#中还有一个decimal类型,拥有比double更高的精度。代码中的浮点型默认也是double类型的,如果要指定为float则在其后面加上F,如float f = 123.45F;

由于浮点型在内存中特殊的存储方式才导致了很多浮点型数据其实是不精准的, 比如以下代码,我们查看1234.56789在内存中的数值:

float f = 1234.56789F;
    printf("%.20f\n", f);

输出为1234.56787109375000000000,可以看到从第九位开始就不精确了。

7、内存中存储方式

字符型:
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\n", 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

浮点型:
浮点型分为单精度类型float和双精度类型double,float数据占用32bit,double数据占用64bit。
浮点型数据在内存中存储分为三部分,从高位到低位分别为:
1.符号位:0代表正,1代表为负,float和double都只占1位.
2.指数位:用于存储科学计数法中的指数数据,并且采用移位存储,float占8位,double占11位.
3.尾数部分:尾数部分,float占23位,double占52位数。
可以使用科学计数法来表示数据,比如120.5用十进制的科学计数法表示为:1.205*10^2,而浮点型在计算机中使用二进制的科学计数法来保存,如86.5用二进制表示为:1010110.1用二进制的科学计数法可以表示为1.0101101*10^6。在内存中86.5的符号位为0;指数位为6+127即133的二进制1000 0101:指数位共有8位,可表达的范围为:0~255,为了能处理负指数,指数位中存储的值为实际指数值加上偏移量,单精度中这个偏移量为127,故实际可表达的指数值的范围为-127~128;尾数位为0101 1010 0000 0000 0000 000。
单精度数的尾数用23位存储,加上小数点前的1位肯定是1,所以又多出来了一位,2^(23+1) = 16777216。因为10^7 < 16777216 < 10^8,所以说单精度浮点数的有效位数是7位-8位。 双精度的尾数用52位存储,2^(52+1) = 9007199254740992,10^16 < 9007199254740992 < 10^17,所以双精度的有效位数是16位-17位。
由于浮点型的存储特点,所以很多浮点型的数值是存在误差的,如:
double d1 = 0.5; //2^-1
double d2 = 0.25; //2^-2,
double d3 = 0.03125; //2^-5
double d4 = 0.75; //2^-1 + 2^-2,
printf("%.50f\n%.50f\n%.50f\n%.50f\n", d1,d2,d3, d4);

double d5 = 0.3; //2^-2 + 2^-5 + ......
printf("%.50f\n%.50f\n", d4, d5);
输出为:

C语言常用数据类型说明

究其原因就是0.3表示成二进制的话是永远无法凑整或者即便能凑整其尾数也超出了浮点型位数的位数。

以下是浮点型数值的一些截取方法:

    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的话取最接近的偶数