6.1.1 C语言其他的基本数据类型
sizeof:属于(静态)运算符,可以给出某个类型或者变量在内存里占据的字节数。其结果在编译的时候就已经确定,与程序执行阶段无关,所以不能用来计算。但是可以放表达式,比如
int a;
sizeof(a+1.0)结果为->8,因为C自动转换了这个表达式,其类型为double,所以字节长度是8,并非真正的计算
比如:sizeof(int) sizeof(i)
6.1.2 整数类型
int字节长度实际上是寄存器存放的字长,比如32位是32bit=4字节,64bit
6.1.3 整数的内部表达
那么整数是怎么在计算机内部进行表达的呢?如果是负数又该怎么表达呢?
当然,不管是整数、浮点数还是什么乱七八糟数,计算机只能看懂二进制数!所以毫无疑义是二进制数,类型的意义,在于我们以什么方式去看待它
那么再回到负数的问题,在C中我们将减法异化为带负号的加法,用独立的方式来表示一个负数,比如15-8 -> 15+(-8) -> 7; 15 - (-8) -> 15+8 -> 23 ; 15*(-8) -> -(15*8);
一个字节为8bit,可以表示二进制数从00000000到11111111,也就是十进制的0~255。
表示负数的方案:
第一种,需要额外一个变量来控制整个加减,而第二种每次加减都需要与中间变量进行比较,因此都不是很简便。实际选用了第三种方式。
那么我们就思考如何表示-1,让1+(-1)=0?
00000001+?=00000000。即?为11111111。和实际上为100000000,但是如果限制了8bit,多出来的最高位就会被舍弃。也就得到了00000000.
也可以用另外一种方式来思考,0-1就得到了-1,那么0向高位借1,即为(1)00000000 - 00000001,也可以得到11111111。
因此可以得知,对于纯二进制数,11111111为255,但对于补码来说它是-1.
同理对于任意数-a,它的补码就是0-a,实际上是2的n次方-a,n为类型位数。也可以得出,补码的意义就是拿补码和原码相加会得到一个溢出的0.
6.1.4 整数的范围
对于一个字节(8位)可以表示的数为:00000000~11111111
其中00000000表示十进制0,11111111~10000000表示(-1)~(-128),00000001~01111111表示1~127
回到数据类型,只有char类型下的11111111表示-1,因为只有一个字节,char类型数据范围为-128~127.如果想要单纯的表示0~255,不要补码。需要在数据类型前面加上unsigned。比如unsigned char a=255;输出a会得到255.如果只是char a=255,则输出a会得到-1.
如果是一个字面量变量想要表示自己是unsigned可以在后面加上u或U,表示自己是long可以加l或L。但unsigned的意义不止是扩大表达范围,而是为了做纯二进制数的计算,主要是为了移位
整数也是用纯二进制的方式计算的,所以会出现一些特殊的情况:整数越界
在一个字节的计算中,-1+1=0,127+1=-128.0-1=-1,-128-1=127
6.1.5 整数的格式化输出
整数的输入输出实际上只有两种:int类型和long long类型(unsigned是例外且这两种都可以加,因为这两种实际上没有区别,只是我们看待他们的方式不同),格式化符为:
- int:%d 有unsigned就加u
- long long:%ld 同上
#include<stdio.h>
int main()
{
char c=-1;
int i=-1;
printf("c=%u,i=%u\n",c,i);
return 0;
}
其运行结果为
这一串数字即为unsigned int可以表示的位数,按理来说c的结果不应该是这个,但是printf这个函数在传递小于int的变量类型时会将其转换为int,所以二者结果相等。
八进制和十六进制
以0开头的数字字面量为八进制,以0x开头的数字字面量为十六进制。
八进制和十六进制很适合表示二进制数据。因为四位二进制为一个十六进制位。三位二进制为一个八进制位。
#include<stdio.h>
int main()
{
char c=012;
int i=0x12;
printf("c=%d,i=%d\n",c,i);
return 0;
}
此时输出结果为
这个程序的内核其实是,你将一串数字定义为八进制或者十六进制,然后让计算机转换成十进制并输出。当你将格式化输出符更改之后,这些数字的意义也就随之改变了。比如
c=%o,i=%x; %x输出小写,%X输出大写字母。
此时输出的结果就是 ,就是没有表示其进制的前缀。可以自行加上。
6.1.6 选择整数类型
为什么有那么多的整数类型!原因是早期的语言需要准确表示计算机内部的结构、接口等。 那今天我们学习时候如何选择整数的类型呢?
没什么特殊需要,就用int
6.1.7 浮点类型
数轴靠近0的部分是无效的,也就是不能无限趋近于0.inf即infinite,正负无穷,nan:无效数。
右边的有效数字意义:第七位为准确,第八位及以上就不准确了。
%e:科学计数法,也可写为%E。
输出结果为:
会出现第二种值的原因是:在数学概念中两个数之间可以取的数是无限的,但在计算机中,数字都是离散的,所以不能取到无限个数,而给出的这个数就是30位小数情况下最接近0.49的一个数。
6.1.8 浮点数的范围与精度
比如以下程序:
结果是:
如果是整数运算:
会怎么样?答案是:不能通过编译。因为整数无法表示无穷,浮点数在设计时加入了上面三种特殊的结果。但是,浮点数的运算没有精度。有下列代码:
此处为了表明自己是float,需要在字面量后面加上f或者F。
若c结果是2.468,则输出相等。那实际结果如何?
如果将c四舍五入到小数点后4位,其值确实是2.468,但实际上在计算机内部其值并不是准确的2.468.所以二者不相等。
因此我们得出:两个浮点数不一定相等,f1==f2可能会失败,那么如何避免这个问题?
只要f1和f2的绝对值fabs(f1-f2)< 精度(float为1e-8,double为1e-16)
在计算过程中,如何避免浮点数带来的误差?如果是整数运算,可以用BCD码
因此浮点数并不是纯二进制,而是编码类型
那么如何选择浮点数?
6.1.9 字符类型
char:character的简写,它是一种整数,也是特殊类型:字符型,原因是:
在上述代码中,分别赋值给两个char类型变量不同的1,一个是整数,一个是字符,运行结果为:
那么我们将c和d作为整数来输出,查看它们的值
结果为:
将d换为其他字符,得到的结果不同,说明每个字符对应着某一个整数。
那么假如定义了一个字符char c,怎么输入数字‘1’给它?
很显然,有两种方式,但是要取决于接受的格式,如果是接收整数的语句,就需要输入49,此时c的值才能为1. 程序如图
输入1
结果为:作为整数,c=49,作为字符时候c为‘1’。这也印证了我们之前探讨过的,数据的类型全看你如何看待它。
所以字符‘1’的ASCII编码是49,当整数为49时代表字符‘1’。验证一下
证明是相等的。
混合输入整数和字符:
第一个语句里两个格式符之间有空格
首先是第一个语句的试验,输入12 1
输出为
如果输入12a(中间没空格),或者12 1(中间很多空格)
都可以得到正确结果。
如果用第二条输入语句:
则此处的32实际上为输入的空格。
所以第一个语句意思是:读完整数到没有空格才终止,而第二个语句则是只读到整数结束为止
字符计算:
因为字符也属于一种整数,那么理应可以做些运算。
则会输出B
用Z减去A?会得到25。也就是说字符加上一个数得到的结果还是字符。两个字符相减的结果是数字(这个数字代表了这两个字符的距离)。 因此有以下规律:
6.1.10 逃逸字符
是用来表示无法打印的控制字符和特殊字符,由一个反斜杠“\”开头,后面加上要打印的字符,两个字符合起来视作一个字符。
比如常见的在字符串中想要打印双引号的语句:
printf("\"");
不同的终端在运行代码时候呈现的结果可能会不同。
\b的作用:相当于backspace,回退一个字符,如果紧跟输入新字符的话就会将最后的字符代替。如果没有的话就默认为无操作。(在devc终端下是这样)
\t的作用:相当于tab制表符,不代表相隔的数量,只有固定位置的区别
abc\t位置1
ab\t位置2
abc <tab> 位置1
ab <tab> 位置2
\n的作用:在一般的终端shell中会将\n回车翻译为同时执行回车和换行,但是实际上回车和换行是模拟打字机的两个独立的动作。
6.1.11 类型转换
自动类型转换:当运算符两侧的类型不一致的时候,会自动转换成较大(能表达数的范围大)的类型 。char<short<int<long<long long 。int<float<double。
但是在printf中,任何小于int的类型会被自动转换为int,float会被自动转换为double。会出现我们上文提到过的情况。
scanf是不会自动转换的,不过要输入short类型的数字需要%hd,longlong类型需要%ld。要给一个char类型输入整数,不行。只能先给一个整数类型输入整数,再把这个变量赋值给char类型变量。
强制的类型转换(通常是大转小):(类型名)值
也就是值放外面。比如:
- (int)10.2
- (short)32
但是此时要注意数据安全性,因为小的量不一定总能表示大的量,最简单的,double转float,因为二者精度不同,转换之后就不一定相同了。
再比如(short)32768。转换结果为: -32768
因为short类型能表示的最大整数就是32767,这个语句如果转换之后就会发生之前说过的“整数越界”。
如果是(char)32768。转换结果为:0
因为32768转为二进制就是:1后面跟15个0。依然是整数越界,表示为0.
- 强制类型转换只是将变量计算为新类型的值,并不改变原有的变量类型和值。
因此转换后,输出原变量,仍然是原来的结果。
- 强制类型转换的优先级要比四则运算高,因此这种情况会优先将a转换,但是不会将除号后的b也转换
应写为: