第5周:数据类型
C
5.1.1 数据类型:数据类型
C是有类型的语言
- C语言的变量,必须:在使用前定义,并且确定类型
- C以后的语言向两个方向发展:
- C++/Java更强调类型,对类型的检查更严格
- JavaScript、Python、PHP不看重类型,甚至不需要事先定义
C语言的类型
- 整数
- char、short、int、long、long long
- 浮点数
- 逻辑
- 指针
- 自定义类型
类型有何不同
- 类型名称:int、long、double
- 输入输出时的格式化:%d、%ld、%lf
- 所表达的数的范围:char
- 内存中所占据的大小:1字节到16个字节
- 内存中的表达形式:二进制数(补码)、编码
sizeof
- 是一个运算符,给出某个类型或变量在内存中所占据的字节数
#include <stdio.h>
int main()
{
printf("sizeof(int)=%ld\n", sizeof(int));
printf("sizeof(double)=%ld\n", sizeof(double));
printf("sizeof(long double)=%ld\n", sizeof(long double));
return 0;
}
sizeof(int)=4
sizeof(double)=8
sizeof(long double)=16
- 是一个静态运算符,它的结果在编译时刻就决定了
- 不要在sizeof的括号里做运算,这些运算是不会做的
#include <stdio.h>
int main()
{
int a;
a = 6;
printf("sizeof(a)=%ld\n", sizeof(a++));
printf("sizeof(a+1.0)=%ld\n", sizeof(a+1.0));
printf("a=%d\n", a);
return 0;
}
sizeof(a)=4
sizeof(a+1.0)=8
a=6
5.1.2 数据类型:整数类型
整数
#include <stdio.h>
int main()
{
printf("sizeof(char)=%ld\n", sizeof(char));
printf("sizeof(short)=%ld\n", sizeof(short));
printf("sizeof(int)=%ld\n", sizeof(int));
printf("sizeof(long)=%ld\n", sizeof(long));
printf("sizeof(long long)=%ld\n", sizeof(long long));
return 0;
}
sizeof(char)=1
sizeof(short)=2
sizeof(int)=4
sizeof(long)=4
sizeof(long long)=8
- char:1字节(8比特):-128 ~ 127
- short:2字节:-32768 ~ 32767
- int:取决于编译器(CPU),通常的意义是“1个字”(int是用来表达寄存器的)
- long:取决于编译器(CPU),通常的意义是“1个字”
- long long:8字节
5.1.3 数据类型:整数的内部表达
整数的内部表达
- 计算机内部的一切都是二进制
- 18 --> 00010010
- 0 --> 00000000
- -18 -->?
二进制负数
- 1个字节可以表达的数:
- 00000000 -- 11111111(0 - 255)
- 三种方案:
- 仿照十进制,有一个特殊的标志表示负数
- 取中间的数为0,如10000000表示0,比它小的是负数,比它大的是正数
- 补码
补码
- 考虑 -1,我们希望 -1 + 1 --> 0。如何能做到?
- 0 --> 00000000
- 1 --> 00000001
- 11111111 + 00000001-->100000000
- 因为 0 - 1 --> -1。所以,-1 =
- (1)00000000 - 00000001 --> 11111111
- 11111111被当做纯二进制看待时,是255,被当做补码看待时是 -1
- 同理,对于-a,其补码就是 0 - a,实际上是2^n - a,n是这种类型的位数
- 补码的意义就是拿补码和原码可以加出一个溢出的“零”
5.1.4 数据类型:整数的范围
数的范围
- 对于一个字节(8位),可以表达的是:
- 其中
- 00000000 --> 0
- 11111111 ~ 10000000 --> -1 ~ -128
- 00000001 ~ 01111111 --> 1 ~ 127
#include <stdio.h>
int main()
{
char c = 255;
int i = 255;
printf("c=%d,i=%d\n", c, i);
return 0;
}
c=-1,i=255
unsigned
- 如果一个字面量常数想要表达自己是unsigned,可以在后面加u或U、
- 255U
- 用l或L表示long(long)
- *unsigned的初衷并非扩展数能表达的范围,而是为了做纯二进制运算,主要是为了移位
整数越界
- 整数是以纯二进制方式进行计算的,所以:
- 11111111 + 1 --> 100000000 --> 0
- 01111111 + 1 --> 10000000 --> -128
- 10000000 - 1 --> 01111111 --> 127
5.1.5 数据类型:整数的格式化
整数的输入输出
- 只有两种形式:int或long long
- %d: int
- %u: unsigned
- %ld: long long
- %lu: unsigned long long
#include <stdio.h>
int main()
{
char c = -1;
int i = -1;
printf("c=%u,i=%u\n", c, i);
return 0;
}
c=4294967295,i=4294967295
8进制和16进制
- 一个以0开始的数字字面量是8进制
- 一个以0x开始的数字字面量是16进制
- %o用于8进制,%x用于16进制
- 8进制和16进制只是如何把数字表达为字符串,与内部如何表达数字无关
#include <stdio.h>
int main()
{
char c = 012;
int i = 0x12;
printf("c=%d,i=%d\n", c, i);
return 0;
}
c=10,i=18
#include <stdio.h>
int main()
{
char c = 012;
int i = 0x12;
int j = 0x1A;
printf("c=%o,i=%x,j=%X,j=%x\n", c, i, j, j);
return 0;
}
c=12,i=12,j=1A,j=1a
- 16进制很适合表达二进制数据,因为4为二进制正好是一个16进制位
- 8进制的意味数字正好表达3位为二进制
5.1.6 数据类型:选择整数类型
选择整数类型
- 为什么整数要有很多种?
- 没有特殊需要,就选择int
- 现在CPU的字长普遍为32位或64位,一次内存读写就是一个int,一次计算也是一个int,选择更短的类型不会更快,甚至可能更慢
- 现代编译器一般会设置内存对齐,所以更短的类型实际在内存中可能也占据一个int的大小(虽然sizeof告诉)你更小)
- unsigned与否只是输出的不同,内部计算是一样的
5.1.7 数据类型:浮点类型
浮点类型
类型 |
字长 |
范围 |
有效数字 |
float |
32 |
±(1.20*10^-38~3.40*10^38),0,±inf,nan |
7 |
double |
64 |
±(2.2*10^-308~1.79*10^308),0,±inf,nan |
15 |
浮点的输入输出
类型 |
scanf |
printf |
float |
%f |
%f,%e |
double |
%lf |
%f,%e |
科学计数法
- -5.67E+16
- 可选的 + 或 - 符号
- 小数点也是可选的
- 可用 e 或 E
- 符号可用是 - 或 + 也可以省略(表示+)
- 整个词不能有空格
输出精度
- 在 % 和 f 之间加上 .n 可指定输出小数点后几位,这样的输出是做四舍五入的
#include <stdio.h>
int main()
{
printf("%.3f\n",-0.0049);
printf("%.30f\n",-0.0049);
printf("%.3f\n",-0.00049);
return 0;
}
-0.005
-0.004899999999999999800000000000
-0.000
5.1.8 数据类型:浮点的范围和精度
超过范围的浮点数
- printf输出Iinf表示超过范围的浮点数:+∞
- printf输出nan表示不存在的浮点数
浮点运算的精度
#include <stdio.h>
int main()
{
float a,b,c;
a = 1.345f;
b = 1.123f;
c = a + b;
if (c==2.468)
printf("相等\n");
else
printf("不相等!c=%.10f,或%f\n",c,c);
}
不相等!c=2.4679999352,或2.468000
- 带小数点的字面量是double而非float
- float需要用f或F后缀来表明身份
- f1 == f2 可能失败
- fabs(f1-f2) < 1e-12
选择浮点类型
- 如果没有特殊需要,只使用double
- 现代CPU能直接对double做硬件运算,性能不会比float差,在64位的机器上,数据存储的速度也不比float慢
5.1.9 数据类型:字符类型**
字符类型
- char是一种整数,也是一种特殊的类型:字符。这是因为:
- 用单引号表示的字符字面量:'a','1'
- ''也是一个字符
- printf 和 scanf 里用%c来输入输出字符
字符输入输出
#include <stdio.h>
int main()
{
char c;
char d;
c = 1;
d = '1';
//判断整数和字符是否相等
if (c==d)
printf("相等\n");
else
printf("不相等\n");
//输出c,d
printf("c=%d\n",c);
printf("d=%d\n",d);
//输入%c
char e;
scanf("%c",&e);
printf("e=%d\n",e);
printf("e='%c'\n",e);
return 0;
}
不相等
c=1
d=49
e=49
e='1'
字符计算
- 一个字符加一个数字得到ASCII码表中那个数之后的字符
- 两个字符相减,得到它们在表中的距离
大小写转换
- 字母在ASCII表中是顺序排列的
- 大写字母和小写字母是分开排列的,并不在一起
- ‘a’-‘A’可以得到两段之间的距离,于是
- a+‘a’-‘A’可以把一个大写字母变成小写字母,而
- a+‘A’-‘a’可以把一个小写字母变成大写字母
5.1.10 数据类型:逃逸字符
逃逸字符
- 用来表达无法印出来的控制字符或特殊字符,它由一个反斜杠“\”开头,后面跟上另一个字符,这两个字符合起来,组成了另一个字符
字符 |
意义 |
字符 |
意义 |
\b |
回退一格 |
\" |
双引号 |
\t |
到下一个表格位 |
\' |
单引号 |
\n |
换行 |
\ \ |
反斜杠本身 |
\r |
回车 |
|
|
制表位
- 每行的固定位置
- 一个\t使得输出从下一个制表位开始
- 用\t才能使得上下两行对齐
#include <stdio.h>
int main()
{
printf("123\b\n456\n");
printf("123\bA\n456\n");
printf("123\t456\n");
printf("12\t456\n");
return 0;
}
123
456
12A
456
123 456
12 456
5.1.11 数据类型:类型转换
自动类型
- 当运算符的两边出现不一致的类型时,会自动转换成较大的类型
- 大的意思是能表达的数的范围更大
- char-->short-->int-->long-->long long
- int-->float-->double
- 对于printf,任何小于int的类型会被转换成int;float会被转换成double
- 但是scanf不会,要输入short,需要%hd
强制类型转换
- 要把一个量强制转换成另一个类型(通常是较小的类型),需要:
- 比如:
- 注意这时候的安全性,小的变量不总能表达大的量
- 只是从那个变量计算出了一个新的类型的值,它并不改变那个变量,无论是值还是类型都不改变
5.2.1 其他运算:逻辑类型
bool
#include <stdbool.h>
5.2.2 其他运算:逻辑运算
- 逻辑运算是对逻辑量进行的运算,结果只有0或1
- 逻辑量是关系运算或逻辑运算的结果
运算符 |
描述 |
示例 |
结果 |
! |
逻辑非 |
!a |
如果a是true结果就是false,如果a是false结果就是true |
&& |
逻辑与 |
a&&b |
如果a和b都是true,结果就是true;否则就是false |
ll |
逻辑或 |
a ll b |
如果a和b有一个是true,结果为true,两个都是false,结果为false |
短路
- 逻辑运算是自左向右进行的,如果左边的结果已经能够决定结果了,就不会做右边的计算
- 对于&&,左边的false时就不做右边了
- 对于||,左边是true时就不做右边了
- 不要把赋值,包括复合赋值组合仅表达式
5.2.3 其他运算:条件运算与逗号运算
条件运算符
- 条件运算符的优先级高于赋值运算符,但是低于其他运算符
逗号运算
- 逗号运算用来连接两个表达式,并以其右边的表达式的值作为它的结果。逗号的优先级是所有的运算符中最低的,所以它两边的表达式会先计算;逗号的组合关系是自左向右,所以左边的表达式会先计算,而右边的表达式的值就留下来作为逗号运算的结果。
- 在for中使用