数据类型:char, double, int, float, short, long, struct, union, enum, void;
控制语句:if, else, while, do, for, switch, goto, default, break, continue, case;
存储类型:auto, register, static, extern;
return:返回函数;
const:只读;
signed:有符号数;
unsigned:无符号数;
sizeof:计算所占内存的大小;
typedef:给一个已有的类型取别名;
volatile:防止编译器优化;
程序员自己定义,一般用来定义变量名,函数名,类型名;
命名规范:
1. 由数字,字母,下划线组成;
2. 第一个字母不能是数字;
3. 不能和关键字重名;
相对于32OS:
字符型:char(1byte)
整型:int(4byte),long(4byte),short(2byte)
浮点型:float(4byte),double(8byte)
相对于64OS:
字符型:char(1byte)
整型:int(4byte),long(8byte),short(2byte)
浮点型:float(4byte),double(8byte)
注意:每一种数据类型所占内存大小不一样,数据类型主要是让我们合理分配内存;
有符号数和无符号数:(计算机内默认是有符号数)
signed:数值有正负之分,以补码的形式存储
最高位是符号位,正数的符号位是0,负数的符号位是1
正数:原码,反码,补码都是一样的;
负数:原码---->补码:取反加1
补码---->原码:减1取反(补码的补码就是原码)
unsigned:只有正数;
值域范围:(以下计算都是补码)
char(1byte) 1byte=8bit
unsigned:0000 0000 ~ 1111 1111 0~255
signed:1000 0000 ~ 0111 1111 -128~127(如果是1111 1111 那原码就是-1,很大)
unsigned char c = 260;(超范围就会转圈去计算值)
printf(“%d\n”,c);//4
signed char a = 130;
printf(“%d\n”,a);-126
int(4byte)
Unsigned:
0000 0000 0000 0000 0000 0000 0000 0000 ~ 1111 1111 1111 1111 1111 1111 1111 1111
0~2^32-1
signed:
1000 0000 0000 0000 0000 0000 0000 0000 ~ 0111 1111 1111 1111 1111 1111 1111 1111
-2^31 ~ 2^31-1
浮点型存储方式和正数的存储方式是不一样的
浮点型的存储方式决定了他不能够准确的表示一个数,只能是一个近似的值
float(4byte):单精度浮点数,有效数字的位数一般为6-7位(%f)
double(8byte):双精度浮点数,有效数字的位数一般位15-16位(%lf)
%f和%lf默认输出小数点后六位。
printf(“格式控制串”,输出表);
格式控制串:原样输出的内容+格式化符(%d,%f)
输出表:要输出的对象
“s y \453 a b”(6个字节)(字符串/0结尾)
在程序运行期间,其数值不会被改变的量;
5.1 字符常量
‘a’ , ‘b’ , ‘c’
5.2 整型常量
二进制:0110
八进制:066
十进制:99 -2
十六进制:0x234
注意:默认情况下,整型常量都是有符号的
无符号的int型数:66U
长整型:66L
无符号的长整型:66UL
5.3 浮点型常量
小数:1.23 0.00001 10000(浮点型常量包含整型常量)
指数形式:(1e-5) (1e+5)
5.4 字符串常量
"hello","word"
注意:字符串以’\0’作为结束符;
5.5 标识常量(宏)
#define 宏名 表达式
注意:
1.宏名一般用大写,小写也可以,因为为了和变量区分,所以用大写;
2.宏后面没有分号;
宏只是单纯的替换
宏函数:(既有函数的特点,又有宏的特点)
#define 函数名(形参) 函数体
注意:宏只是单纯的替换,不会考虑运算的优先级问题,所以需要给每个形参加括号,整个表达式也加括号;
如何定义一个变量?
存储类型 数据类型 变量名;
存储类型:决定了开辟的空间在内存中的哪个分区(局部变量不写默认auto,全局变量不写就是没有);
数据类型:决定了要开辟的内存空间的大小;
变量名:开辟的空间的名字;
6.1 局部变量
定义在函数体内部的变量(任何函数体)
6.2 全局变量
定义在函数体外的变量;
6.3 存储类型
存储类型:auto,extern,static,register
auto:修饰的变量存储在栈区,只能修饰局部变量
extern:修饰的变量存储在静态区(.bss和.data统称为静态区),只能修饰全局变量
static:修饰的变量存储在静态区,局部变量和全局变量都可以修饰
register:修饰的变量存储在寄存器中,只能修饰局部变量
总结:
1. 除了static和register修饰的局部变量外,其他的都存在栈区;
2. 全局变量存储在静态区;
3. 静态区变量存储在静态区(static修饰的变量就是静态变量)
6.4 初始化
初始化:定义变量时对变量进行赋值;
1)int a = 10; //初始化
2) int a;
a = 10; //赋值
总结:
1)全局变量没有初始化其默认值为0;
2)局部变量没有初始化其默认值为随机值(64位操作系统优化为0)
auto:修饰局部变量,存储在栈区
register:修饰局部变量,存储在寄存器中,建议把变量存储在寄存器,可以提高程序的运行速度,最终是否存储在寄存器中,取决于编译器,如果没有寄存器,存储在栈区
extern:修饰全局变量,存储在静态区
程序可以由多个.c文件组成,但一个程序只能有且仅有一个main函数;
作用:告诉编译器,这个全局变量已经在其他文件定义过了;
static:修饰的变量存储在静态区,既可以修饰局部变量也可以修饰全局变量
1.static 修饰局部变量时,延长了局部变量的生命周期,如果局部变量没有初始化,其值为0,如果初始化,只能初始化一次;
2. 修饰全局变量,只能在本文件内使用(限制了全局变量的作用域)
6.5 生命周期和作用域
生命周期:从什么时候开辟空间到什么时候释放空间;
作用域:
局部变量:
生命周期:从定义开始,到模块(大括号)结束
作用域:大括号内
static修饰的局部变量:
生命周期:从定义开始,到程序结束
作用域:大括号内
全局变量:
生命周期:从定义开始,到程序结束
作用域:整个程序
static修饰的全局变量:
生命周期:从定义开始,到程序结束
作用域:本文件内
横向箭头:不管有没有进行混合运算,都势必进行转换;
注意:char,short使用的时候都按照int来使用;
注意:float使用的时候按照double来使用;
注意:竖向箭头只有进行混合运算时才会进行转换;
2. 运算符
单算移关与,异或逻条赋
单目运算符,算数运算符,左移右移,关系运算符,按位与,异或,按位或,逻辑运算符,条件运算符,赋值;
2.1 算数运算符
+, -, *, /, %, ++, --
注意:%不能用于浮点数
++在前:++a,先自加,再赋值
++在后:a++,先赋值,再自加
注意:如果a++或者++a自成立一条语句。都当作a=a+1
2.2 关系运算符
>, <, >=, <=, ==, !=
2.3 逻辑运算符
&& || !
表达式1 && 表达式2
&&截断法则:有一个为假结果就为假,前一个为假,后面就不进行运算了
表达式1 || 表达式2
||截断法则:有一个为真,结果就为真,前一个为真,后面就不需要进行计算
2.4 sizeof 运算符
2.5 三目运算符
表达式1?表达式2:表达式3
判断表达式1的值是否成立,如果成立,将表达式2的值作为整个表达式的值,否则,将表达式3的值作为整个表达式的值;
2.6 逗号运算符
表达式1,表达式2,表达式3,………………表达式n
从左到右依次计算表达式的值,将表达式n的值作为整个表达式的值;
注意:逗号运算符的优先级是最低的,所以使用的时候加括号;
2.7 位运算符
&,|,~,^,<<,>>
与运算(&)(有0为0);或运算(|)(有1为1);取反;异或
左移:
高位丢弃,低位补0
将1左移俩位
0000 0001--->0000 0100
右移:
无符号数:低位丢弃,高位补0
有符号数:低位丢弃,正数高位补0,负数高位补1
函数:有独立功能的模块
标准的输入输出函数:scanf,printf(对变量的类型没有限制)
输入:从键盘拷贝数据到内存中
输出:从内存拷贝数据到显示屏
3.1 输出
printf(“格式控制串”,输出表)
格式控制串:原样输出的内容(可省)+格式化符
输出表:要输出的对象
整型:
%d:十进制整数
%o:八进制整数
%x,%X:十六进制整数
#:自动在八进制和十六进制前加前缀
%u:无符号整型
%hd:short类型
%ld:long类型
%lld:longlong类型
字符型:
%c字符型
浮点型:
%f:float
%lf:double
%e:指数
%g:选择小数和指数合适的一种
.n:保留n个小数
m:指定我们输出的域宽,默认是右对齐,m的值大于数据的实际长度,左边补空格,否则,原样输出 %m.nf
3.2 输入
scanf(“格式控制串”,地址表)
地址表:&+变量名
注意:
1.scanf 格式控制串,不要加修饰语,如果要加,原样输出;
2.如果输入"%d%d" 时要给多个变量赋值,在格式控制符之间没有间隔,那在输入的时候,以空格,回车,tab作为一个变量的输入结束;
3.全部输入结束,必须以回车作为结束符;
4.如果是“%c%c”,在输入的时候,不能有空格,回车,tab,因为空格,回车,tab也是字符;
解决办法:
1.在"%c%c"之间加个空格,逗号(输入时原样输入);
2.加%*c,*代表是抑制符;
3.3 字符的输入输出
int getchar();
返回值:从键盘得到的ASCLL码
char a;
a = getchar();
putchar(参数);
参数:你要输出的ASCLL码
printf右结合,从右到左计算
语句按照一定的先后顺序去执行;
2.1 单分支选择语句
if(表达式)
{
语句;
}
先判断表达式的值,如果表达式的值为真,则执行语句;
2.2 双分支 if 语句
if(表达式)
{
语句1;
}
else
{
语句2;
}
先判断表达式的值,如果表达式的值为真,执行语句1,否则执行语句2;
案例:输入一个年份,判断该年是平年还是闰年
2.3 多分支语句
if(表达式1)
{
语句1;
}
else if(表达式2)
{
语句2;
}
else if(表达式3)
{
语句3;
}
………………
else if(表达式n)
{
语句n;
}
else
{
语句n+1;
}
从上到下,依次判断表达式的值,如果表达式的值成立,就执行对应的语句;
2.4 switch 语句
Switch(表达式)
{
case 标号1:
语句1;
case 标号2:
语句2;
…………
case 标号n:
语句n;
default:
语句n+1;
}
注意:
1.表达式不能是float类型;
2.标号必须为常量;
3.表达式 = 标号时,执行冒号后面的语句,直到switch,case语句结束,或者碰到break语句结束,所有的标号都不等于表达式是,执行default;
案例:输入年份,月份,天数,输出是该年的第几天?
重复的去做某一件事
循环的三要素:循环的起始条件,循环的终止条件,循环变量的变化
3.1 for 循环
for(表达式1;表达式2;表达式3)
{
循环体;
}
表达式1:循环的起始条件
表达式2:循环的终止条件
表达式3:循环变量的变化
先执行表达式1的值,再执行表达式2的值,如果表达式2的值为真,执行循环体,然后执行表达式3的值,如此反复,直到表达式2的值为假,跳出循环;
注意:表达式1,2,3都是可以省略的,但是分号不能省略;
案例:
*
***
*****
*******
自己定义行数进行打印;
3.2 while 语句
while(表达式)
{
循环体;
}
判断表达式的值是否成立,如果成立,执行循环体,否则,跳出循环;
3.3 do while 语句
do
{
循环体;
}while(表达式);
总结:while和do while区别
while先判断,再执行,语句至少执行0次
do while先执行,后判断,语句至少执行1次
break,continue
break:1,跳出switch语句 2,跳出循环
continue:结束本次循环,开始下一次循环
3.4 死循环
for(;1;)
{
;
}
while(1)
{
;
}
3.5 goto 语句
无条件跳转语句,一般格式为goto语句标号
语句标号:按照标识符命名规范
一组数据类型相同的元素的集合
特点:(1)数据类型相同(2)地址连续
打印地址%p
存储类型 数据类型 变量名;
int a;//定义了一个整型变量;
存储类型 数据类型 数组名[元素的个数];
int a[5];//定义了一个整型数组;
存储类型:auto,static,extern,register
数据类型:数组中每一个元素的数据类型
数组的数据类型:数据类型 [元素个数]
数据类型:去掉变量名就是数据类型
数组所占内存空间=sizeof(数据类型)*元素个数
注意:
1. 数组名代表整个数组;
2. 数组名也代表数组首元素的地址;
注意:元素个数必须是一个确定的数;
3.1 部分初始化
在进行部分初始化的时候,未初始化部分的值为0,因此,利用这一特点可以给数组中元素清零。
int arr[100]= {0};
3.2 全部初始化
int a[5] = {1,2,3,4,5};
int a[ ] = {1,2,3};//在进行全部初始化的时候,数组元素的个数是可以省略的,由后面赋值的具体个数来决定;
总结:
4. 数组的访问
数组名[下标];
注意:下标是从0开始
案例:
a[5] = {1,2,3,4,5};//error
a[] = {1,2,3,4,5};//error,数组名代表数组首元素地址
打印等腰三角形
思想:从左到右,俩俩依次比较,如果前一个数比后一个数大的话,交换位置,否则不变;
字符数组的本质:其实就是字符串,以’\0’作为结束符;
存储类型 数据类型 数组名[元素个数];
char str[10];//定义了一个字符数组,名字叫str,有十个元素,每一个元素都是char型
char str[6] = {‘h’,’e’,’l’,’l’,’o’,’q’};//error 最多存放五个元素,留一个位置给‘\0’
char str[6] = “hello”;
char str[6] = {‘\0’};//字符数组清零
6.1 字符数组的输出函数
%s:字符串
puts(数组名);
功能:将数组中的内容输出打印到终端,并且自动加换行
注意:遇到’\0’结束
6.2 字符串的输入函数
gets(数组名);
功能:将键盘接收到的字符串存放在数组中,并在末尾自动加’\0’
注意:不会越界检查,所以输入时不要越界
总结:
scanf和gets区别:
gets是以回车作为结束符,但是scanf以回车,table,空格作为结束符;
缓冲区:
gets:当完成字符串的输入后,会自动清空缓冲区内容
scanf:当完成字符串的输入后,缓冲区会遗留空格,回车,tab
注意:
gets首先会检查缓冲区有没有内容,如果有,直接拿来使用,否则输入
scanf是标准的输入函数,只能通过键入方式
printf和puts的区别:
puts会自动添加换行,而printf不会
strlen(数组名);
功能:求字符串的长度
返回值:求得的字符串的实际长度,不包含’\0’
strlen和sizeof的区别:
strcpy(数组1,数组2/字符串);
功能:将数组2的内容拷贝到数组1,包含’\0’,相当于完全拷贝
注意:数组1的容量大于数组2
strncpy(数组1,数组2/字符串2,n);
功能:将数组2的前n个字符拷贝到数组1中
注意:拷贝的字符不包含’\0’
strcat(数组1,数组2/字符串2);
功能:将数组2或者字符串2的内容连接到数组1中,数组1的’\0’会被覆盖
注意:数组1的容量大于数组2
strncat(数组1,数组2/字符串2,n);
功能:将数组2的前n个字符,连接到数组1中
strcmp(数组1/字符串1,数组2/字符串2);
功能:比较字符串1和字符串2的大小
返回值:
大于0:字符串1 > 字符串2
等于0:字符串1 == 字符串2
小于0:字符串1 < 字符串2
比较规则:
从左到右依次对字符串的ASCII码进行比较,直到遇到不同的ASCII码或者遇到’\0’结束
数组:一组数据类型相同的元素的集合
整型数组:一组int型的数集合在一起
字符数组:一组char型的字符集合在一起
二维数组:一组(数组类型的)元素集合在一起
7.1 概念
本质:表示元素为一维数组的数组(数组的数组)
数组特点:(1)数据类型相同(2)地址连续
7.2 定义
存储类型 数据类型 数组名[元素个数];
存储类型 数据类型 数组名[行数][列数];
行数:有几个一维数组
列数:一维数组中有几个元素个数
int a[2][3];//定义了一个二维数组,数组的长度为2,每一个元素又是长度为3的int型数组
7.3 数组的初始化
部分初始化:
int a[2][3] = {1,2};
全部初始化:
int a[2][3] = {1,2,3,4,5,6};
int a[2][3] = {{1,2,3},{4,5,6}};
注意:二维数组的行数可以省略,列数不能省
int a[ ][3] = {1,2,3,4,5,6,7,8};//表示有三个一维数组,每一个一维数组有三个元素
int a[2][ ] = {1,2,3,4,5,6};//error有俩个一维数组,但每一个一维数组中有几个元素,不能确定
7.4 数组的访问
7.5 二维字符数组
char dtr[30] = “hello”;
scanf(“%s”,dtr);
printf(“%d\n”,dtr);
存储类型 数据类型 数组名[行数][列数];
行数:(一维字符数组的个数)字符串的个数
列数:(一维字符数组中字符的个数)每一个字符串最多存放几个字符
char str[3][20];//定义了一个长度为3的数组,每一个元素都是长度为20的字符数组
一个独立的功能模块
8.1 为什么要使用函数
使程序变得模块化;提高代码的复用率;
8.2 函数的分类
库函数:printf,scanf,strlen;
引入头文件:#include
调用函数:
strlen(str);
函数名(实际参数列表);
注意:参数有多少个,数据类型是什么,返回值
8.3 自定义函数
函数定义
存储类型 数据类型 函数名(形式参数列表)
{
函数体;
返回值;
}
存储类型:auto,static,extern,register
数据类型:函数返回值的数据类型
函数名:见名知意
形式参数列表:要实现功能所需要的参数,需要调用者传入(1,需要几个参数2,每个参数需要什么类型)
函数体:具体实现的功能
返回值:如果没有返回值,可以省略,不需要写return,数据类型void,如果有,有且仅有一个
调用函数
函数名(实际参数列表)
注意:
函数声明
如果函数没有在main函数之前,就需要在main函数之前进行声明
声明:将函数头直接拷贝至main函数之前,然后加分号
声明的作用:帮助编译器做语法检查
案例:封装函数实现+ - * /
9.1 什么是指针
指针是一种数据类型,是一种保存地址的数据类型
int a;//int:用来保存整型数的数据类型
char c;//char:用来保存字符的数据类型
float b;//float:用来保存小数的数据类型
指针 d;//指针:用来保存地址的数据类型
9.2 什么是地址
内存分配的最小单位是字节,每一个字节都有一个编号,我们把这个编号叫做地址
地址的本质:内存单元的编号
指针:指针就是地址
指针的本质:内存单元的编号
9.3 指针变量
int a;//整型变量
float b;//浮点型变量
char c://字符型变量
指针变量:专门用来保存地址(内存单元的编号)的变量
9.4 指针的定义
存储类型 数据类型 *指针变量名;
int *p;
存储类型:auto,extern,static,register
数据类型:指针所指向的数据类型(去掉*和变量名,剩下的就是指针所指向的数据类型)
指针的数据类型:数据类型 *//int *
注意:
在所有32os,所有的指针占4个字节;
在所有64os,所有的指针占8个字节;
9.5 赋值
har *p = &a;
char *p = NULL;
p = &a;
注意:对指针赋值时,一定要注意数据类型的匹配
思考?
什么是指针
是一种数据类型,保存地址的数据类型
地址是什么
字节(内存单元)的编号
什么是指针变量
专门用来保存地址(内存单元的编号)的变量
指针变量如何定义
存储类型 数据类型 *指针变量名
int *p;
指针变量赋值后能干什么
指针指向谁,就是保存谁的地址,赋值之后可以操作地址里面的元素;
9.6 空指针
没有指向的指针(值为0的指针,就认为该指针没有指向)
注意:0号地址,禁止操作
要操作的话就改变指针指向
9.7 野指针
不知道指向哪里的指针
局部变量没有初始化,其值是随机值
局部指针变量没有初始化,就是野指针
如何避免野指针的出现?初始化为NULL
int *p = NULL;
很危险!!!!!
10.1 概念
指针的指针;
二级指针的内存空间存放的是一级指针的地址;
10.2 定义
定义一级指针:存储类型 数据类型 *指针变量名;
数据类型:指针指向的数据类型;
存储类型 数据类型 **指针变量名;
总结:
1.指针的数据类型,去掉变量名剩下的就是
int *p;//int *
int **pp;//int **
int ***ppp;//int ***
2.指针指向的数据类型,去掉一个*和变量名就是所指向的数据类型
int *p;//int
int **pp;//int *
int ***ppp;//int **
3.指针所能访问的空间大小,由指针所指向的数据类型决定
int a;
int *p = &a;//*p所能访问的空间大小是4个字节
char b;
char *p = &b;//*p所能访问的空间大小是1个字节
int*p;
int **pp = &p;//**pp所能访问的空间大小是4个字节
1.1 指针的算数运算
总结:
p+n:p+n相对于p向地址增大的方向移动了n个数据
实际的变化:p + sizeof(指向的数据类型)*n
p-n:p-n相对于p向地址减小的方向移动了n个数据
实际的变化:p - sizeof(指向的数据类型)*n
p++:p向地址增大的方向移动了一个数据
p--:p向地址减小的方向移动了一个数据
p-q:(p和q的数据类型必须一致)这俩个指针直接相隔的个数
实际的变化:(p-q)/sizeof(指向的数据类型)
注意:
(数组名:1.数组首元素地址(指针常量))
1.2 通过指针常量来访问
数组名:指针常量,不能++,--
1.3 通过指针变量来访问
访问数组:
总结:数组名和指针变量的本质区别:数组名是指针常量
指针的指向没有改变
指针指向发生改变
int a[2][3] ={0};
&a a &a[0] a[0] &a[0][0]
&a:a当作整个数组,整个数组的地址就是首地址
a:代表首元素地址,a[0]地址
&a[0]:二维数组首元素的地址
a[0]:代表一维数组中首元素的地址,a[0][0]地址
&a[0][0]:第一个一维数组的第一个元素的地址
1. a,&a[0],&a[0][0]的值是一样的,但是意义不一样;
a:int (*)[3]:指向一维数组的指针 a[0]:int *:指向一个整型变量 a[0][0]:int类型;
2. 为·什么a不是 int**?
a+1移动了一个数组(12byte)如果是int **,加1,移动4byte;
3. a指向a[0],a[0]又是一个一维数组,所以说a指向了一个一维数组
4.1 数组指针
概念:
指向数组的指针;
定义:
存储类型 数据类型 (*指针变量名)[元素个数];
存储类型:auto,register,static,extern
数据类型:数组指针指向的数组中元素的数据类型
指针的数据类型:数据类型 (*)[元素个数]
元素个数:指针所指向的数组中元素的个数
int a;//数据类型 int
int *p = NULL;//数据类型 int *
p = &a;//一个指向整型变量的指针
int a[5] = {0};//数据类型 int [5]
int (*p)[5] = NULL;//int (*)[5] 数组指针指向整个数组
p = &a;//a相当于整个数组
4.2 数组指针与一维数组
注意:数组指针几乎不操作一维数组,更多的操作二维数组,因为指针访问连续的内存空间才有意义,如果是一维数组,p+1就会越界 ;
4.3 数组指针与二维数组
int a[2][3] = {0};
int (*p)[3] = NULL;(p=&a[0],&a[0]就是二维数组的数组名)
p = a;(&a[0])(a就是二维数组首元素地址)
//之前 int a[5];int *p = a; a,指向首元素的地址,定义了一个指针也指向首元素地址
//现在,首元素为一维数组,a指向首元素地址,首元素一维数组,a就是数组指针,
定义一个数组指针,也指向二维数组的首元素,首元素是一维数组
4.4 指针数组
概念:元素为指针的数组;
定义:
存储类型 数据类型 *数组名[元素个数];
数据类型 *:数组中元素的数据类型
int *arr[3];
定义了一个数组,数组名叫arr,有三个元素,每一个都是int *
4.5 指针数组与二维数组
const:只读
const用来修饰变量,修饰的变量只能读,不能写
总结: