注意:定义变量时,除变量名外其余均为数据类型
一、进制
1.分类
二进制(B),八进制(O),十六进制(H),十进制(D)
2.强调
二进制的范围:0,1
八进制的范围:0,1,2,3,4,5,6,7
十六进制的范围:0,1,2,3,4,5,6,7,8,9,a(A),b(B),c(C),d(D),e(E),f(F)
十进制的范围:0,1,2,3,4,5,6,7,8,9
3.思考
1.为什么要存在八进制和十六进制?
答:更好的表示二进制所表示的序列
2.八进制和十六进制如何替换二进制?
答:3位二进制代表一个八进制(000 - 111)---》0 -7,4位二进制代表一个十六进制 (0000 - 1111)---》0 -F
3.二进制转十进制:从二进制的低字节处开始,每一位乘以2的次幂,(次幂是从0开始),然后累加即可得到十进制的数字。
4.十进制转二进制:十进制短除法,取余,后将商数再次短除,取余,直到余数为0截止,后将每一步的余数按照从下向上的顺序书写。
二、数据类型
1.32关键字分类
1、基本数据类型:
char,short,int,long,float,double,enum,union,struct,void共10个
2、存储类型:
auto,register,static,extern 4个
3、控制语句:
if,else,switch,case,default,break,continue,for,while,do,goto共11个
4、类型修饰符:
const,volatile共2个(底层ARM的时候会看到:可以防止编译器进行优化)
5、其他:
signed,unsigned,return,sizeof(运算符 ),typedef共5个
2.基本数据类型
char,short,int,long,float,double,void
为什么要存在数据类型?
答:数据类型标志着系统分配内存空间的大小,因为用户存储的数据所占字节空间不一定完全一样大,为了能够合理化的利用内存空间,提出了数据类型的概念,每一种数据类型所占字节数是不一样的。(同一个数据类型在不同的操作系统位数下,所占字节数也会不同。)
3.数据类型代表的范围
(1)有符号数和无符号数:
有符号数:有符号位(一般处于最高位,占一个位),意味着会有正负之分。最高位为0代表为正数,1代表为负数
无符号数:无符号位(意味着全为大于0的数字)
(2)数字范围(32位操作系统)
有符号数:signed char:1个字节(8Bit)
1000 0000(补码)(1111 1111)(原码)~ 0111 1111(补码)(0000 0000)(原码)(-128 ~ 127)
无符号数:unsigned char
0000 0000(补码)(0000 0000)(原码)~ 1111 1111(补码)(1111 1111)(原码)(0 ~ 255)
有符号数:signed short:2个字节(16Bit)
1000 0000 0000 0000 (补码)~ 0111 1111 1111 1111(补码) (-2^15 ~ 2^15 - 1)
无符号数:unsigned short
0000 0000 0000 0000(补码) ~ 1111 1111 1111 1111(补码) (0 ~ 2^16 - 1)
int :4个字节(32Bit)
signed int ( -2^31 ~ 2^31 -1)
unsigned int (0 ~ 2^32 - 1)
4.类型转换
(1)分类
强制类型转换;
隐式类型转换。
(2)强制类型转换
由程序员自己来实现的;
格式:(目标数据类型)变量名/函数名;
理解:目标数据类型就是最终想要强转之后的数据类型。
(3)隐式类型转换
由系统自动触发的;
三、常量
1.概念
其值在程序运行期间不可以被改变的数据
2.分类
字符常量:’a’ ‘\0’ ‘\n’ ‘\t’ ‘\b’
整形常量: 12 , 890
字符串常量: ”haha” “hello” “a”
浮点型常量: 1.25
宏常量(概念):宏是一种替换
(格式): #define 宏名 值/表达式
3.注意
1、宏名建议大写,(醒目)
2、替换
写法1:#define N 10
写法2:#define X(m,n) m * n
宏的总结:
(1)因为宏是一种替换,因此我们需要严格遵守“替换”这个法则,不要人为在宏替换之前进行任何的运算,只需要换即可。
(2)但是,为了保证定义的宏绝大多数情况下符合我们的初衷,因此建议给每一个宏的参数以及宏的每一个表达式都加上括号。
四、变量
1.概念
其值在程序运行期间可以被改变的数据;
2.定义
存储类型 数据类型 变量名;
3.解释
存储类型:标识开辟空间的位置,有4个,分别是:auto register static extern
数据类型:标识开辟空间的大小
变量名:申请到的连续空间的名字
4.初始化与赋值
初始化:定义变量的同时给其赋值
eg:int a = 90;
赋值:先定义,再赋值
eg: int a;
a = 89;
5.生命周期和作用域
生命周期:生效时间的长短(从什么时候开始,到什么结束)
作用域:能够使用的范围
6.全局变量和局部变量
局部变量:只要在{}内部的都是局部变量
全局变量:在{}外面定义的变量都是全局变量
全局变量和局部变量可以重名,当重名时,如果局部变量的生命周期还未结束,则会优先使用局部变量的值。
局部变量:没有初始化,值为随机值
生命周期:从定义开始,到距离它最近的}结束
作用域:距离它最近的模块{}内有效
全局变量:没有初始化,值为0
生命周期:从定义来开始,到源程序结束
作用域:服务于整个源程序(该源程序可以包含多个文件,前提:另外的文件必须得“引用”方可使用)。
被static修饰的局部变量:没有初始化吗,值为0
生命周期:从定义开始,到程序终止(被延长了)
作用域:距离它最近的模块{}内有效
被static修饰的全局变量:没有初始化吗,值为0
生命周期:从定义来开始,到源程序结束
作用域:只在当前文件内有效(被缩短了)
7.内存分区
(1)auto:
被auto 修饰的变量存储在栈区,只能修饰局部变量,可以省略不写。
(2)register:
被rsgister修饰的变量存储在寄存器中,只能修饰局部变量,因为编译被优化过,因此可以自动识别一些频繁使用的变量,优先将其放置在寄存器中,如果寄存器不够存储,则会放在栈区。
(3)static:
被static修饰的变量存储在静态区,可以修饰局部变量,全局变量和函数,不可以省略,其中:
1.static修饰局部变量:代表可以延长该局部变量的生命周期,意味着该静态局部变量只会被定义一次(要是初始化过了,就按照初始化的值,没有初始化则需要按分配的值看待)。
2.static修饰全局变量或者函数:都代表隐藏,只能在本文件内有效(因为可以有多个文件一个工程中),不能被外部调用。
(4)extern:
专门引用一个来自于外部文件(前提:同一个工程)的一个全局变量或者函数进来使用。
五、运算符
1.分类
算术运算符,逻辑运算符,关系运算符,位运算符,赋值运算符,逗号运算符,三目运算符,sizeof运算符。
2.算数运算符
+ ,-,*,/,%,++,--
总结:
%(取余运算符)只能针对整数进行运算,结果正负号由运算符左侧值决定
当表达式被拿来使用的时候,需要关心在前在后的问题,毕竟赋值都是给表达式整体赋值,反之,不管在前在后,都能够使得变量自身自加或者自减。
3.逻辑运算符
&& ,||,!
注意:&& 和 ||在运算的时候会存在一个截断法则
假设形式如下:表达式1 && 表达式2 或者 表达式1 ||表达式2
对于&&:
表达式1为假时,表达式2不执行
对于||:
表达式1为真时,表达式2不执行
4.关系运算符
>,<,>=,<=,==,!=
建议:将常量放置在==
左边,将变量放置在右边,这样可以避免逻辑错误的出现
5.位运算符
& ,|,~,<<,>>,^
(1)按位与:&
规则:全1为1,有0 为0
总结:按位与一般是用来将一串二进制指令中的某几位或者某一位清零。
(2)按位或:|
规则:有1为1,全0为0
总结:按位或一般是用来将一串二进制指令中的某几位或者某一位置1
(3)按位取反:~
每一位都取相反的位
(4)异或:^
规则:相同为0,不同为1
总结:异或是将一串二进制指令中的某几位或者某一位取反
(5)左移:<<
有符号数:
正数:高位丢弃,低位补0
负数:高位丢弃,低位补0
无符号数:高位丢弃,低位补0
(6)右移:>>
有符号数:
正数:低位丢弃,高位补0
负数:低位丢弃,高位补1
无符号数:低位丢弃,高位补0
总结:左移1位相当于扩大2倍,右移1位相当于缩小2倍。
6.逗号运算符
格式:
表达式1,表达式2,表达式3.。。。。。。
执行规则:从左到右,依次执行,并将最后一个表达式的结果作为整个逗号运算符的结果。
7.三目运算符
格式:
表达式1 ? 表达式2 :表达式3
执行规则:
先执行表达式1,根据表达式1 的真假;来选择表达式2或者表达式3,如果表达式的结果为真:则直接执行表达式2,并将表达式2的结果作为整个三目运算符的结果输出,反之则将表达式3 的结果作为整个三目的结果进行输出。
8.sizeof运算符
功能:测试对象所占字节数的大小
测试方法:
(1) sizeof(类型名);
(2) sizeof(变量名);
注意:
当sizeof的测试对象是变量名的时候,可以不用写括号
int a;
sizeof(a); 对的
sizeof a; 对的
sizeof(int) 对的
六、三大结构
1.顺序结构
概念:
任何代码的组成都是有逻辑性,意味着有先后顺序。
2.分支结构
(1)单分支
格式:
if(表达式)
{
语句块;
}
执行规则:表达式成立则进入if语句,执行语句块,否则不进入。
注意:{ }在书写的时候,允许不写,但是不写{ },这个时候程序就只会将if之后的一句话当成是if的子语句。
if(表达式)
语句1; //是if成立之后需要执行的代码,仅此一句,后面的语句2,语句3不会被当成if的子语句。
语句2;
语句3;
(2)双分支
格式:
if(表达式)
{
语句块1;
}
esle
{
语句块2;
}
(3)多分支
if~else if~else语句:
格式:
if(表达式1)
{
语句块1;
}
else if(表达式2)
{
语句块2;
}
。。。。
else
{
语句块n;
}
switch语句:
格式:
switch(整形/常量/表达式)
{
case 常量1:语句块1;break; //case语句后是冒号
case 常量2:语句块2;break;
....
default:语句块N;
}
注意:switch括号中不允许出现浮点型。只能是整形值(包含字符常量)。
3.循环结构
(1)for循环
格式:
for(表达式1;表达式2;表达式3)
{
循环体;//代码的具体实现过程
}
执行规则:
先执行表达式1,再表达式2,判断表达式2是否成立,成立则进入循环体执行代码,执行完循环之后,再次执行表达式3,最后再执行表达式2,以此类推,如果表达式2不成立,则退出循环。
表达式1:赋初值,如果之前已经赋好初值,在此位置可以省略不写表达式1
表达式2:条件判断,在此位置不能省略
表达式3:条件更新,在此位置可以省略,但是循环体最后一句话需要写上
(2)while循环
格式:
//定义计数器并赋初值(事先做好)
while(表达式)
{
循环体;
//更新计数器(需要手动做的)
}
执行规则:
先判断表达式是否为真,为真进入循环体,再判断是否还未真。。。。为假直接结束循环
(3)do~while循环
格式:
//定义计数器并赋初值
do
{
循环体;
//更新计数器
}while(表达式); //别忘了有分号
执行规则:
先do一次,然后再判断表达式是否为真,为真则进入循环体,最后再判断。。。为假结束循环。
(4)总结:
for和while:
for适合循环次数已知
while循环适合循环次数未知
do_while 和 while:
do_while至少被执行1次
while至少被执行0次
4.break和continue
概念:
break:结束循环的(一旦遇到break,循环不管还有多少次没有被执行,都会结束掉)
continue:结束本次循环,立即进入下一次循环(意味着只是将continue之后的语句在本次循环中不再执行,但是不妨碍执行下一次的循环。)
七、数组
数组是一组相同数数据类型元素的集合
1.一维数组
定义数组:
存储类型 数据类型 数组名[元素个数];
分析:
存储类型:数组自身开辟空间的位置
数据类型:数组中元素的数据类型
数组名:一片连续空间的名字(见名知意)
[N]:数组的标识(下标引用操作符)数组边界(0~(N-1))
元素个数:存储元素的个数,可有也可无(数组需初始化,数组元素 个数由初始化内容确定),声明数组时方括号中只能使用整形常量表达式,且表达式值必须大于0
初始化:
int arr[6] = {0,0,0,0,0,122};
int arr[6] = {[5] = 122};
对于一般的初始化,在初始化一个元素或初始化列表少于数组元素个数时,未初始化的元素都被设置为0。
数组元素赋值
C不允许把数组作为一个单元赋值给另一个数组,且除初始化外也不许使用{}花括号列表的形式赋值。
2.二维数组
定义二维数组:
存储类型 数据类型 数组名[行数] [列数];
分析:
存储类型:二维数组在内存中开辟空间的位置
数据类型:二维数组中元素的类型
数组名:申请连续空间的名字
行数:
列数:
二维数组内存图解
二维数组初始化
int arr[2] [3] = {{1,2,3},{4,5,6}};
总结:
二维数组名可以用来测试所占空间的总字节数。
二维数组的行数可以省略,但是列数不可以省略的。
3.字符数组(字符串)
字符数组(字符串)
字符数组的本质是字符串,而字符串是以空字符\0
结尾的char类型的数组
定义格式
存储类型 数据类型(char) 数组名[元素个数];
字符数组(字符串)初始化
char str[20] = {‘h’,’e’,’l’,’l’,’o’}; //不建议使用
char str[20] = {“hello”}; //居多
char str[20] = “hello”;
总结:赋值字符串时,要是以单引号引起来,没有赋值’\0’就没有,赋值了才会有\0
,但是双引号引起来会自带一个\0
。
八、函数
如何学习函数(库函数,自定义函数)?
三步走:
第一步:功能
第二步:参数
第三步:返回值
1.main函数
思考:如何实现main函数传参?
答:在命令行实现
2.自定义函数
概念:为了将功能单一的代码进行模块化编程,提高代码的复用率。
定义格式:
存储类型 返回值类型 函数名 (数据类型1 形参名1,数据类型2 形参名2........);
{
函数体;//代码的实现过程
return 返回值;//不需要返回值就不用写或者写:return;
}
3.函数传参
1、形参和实参
定义函数时函数名后括号中的变量为形参(形式参数)
调用函数时函数名后括号中的内容为实参(实际参数)
实参和形参是否可以重名?
可以,因为实参和形参的作用域不同
2、传参方式
1.值传递
2地址传递(址传递)
想通过子函数来引起实参自己发生改变的时候,就需要传递实参的地址(不管实参此时是什么类型
4.函数递归
书写递归函数的思路:
三步走:
从哪里开始?
从哪里结束?
每一步需要干什么?
5.指针函数
概念:函数返回值类型指针类型的函数
格式:
指针类型 函数名(数据类型1 形参名1, 数据类型2 形参名2.。。。。)
{
函数体;
return ;
}
九、枚举类型
概念:和宏特别类似(都是一些常量值),枚举也算是一种基本数据类型
作用:使用枚举作为错误码使用居多(可以给很多的错误值eg:-1 -2 -3 ...可以取一些语义化一点的常量名字来代替)。
定义:
enum 枚举名
{
常量1,
常量2,
。。。。
常量N
};