C语言程序设计(四)
文章目录
数据类型
C语言是有类型的语言
-
C语言的变量必须:在使用前定义,并且确定类型
-
C以后的语言的两个方向发展: ①C++/Java更强调类型,对类型的检查更严格②JavaScript、Python、PHP不看重类型,甚至不需要事先定义
越偏底层的语言越注重类型,偏应用型的语言不太在乎类型的定义
类型安全
- 支持强类型的观点认为明确的类型有助于尽早发现程序中的简单错误
- 反对强类型的观点认为过于强调类型迫使程序员面对底层、实现而非事务逻辑
- 总的来说,早期语言强调类型,面向底层的语言强调类型
- C语言需要类型,但是对类型的安全检查并不足够
C语言的类型
- 整数
- char、short、int、long、long long
- 浮点数
- float、double、long double
- 逻辑
- bool
- 指针
- 自定义类型
高亮的是C99的类型
类型有何不同
-
类型名称: int、long、double
-
输入输出时的格式化:%d、%ld、%lf
-
所表达的数的范围:char < short < int < float < double
-
char short int int* float double 1字节 2字节 4字节 4字节 4字节 8字节 -
内存中所占据的大小: 1个字节到16个字节 (1字节(byte)=8位(bit); 8位->64位二进制数) ; long double 16位
-
内存中的表达形式: 二进制数(补码)、编码
- 二进制数可以直接进行运算,而编码不能(换句话来说,int型整数可以直接进行二进制运算;但float型不行,它是以编码的形式,不能直接进行运算。)
sizeof
- 是一个运算符,给出某个类型或变量在内存中所占据的字节数
- sizeof(int)
- sizeof(i)
- 是静态运算符,它的结果在编译时刻就决定了,
- 不要在
sizeof
的括号里做运算,这些运算不会在里面进行运算 [另外sizeof(int + 0.1)
它会显示8个字节,并不是进行运算,而只是将整型转化为浮点型,然后根据浮点型进行判断字节长]
#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字节(占8bit)
- short:2字节
- int:取决于编译器(CPU),通常的意义是“1个字长”
- long:取决于编译器(CPU),通常的意义是“1个字长”
- long long:8字节
字长:参与一次整数运算的二进制数位数,就是CPU中的寄存器一次能处理的数据的大小; 在C语言中反映出来就是一个int, int能表达的大小就是寄存器的大小(int就是用来表达寄存器的)
*整数的内部表达
-
计算机内部一切都是二进制数
8 -> 00001000
*如何表示负数
- 二进制负数
- 一个字节可以表达的数:
- 00000000 - 11111111(0-255)
- 补码:-1 -> 11111111
- 补码的意义就是拿补码和原码可以加出一个溢出的"零"
数的范围
- 对于⼀一个字节(8位),可以表达的是:00000000 - 11111111
- 其中 00000000 —> 0
- 11111111 ~ 10000000 —> -1 ~ -128
- 00000001 ~ 01111111 —> 1 ~ 127
整数的范围
- char : 1字节:-128 ~ 127
- short: 2字节:-32768 ~ 32767
- int:取决于编译器(CPU),通常的意义是“1个字”
- long: 4字节
- long long: 8字节
unsigned(无符号数)
- 在整数型类型前加上unsigned使得它们成为无符号数的整数
- 内部的二进制表达式没变,变的是如何看待它们如何输出
- 11111111
- 对于char,是-1
- 对于unsigned char,是255
- 如果一个字面量常数想要表达自己是unsigned,可以在后面加U或u
- 255U
- 用I或L表示long(long)
- *unsigned的初衷并非扩展数能表达的范围,而是为了做纯二进制运算,主要是为了移位。
整数越界
- 整数是以纯二进制方式进行计算的,所以:
- 11111111 + 1 —> 100000000 —> 0
- 01111111 + 1 —> 10000000 —> -128
- 10000000 - 1 —> 01111111 —> 127
-
0减一为-1; -1加一为0; -128减一为127; 127加一为-128
-
int类型能表示的最大整数:
#include <stdio.h>
int main()
{
int a=0, b=0;
while (++a>0);
//while (++a>0){
//}//两种写法都可以
//while (a>0){
// a++;
//}//也可以这样写
printf("int数据类型最大数是:%d\n", a-1);
b++;
while ((a=a/10) != 0){
b++;
}
printf("int数据类型最大的数的位数是:%d",b);
return 0;
}
- 输出结果:
int数据类型最大数是:2147483647
int数据类型最大的数的位数是:10
- unsigned int表示的最大整数:
#include <stdio.h>
int main()
{
unsigned int a=1;
// while (a>0)
//
// a++;
while (++a!=0);//或者while(++a>0);
printf("unsigned_int max =%u\n",a-1);
return 0;
}
- 输出结果:
unsigned_int max =4294967295
整数的输入输出
- 只有两种形式:int或long long
- 格式化输出
%d:int
%u:unsigned
%ld:long long
%lu:unsigned long long
8进制和16进制
- 一个以
0
开始的数字面量是8进制 - 一个以
0x
开始的数字面量是16进制 %o
用于8进制,%x
用于16进制?- 8进制和16进制只是如何把数字表达为字符串,与内部如何表达数字无关(内部永远以二进制表示)
*8进制和16进制
- 16进制很适合表达二进制数据,因为4位二进制正好是一个16进制位
- 8进制的一位数字正好表达3位二进制
- 因为早期计算机的字长是12的倍数,而非8
选择整数类型
- 为什么整数要有那么多种?
- 为了准确表达内存,做底层程序的需要
- 没有特殊需要,就选择int
- 现在的CPU的字长普遍是32位或64位,一次内存读写就是一个int,一次计算也是一个int,选择更短的类型不会更快,甚至可能更慢
- *现代的编译器一般会设计内存对齐,所以更短的类型实际在内存中有可能也占据一个int的大小(虽然
sizeof
告诉你更小) - unsigned与否只是输出的不同,内部计算是一样的
浮点数
浮点类型
类型 | 字长 | 范围 | 有效数字 |
---|---|---|---|
float | 32 | ±(1.20x
1
0
−
38
10^{-38}
10−38~3.40x
1
0
38
)
10^{38})
1038), 0, ±inf, nan | 7 |
double | 64 | ±(2.2x
1
0
−
308
10^{-308}
10−308~1.79x
1
0
308
10^{308}
10308), 0, ±inf, nan | 15 |
- 注:
nan
:不是有效的数字;±inf
: 正负无穷大
浮点的输入输出
类型 | scanf | printf |
---|---|---|
float | %f | %f, %e |
double | %lf | %f, %e |
科学计数法
%e : 输出科学计数法
#include <stdio.h>
int main()
{
double a = 1234.5678;
printf("%e\n%f\n",a,a);
return 0;
}
输出结果:
1.234568e+003
1234.567800
- 即
±aE±n
->
± a ± n ±a^{±n} ±a±n
输出精度
- 在%和f之间加上.n可以指定输出小数点后几位,这样的输出是做4舍5入的
- printf("%.3f\n", -0.0049);
- printf("%.30f\n", -0.0049);
- printf("%.3f\n", -0.00049);
#include <stdio.h>
int main()
{
printf("%.3f\n",-0.0049);//小数点后3位
printf("%.30f\n",-0.0049);//小数点后30位
printf("%.3f\n",-0.00049);//小数点后3位 (进行四舍五入)
return 0;
}
输出结果:
-0.005
-0.004899999999999999800000000000
-0.000
超过范围的浮点数
printf
输出inf
表示超过范围的浮点数: ±∞printf
输出nan
表示不存在的浮点数
#include <stdio.h>
int main()
{
printf("%f\n",12.0/0.0);
printf("%f\n",-12.0/0.0);
printf("%f\n",0.0/0.0);
return 0;
}
输出结果:
1.#INF00
-1.#INF00
-1.#IND00
浮点运算的精度
- 带小数点的字面量是double而非float (即默认是double)
- float需要用f或F后缀来表明身份
#include <stdio.h>
int main()
{
float a, b, c;
a = 1.345f; //带f来表明是float类型,小数点后不带f则默认表示double
b = 1.123f;
c = a + b;
if(c == 2.468)
printf("相等\n");
else
printf("不相等! c = %.10f,或%f\n", c, c);
return 0;
}
输出结果:
不相等! c = 2.4679999352,或2.468000
- 注:带小数点的数进行运算是不准确的, 整数运算是准确的
选择浮点类型
- 如果没有特殊需要,只使用double
- 现代CPU能直接对double做硬件运算,性能不会比float差,在64位的机器上,数据存储的速度也不比float慢。
字符
字符类型
- char是一种整数,也是一种特殊的类型:字符(character)。这是因为:
- 用单引号表示的字符字面量:
'a', '1'
''
也是一个字符(注意是单引号;在C中双引号不被单做字符;)printf
和scanf
里用%c
来输入输出字符
- 用单引号表示的字符字面量:
#include <stdio.h>
int main()
{
char c;
char d;
c = '1';
d = 1;
if (c==d){
printf("相等\n");
}else{
printf("不相等\n");
}
printf("c=%d\n",c);
printf("d=%d\n",d);
return 0;
}
输出结果:
不相等
c = 49
d = 1
字符的输入输出
- 如何输入’1’这个字符给char c?
scanf("%c",&c);
->1; 输入1;就会得到1- scanf("%d",&i); c = i; -> 49; 输入1, 就会得到字符’1’的ASCII编码是49,它代表’1’
#include <stdio.h>
int main()
{
char c;
scanf("%c",&c);
printf("c=%d\n",c); //以整数输出
printf("c=%c\n",c);//以字符输出
return 0;
}
输出:
1
c=49
c=1
混合输入
- scanf("%d %c", &i, &c);
- scanf("%d%c", &i, &c);
有何不同?
#include <stdio.h>
int main()
{
int i;
char c;
scanf("%d %c",&i,&c);//scanf("%d%c",&i,&c);
printf("i=%d; c=%d, c=%c\n",i, c, c);
return 0;
}
有空格的输出 :
12 1
i=12; c=49, c=1
- 区别有无空格; 有空格它就会读完空格再读下一个; 没有空格,就不会读到空格,直接把空格当做下一个读入。
没有空格的输出:
12 1
i=12; c=32, c=
注: 空格的ASCII编码是32。
字符计算
- 一个字符加一个数字得到ASCII码表中那个数之后的字符
- 两个字符的减,得到它们在表中的距离
char c = 'A';
c++;
printf("%c\n",c);
int i = 'Z' - 'A';
printf("%d\n",i);
大小写转换
- 字母在ASCII表中是顺序排列的
- 大写字母和小写字母是分开排列的,并不在一起
'a' - 'A'
可以得到两段之间的距离,于是a + 'a' - 'A'
可以把一个大写字母变成小写字母,而a + ’A’ - ‘a’
可以把一个小写字母变成大写字母
逃逸字符
用来表达无法印出来的控制字符或特殊字
用来表达无法印出来的控制字符或特殊字符,它由一个反斜杠\
开头,后面跟上另一个字符,这两个字符合起来,组成了一个字符
字符 | 意义 | 字符 | 意义 |
---|---|---|---|
\b | 回退一格 | \" | 输出双引号 |
\t | 到下一个表格位 | \' | 输出单引号 |
\n | 换行 | \\ | 输出反斜杠本身 |
\r | 回车 |
- \b : 类似于
BackSpace
键
举个栗子:
#include <stdio.h>
int main()
{
printf("123\bA\n456");
return 0;
}
输出:
12A
456
- \t : 类似于
Tab
键 (制表位)
举个栗子:
#include <stdio.h>
int main()
{
printf("123\t456\n");
printf("12\t456\n");
return 0;
}
输出:
123 456
12 456
自动类型转换
-
当运算符的两边出现不一致的类型时,会自动转换成较大的类型
-
大的意思是能表达的数的范围更大
char —> short —> int —> long —> long long
int —> float —> double
从左往右表示的范围越来越大
-
-
对于
printf
,任何小于int
的类型会被转换成int
;float
会被转换成double
-
但是
scanf
不会自动转换,你输入的是啥类型, 格式化输出就是啥类型; 要输入short
,需要%hd
强制类型转换
- 要把⼀个量强制转换成另一个类型(通常是较小的类型),需要:
- 写法:
(类型)值
; 括号内是要转换的类型 - 如:
(int)10.2
、(short)32
- 只是从那个变量计算出了一个新的类型的值,它并不改变那个变量,无论是值还是类型都不改变
- 注意这时候的安全性,小的变量不总能表达大的量
(short)32768
#include <stdio.h>
int main()
{
int i = 32768;
short s = (short)i;
char c = (char)i;
printf("%d\n", i);
printf("%d\n", s);
printf("%d\n", c);
return 0;
}
输出:
32768
-32768
0
short类型最大能表示32767;因此32768会表示成-32768; 6
而32768=10000000000(2)二进制而char是
8bit
的,溢出部分就不计,即为0
-
强制类型转换的优先级高于四则运算
-
如:
double a = 1.0; double b = 2.0; int i = (int)a/b;//它会先转换再运算 int i = (int)(a/b);//先运算再转换
逻辑类型
bool
#include <stdbool.h>
- 之后就可以使用
bool
和true
、false
#include <stdio.h>
#include <stdbool.h>
int main()
{
bool b = 6>5;
bool t = true;
t = 2;
printf("%d\n",b);
return 0;
}
输出结果:
1
bool的运算
bool
实际上还是以int
的手段实现的,所以可以当作int
来计算- 也只能当作
int
来输入输出
逻辑运算
- 逻辑运算是对逻辑量进行的运算,结果只有0或1
- 逻辑量是关系运算或逻辑运算的结果
运算符 | 描述 | 示例 | 结果 |
---|---|---|---|
! | 逻辑非 | !a | 如果a是true结果就是false,如果a是false结果就是true |
&& | 逻辑与 | a&&b | 如果a和b都是true,结果就是true;否则就是false |
|| | 逻辑或 | a||b | 如果a和b有一个是true,结果为true;两个都是false,结果为false |
-
如果要表达数学中的区间, 如: x∈(4,6),应该如何写C的表达式?
- 像4<x<6这样的式子,不是C能正确计算的式子,因为4<x的结果是一个逻辑值(0或1)
- C语言应该这样写:
x>4&&x<6
即x>4且x<6 - x∈[4,6];
x>=4&&x<=6
-
如何判断一个字符c是否是大写字母?
c >= 'A' && c <= 'Z'
-
index < 0 || index > 99
- 即index<0或index>99
-
!age <20
- age不小于20; 即age>=20
优先级
-
逻辑运算符低于比较运算符;
-
单目运算符高于双目运算符;
-
优先级:
!
>&&
>||
- !done && (count > MAX) ; 先算!done和(count>MAX), 这个括号可以没有,也是先算count>MAX。
短路
- 逻辑运算是自左向右进行的, 如果左边的结果已经能够决定结果了, 就不会做右边的计算。
- a == 6 && b == 1
- a == 6 && b += 1
- 对于 && 来说,左边是 False 时就不做右边了
- 对于 ||,左边是True时就不做右边了
#include <stdio.h>
int main()
{
int a = -1;
if(a>0&&a++>1){
printf("%d\n", a);
}
printf("%d\n", a);
return 0;
}
输出结果:
-1
它只做了左边a>0,条件不满足就直接不做右边, 这种现象好比电路中的短路.
- 不要把赋值,包括复合赋值组合进表达式! 为了避免出现短路的错误.
条件运算符
count = (count > 20) ? count -10 : count+10;
- 条件、条件满足时的值和条件不满足时的值
count = (count > 20) ? count -10 : count+10;//条件运算符'?'的优先级高于赋值运算符'='
//相当于
if ( count >20 ){
count = count-10;
}else{
count = count+10;
}
优先级
- 条件运算符的优先级高于赋值运算符,但是低于其他运算符
m<n ? x : a+5; //如果m<n,则结果为x,否则结果为a+5
a++ >= 1 && b-- > 2 ? a : b;//如果a++>=1且b-->2,则结果为a,否则结果为b
x=3 * a > 5 ? 5 : 20;//如果x=3*a>5;则结果为5否则为20
嵌套条件表达式
-
count = (count > 20) ? (cout < 50) ? count -10 : count - 5 : (count < 10 ) ? count + 10 :count + 5;
-
条件运算符是自左向右结合的
- w< x?x+w:x < y?x:y
-
不建议使用嵌套表达式,理解起来比较困难
逗号运算
逗号运算符
-
逗号用来连接两个表达式,并以其右边的表达式的值作为它的结果。逗号的优先级是所有的运算符中最低的,所以它两边的表达式会先计算;逗号的组合关系是自左向右,所以左边的表达式会先计算,而右边的表达式的值就留下来作为逗号运算的结果。
-
i = (3+4, 5+6);
,最终i = 11,以最后一个为结果. -
逗号运算符主要用于for循环中
- for (i=0, j=10; i<j; i++,j–)…