一、C语言中的数据类型
短整型(short):2个字节
长整型(long):4个字节
单精度型(float):4个字节
双精度型(double):8个字节
-
基本类型
· 数值类型
· 整型
· 短整型:short;
· 整型:int;
· 长整型:long;
· 长长整型:long long;
· 浮点型
· 单精度:float
· 双精度:doouble
· 字符类型:char
-
自定义/构造类型【后续学习】
· 结构体:struct
· 共用体:union
· 枚举:enum
-
指针类型【后续学习】
-
空类型:void
二、常量与变量
在C语言中的表现形式是常量与变量。
1、常量
常量:在程序执行过程当中其值不能改变的量称之为常量。
1.数值常量
整数常量
- 二进制常量:以0b开头,由0,1两个数字构成,举例: 0b1111、0b0110、0b01110101;
- 八进制常量:以0开头,由0~7八个数字构成,举例: 012345、07654 -075;
- 十进制常量:默认,由0~9共十个数字构成,举例: 1234、9876、-87654;
- 十六进制常量:以0x开头,由0~9,A-F共16个字符构成,一般用于嵌入式开发,举例: 0xFFFFFF、0xFFFF00
小数常量
- 单精度:常常给单精度的数值后加F作为标记,举例: 12.24F、-45.4444F;
- 双精度:默认就是双精度,举例:12.24、-45.4444;
2.字符常量
用单引号 ' ' 引起来的单个字符
举例: 'a' '1'
注意: 'a' - 字符, '马' - 错误
转义符:
' \n ' :换行符
' \t ' :制表符
' \\ ' :反斜线, \
' \0 ' :0字符对应的ASCII码值是0
3.符号常量
使用 #define 定义的宏
举例:
定义
-#define PI 3.1415926
定义 π,使用- 2 * PI
案例:
4.字符串常量
使用 "..." 引起来的字符序列,称之为字符串常量
注意 :C语言常量是支持字符串的,C语言变量不支持字符串(需要字符数组或者字符指针进行模拟)
系统自动为字符串常量加上结尾符 “ \0 ” ,使所占内存单元多一个,但字符串常量的长度不变,可用 sizeof( )运算符来测量。
举例 : printf("Hello World!")、printf("")、printf(1)-数值常量、printf("1")-字符串常量
案例 :
5.空常量
用于给指针变量作为默认值初始化:int *p = NULL;
注意 : * 前面是常量或者变量,此时 * 是数学运算符乘号, * 前面是数据类型,此时 * 表示指针;
补充:
长整型一般在数值后加 L 或者 l ;长长整型一般在数值后加 LL 或者 ll
long a = 90L;
long long b = 99LL;
练习题
2、变量
说明:
-
表面:在程序执行过程中取值可以改变的数据称之为变量;
-
实质:变量其实代表了一块内存区域/单元/空间。变量名可视为该区域的标识。
-
整个变量分为三部分:
1.变量名:这个只是变量的一个标识,我们借助变量名来存储数据;
2.变量空间/内存单元:这个就是内存中分配的一块用来存储数据的空间/区域;
3.数据/变量值:这个就是存放在变量空间中的数据;
注意:我们把数据存放在变量名对应空间的过程称之为变量的赋值操作。
-
变量是内存中一块用来存储数据的空间;
-
变量由变量名+变量值构成。通过变量名可以存放和获取变量值。
变量定义格式
[标识符] 变量数据类型 变量名列表;
变量名的命名要符合下列两个规则:
- 变量名可以以数字、字母(区分大小写)、下划线_、¥组成,后面可以跟若干个字母、数字或下划线,不能以数字开头。如:max_width;
- 不建议使用中文,也不建议使用拼音;
- 变量名不能与C语言中已经存在的标识符重名,也就是关键字和保留字不能作为变量名(例如int,signed,if,break...)。
举例:
// 方式①:声明变量并赋值 (声明一块变量存储单元,并向这个空间存储一个初始值)
//注意:赋值操作顺序为 从右至左
int num1;
num1 = 21; //将21赋值给变量num1,也就是将21赋值给变量num1对应的内存单元
printf("%d",num1);//将num1对应的数据输出到控制台,需要指明输出数据的格式:int使用占位符%d表示
// 方式②:声明并赋初值(声明一块变量,暂时为空)
int num2 = 21;//这句代码执行:①向内存申请了一块存储单元(分配内存);②将21赋给num2对应的存储单元
printf("%d",num2);
//方式②:覆盖初始值
int num3 = 21;
num3 = 22;
printf("%d",num3);
//方式③:变量列表。逗号在C语言中可作为分隔符和运算符使用
int x = 12,y = 13;
说明:
1.变量定义是,利用同一个类型标识符可同时定义多个变量,各个变量之间使用 ","(英文逗号)分割;
2.定义变量后,如果未对变量进行初值的赋予,则变量所代表的内存空间中的数据是随机安排的;
3.可以在变量定义的同时给变量赋值,这个操作成为变量的初始化,C语言允许对变量进行初始化;
4.初始化变量是,尽量做到类型相同,举例:long l = 567L;
5.整形数据在内存中的存放方式:
按照补码方式存放:
- 正整数的补码就是将该数据转换为为二进制格式;
- 负整数的补码就是将该数据的绝对值转换为二进制格式,按位取反,并+1。
6.浮点型数据在内存中的存放方式:
按照指数方式存放:
类型 | 符号位 | 指数位 | 尾数位/部分(小数) | 指数偏移量 |
---|---|---|---|---|
float(4个字节) | 1位[31] | 8位[30-23] | 23位[22-00] | 127 |
double(8个字节) | 1位[63] | 11位[62-52] | 52位[51-00] | 1023 |
举例:
需求:将 27.5 以 float 类型存放;
解析:
- 27.5的二进制为11011.1,指数表示法:1.10111*2⁴
-
指数:4,加上127,就是131,二进制1000 0011
-
尾数(小数点后的数)10111,补够23位 1011 1000 0000 0000 0000 000
-
用二进制表示就是 (符号数位1位)0 (指数位8位)1000 0011 (尾数位23位)1011 1000 0000 0000 0000 000
-
所以,单精度浮点型数据27.5在内存中的存储方式如下 0 1000 0011 1011 1000 0000 0000 0000 0000
注意:浮点型数据在内存中存放的是一个近似值。
7.字符数据在内存中的存放方式:以ASCII码存放。
字符的存在分为两种形式,ASCII码(编号,从0开始的正整数)和ASCII码值(数据,包括英文字母大小写、数字、特殊符号)。
注意:ASCII码是可以和整数进行数学运算的。
举例:
//写法1
char c1 = 'A';
//写法2
char c2 = 60;
字符型数据和整型数据之间可以通用。即一个字符数据既可以字符形式输出,也可以整数形式输出。
1、以字符形式输出时,系统先将内存单元中的ASCII码转换成相应字符,然后输出;
2、以整数形式输出时,直接将ASCII码作为整数输出;
所以可以对字符数据进行算术运算,这时相当于对其ASCII码进行算术运算。
%c 输出字符形式; %d 输出整数形式
注意:字符数据只占一个字节,只能存放0~255范围内的整数。
-
每一个英文小写字母都比它相应的大写字母的ASCII码大32
- char类型的ASCII码值(就是整数)与int型进行运算;
- 转换成整数型和字符型
- 超出存放范围
3、数据类型之间的转换
规则:不同的数据类型参与运算,需要转换为同一类型后运算
数值比较由小到大简单排序:
short < char < int < long < float < double
-
隐式类型转换
说明:编译系统自动完成,一般是低优先级类型向高优先级类型转换,这种被称为自动类型转换(由小到大)。
语法:
大数据类型 变量 = 小数据类型变量;
举例:
//案例1
char c = 'A';//65
int num = c;
printf("%d",num);//65
//案例2
char c2 = 'A';//65
int num2 = c2 +22;
printf("%d",num2);//87
-
强制类型转换
说明:程序员自己实现,一般是高优先级类型向低优先级类型转换,这种被称作强制类型转换(由大到小)。
语法:
小数据类型 变量名 = (小数据类型)大数据类型变量
举例:
//案例1
int num1 = 65;
char c1 = (char)num1;
printf("%c",c1);// A
//案例2
int num2 = 65;
char c2 = (char)(num2 + 32);// char不是函数
printf("%c",c2);// a
注意:强制类型转换过程中,可能会出现精度缺失的问题。如果大类型数据范围 <= 小类型数据范围,此时数据不会丢失,正常转换;如果大类型数据范围 > 小类型数据范围,此时丢失超出部分的数据,精度会缺失!
原则:类型转换并不会改变原变量本身的类型。
变量名、变量值、变量所占存储单元之间的关系
注意:在C语言中,要求对所用到的变量使用前必须先强制定义,即:先定义,后使用。
三、运算符
1、算术运算符
单目运算符
说明:++ -- +(正) -(负) *(解引用)
++a 与 a++ 的区别:
- int a = 1,int x = ++a; ++在前,先自加1,后运算(先计算,再赋值);
- int a = 1,int x = a++; ++在后,先运算,后自加1(先赋值,再计算);
举例:
总结:
不管是 ++ 在前,还是 ++ 在后,计算数自身都会+1。区别在于运算的结果不一样。
练习:
int i = 5; 如果 int ret1 = i+++i++;//求ret1和i的值 如果 int ret2 = i+++(++i);//求ret2和i的值
--a 和 a-- 的区别
- int a = 1,int x = --a; -- 在前,先自减1,后运算(先计算,再赋值);
- int a = 1,int x = a--; -- 在后,先运算,后自减1(先赋值,再计算)。
双目运算符
说明:+ - * / %
举例:
#include <stdio.h>
int main()
{
printf(3/2);// 结果是1 ①为什么结果是1而不是1.5?因为是整型的运算,会直接舍弃掉小数部分
printf(3*1.0/2);// 结果是1.5 ②为什么结果是1.5而不是1?3*1.0=3.0(1.0是double类型,将结果从整形转换为double类型)
printf(10%3);// 结果是1 ③求模运算
printf(10.0%3);// 结果是error ④求模运算是针对整数进行的
int a = 10;
printf(a*1.0);
return 0;
}
注意:
- 两个整数相除的结果是整数,小数部分被舍弃。要想其结果是实数,可在分子*1.0(隐式类型转换);
- 求模运算(取余运算)左右两边的操作数都必须是整型。如果是类似于3.0这样的数,是错误的。
关系运算符
说明: > < >= <= \
举例:
什么是表达式(变量 + 运算符 组成表达式)?
表达式就是表达某种意思的式子。在C语言中,表达式指的是 运算符 连接 操作数 (变量|常量
...)的式子。
注意:
由关系运算符构成的表达式称为关系表达式,关系表达式的值为boolean(布尔值)
- 非0:关系成立,为真;
- 0:关系不成立,为假。
逻辑运算符
说明:&&(与) ||(或) !(非)
- &&:逻辑与(且),符号两边的操作数都为真,结果才为真,举例:((5>4)&&(5-4)>1),结果为假;
- ||:逻辑或(或),符号两边的操作数只要有一个为真,结果就为真,举例:((5>4)||(5-4)>1),结果为真;
- !:逻辑反(取反),取反,非0为真,举例: !(!(5>4)),结果为真;
惰性运算:
所谓的惰性运算,就是减少运算次数。
- 短路与:&&两边的操作数,只要左边不成立0,直接返回假,不再检验右边;
- 短路或:||两边的操作数,只要左边成立1,直接返回真,不再检验右边。
注意:
算术运算符,运算结果是数值类型;
关系运算符,运算结果是boolean类型(其实就是int的 0-假 ,非0-真);
逻辑运算符,运算结果是boolean类型(其实就是int的 0-假 ,非0-真)。
2、位运算符
说明:按位(bit)来进行运算操作的运算符。
语法:~ & | … ^ << >>
~ :按位取反
说明:单目运算符,数据的每一个bit位取反,也就是二进制数位上的1变0,0变1。
举例:
unsigned char ret = ~0x05;//对 十六进制 的 5 转化为二进制 进行 按位取反
// 0000 0101 --> 1111 1010
printf("%d\n",~5);//对 十进制 的 5 转化为二进制 进行 按位取反
// 0000 0101 --> 1111 1010
& :按位与
语法:a & b
说明:首先将参与计算的操作数转换为二进制,然后按照每一位对齐,处理结果如下:
- 1 & 1:1 1 & 0:0 0 & 1:0 0 & 0:0
总结:如果我们前后两个操作数对其位置上的二进制数字都为1,其结果是1,否则结果均为0;
| :按位或
语法:a | b
说明:首先将参与计算的操作数转换为二进制,然后按照每一位对齐,处理结果如下:
- 1 | 1:1 1 | 0:1 0 | 1:1 0 | 0:0
总结:如果我们前后两个操作数对其位置上的二进制数字只要有1,其结果是1,否则结果为0;
^ :按位异或
语法:a ^ b
说明:首先将参与计算的操作数转换为二进制,然后按照每一位对齐,处理结果如下:
- 1 ^ 1:0 0 ^ 0:0 1 ^ 0:1 0 ^ 1:1
总结:如果我们前后两个操作数对其位置上的二进制数字不同,其结果为1,否则结果为0;
<< :左移,按bit位往左偏移(数值会变大)
1.无符号左移:
语法:操作数 << 移动位数(bit位)
unsigned int a = 3 << 3;//将 3 转化为 二进制 后 向左移动 3位
printf("%d\n",a);// 24
2.有符号左移:
语法:操作数 << 移动位数(bit位)
unsigned int a = -3 << 3;//将 3 转化为 二进制 后 向左移动 3位
printf("%d\n",a);// -24
>> :右移,按bit位往右偏移(数值会变大)
1.无符号右移:
语法:操作数 >> 移动位数(bit位)
unsigned int a = 3 >> 3;//将 3 转化为 二进制 后 向右移动 3位
printf("%d\n",a);
2.有符号右移:
语法:操作数 >> 移动位数(bit位)
unsigned int a = -3 >> 3;//将 3 转化为 二进制 后 向右移动 3位
printf("%d\n",a);
举例:
注意:
1.在进行移位运算的时候,凡是被移出去的位统统丢弃,凡是空出来的位统统补0.移位运算针对的是无符号整数;
2.如果非要进行有符号的移位运算,那么左移的时候,空出来的补0,右移的时候,空出来的补符号位(原码阶段);
3、其他运算符
说明:= += -= *= /= %=
赋值运算符
包含:= ,由 右 -> 左,优先级排倒数第二。
int a = 4;
int num = 5 + 6;
注意:
赋值运算符的左边(左操作数)必须是可写的地址。
复合赋值运算符
包含:+= -= *= /= %= ,由 右 -> 左,优先级排倒数第二。
int i = 1;
i+=1;//等价于 i = i + 1
i*=5;//等价于 i = i * 5
三目运算符
语法:表达式1 ? 表达式2 : 表达式3
求值顺序:
- 如果表达式1的值为1(真),则整个条件运算表达式的值为表达式2的值;
- 如果表达式1的值为0(假),则整个条件运算表达式的值为表达式3的值;
//案例1 //需求:根据考试成绩进行奖励和惩罚 // score >= 90 奖励外星人电脑一台 // score < 90 奖励刷锅刷碗1个月 int score = 89; printf("%s",score >= 90 ?"奖励外星人电脑一台":"奖励刷锅刷碗1个月");//奖励刷锅刷碗1个月 %s 字符串 //案例2 int a = 10,b = 20; int ret = a>b?++a:++b; printf("ret:%d a:%d b:%d",ret,a,b);// ret=21,a=10,b=21
练习题:
使用三目运算符比较三个任意整型数的最大值?
sizeof(int)
说明:用来计算某种类型或者变量所占的字节数。(中文英文字符集问题,不标准)
逗号运算符(,)
说明:优先级最低,左 -> 右,由多个运算符将多个不同的式子连接起来的表达式称之为逗号表达式;
语法:
(表达式1,表达式2,...,表达式n);
求值顺序:先求表达式1,再求表达式2,以此类推,整个逗号表达式的值为表达式n的值。
注意:
- 逗号表达式的优先级最低;
- 运算顺序从左往右;
- 整个逗号表达式的值取决于最右边的表达式的值。
举例:
int a = 5,b = 3;
int ret = (a>b,a++,b++,a);(这种写法其实就是为了减少代码量)
printf("ret:%d\n",ret);
四、扩展:
ISO-8859-1:西欧编码,一般遇到直接改编码;
GBK:国标,中国标准编码/字符库,收录了大量的汉字+中文符号;
UTF-8:全球标准,几乎收录了全球的各种字符集,包括GBK包含的常用汉字以及中文符号;
ASCII:字符集,C语言字符就是以ASCII码进行存储。针对程序内部数据。
1、常见的字符编码
- ASCII:1个字符等于1个字节,总共256个字符,针对程序内部;
- GBK:1个字符等于2个字节,表示简体、繁体及其符号。表示: \xced2;
- Unicode:四个字节表示:全球统一 编码,囊括了全世界所有的字符。表示: \u6211;
- UTF-8:长度变化的字符集,是对Unicode字符集的优化,提高效率,表示: \xe68891;
- ISO-8859-1:这个是西欧语言字符集。
如果代码中有字符乱码----编码和解码的字符集不一致:
解决方案:
- 修改成一致的编码;
- 换成英文或者数字,毕竟只有中文才会乱码
2、进制转换
int 4字节(8位)
float 4字节
double 8字节
我们目前接触到的进制有二进制、八进制、十进制、十六进制
1.十进制整型常量:由0至9的数字组成,没有前缀,不能以0 开头;
2.八进制整型常量:以0(数字0)为前缀,其后由0到7的数字组成,没有小数部分;
3.十六进制整型常量:以0x或0X为前缀,其后由0到9的数字和A到F(大小写均可)字母组成,没有小数部分;
整型常量中的长整型数据可用L作后缀表示。如:1234L等。
-
其他进制 转 十进制:
按权相加
- (1234)⏨ = 4*10⁰ + 3*10¹+2*10²+1*10³
- (0*1234)₁₆ = 4*16⁰+3*16¹+2*16²+1*16³
-
十进制 转 其他进制:
辗转相除法:将需要转换的数据不停的除以转换的进制数,直到商为0;
1.十进制转二进制
2.十进制转八进制
-
八进制 转 十六进制:
借助于二进制,将八进制转化为二进制,将二进制转化为十六进制;
-
十六进制 转 八进制:
借助于二进制,将十六进制转化为二进制,将二进制转化为八进制;
3、有符号数和无符号数
在C语言中,整数是可以带符号的(有符号,signed)或不带符号的(无符号,unsigned)。这两种类型的整数在内存中以二进制形式表示,并使用不同的模式。
-
有符号数(Signed Numbers)
有符号数是来表示正数、负数、0的整数类型;
在内存中,有符号数使用最高位(通常是符号位)来表示正负;
如果最高位为0,表示这个数是正数或0;如果最高位为1,表示这个数是负数;其余位用于表示数值本身。
举例:例如一个8位有符号整数可以表示的范围是 -128 ~ 127 。这是因为1位用于表示符号(正或负),剩下的7位。
注意:有符号位是默认的,在C语言中,基本整数类型如 int,short,long 默认为有符号数,除非明确指定位无符号。
举例:
signed int num = 12; //标准有符号位写法
int num == 12; //默认省略signed关键字
-
无符号数(Unsigned Numbers)
无符号数是只能表示非负整数的数据类型。在内存中,无符号数不使用符号位,所有位都用于表示数值。因为,无符号数的范围比有符号数的范围更大。
注意:在C语言中,通过关键字后天添加 unsigned 。来指定无符号类型,如 unsigned int、unsigned short...
举例:例如一个8位的无符号整数可以表示的范围是 0 ~ 255 。这是因为所有8位都用来表示数值,没有符号位。
unsigned int num = 12; //标准无符号位写法
4、原码、反码和补码
计算机存储的是补码。
原码:是最直观的表示方法,它直接用二进制数表示一个数,包括正负号。在原码中,最高位(最左边的位)是符号位,0 表示正数,1 表示负数;其余位表示数值本身。例如,十进制数 +5 的原码表示为 0000 0101
,而 -5 的原码表示为 1000 0101
;
反码:主要用于表示负数。对于正数,其反码与其原码相同。对于负数,其反码是将原码除符号位外的所有位取反(0 变 1,1 变 0)。例如,十进制数-5的反码表示为1111 1010;
补码:是计算机中最常用的表示方法,用于进行二进制加法运算。对于正数,其补码与其原码相同。对于负数,其补码是其反码加 1。补码的一个重要特性是,任何数的补码加上该数本身,结果总是 0。例如,十进制数 -5 的补码表示为 1111 1011
。
0的反码、补码都为0。
五、标识符
1、关键字(32个)
数据关键字(12个):char ,double ,float ,enum ,unsigned ,int ,long ,short ,signed ,struct ,union ,void ;
控制语句关键字(12个):for ,do ,while ,break ,continue ,if ,else ,goto ,switch ,case ,default ,return ;
存储类型关键字(4个):auto ,extern ,regsiter ,static ;
其他关键字(4个):const ,sizeof ,typedef ,volatile ;
2、预处理命令(12个)
define endif elif error line include
ifdef ifndef pragma undef if else