Day1
- 基本的数据类型
- 转义字符
- 常量
- 宏
32个关键字
关键字:系统预定义好的,有特定的含义,全部都是小写,不能重新定义。
数据类型: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:防止编译器优化
标识符:程序员自己定义,一般用来定义变量名,函数名,类型名
命名规范(见名知意)
- 有数字,字母,下划线组成
- 第一个字母不能是数字
- 不能和关键字重名
- 基本数据类型
相对于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
正数:原码,反码,补码都是一样的
比如:10-->0000 1010
负数:-10-->1000 1010
原码:1000 1010
反码:1111 0101
补码:1111 0110(补码的补码就是原码)
1000 1001 1111 0101
1000 1010 1000 1010
负数:原码---->补码:取反加1
补码---->原码:减1取反(补码的补码就是原码)
unsigned:只有正数
2.1字符型家族
值域范围:(以下计算都是补码)
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
2.2整型家族
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
2.3浮点型家族
浮点型存储方式和正数的存储方式是不一样的
浮点型的存储方式决定了他不能够准确的表示一个数,只能是一个近似的值
float(4byte):单精度浮点数,有效数字的位数一般为6-7位(%f)
bouble(8byte):双精度浮点数,有效数字的位数一般位15-16位(%lf)
%f和%lf默认输出小数点后六位。
printf(“格式控制串”,输出表);
格式控制串:原样输出的内容+格式化符(%d,%f)
输出表:要输出的对象
练习:
求short的值域
2byte
Unsigned:
0000 0000 0000 0000 ~ 1111 1111 1111 1111
0 ~ 2^16-1
signed:
1000 0000 0000 0000 ~ 0111 1111 1111 1111
-2^15 ~ 2^15-1
3.转义字符
“s y \453 a b”(6个字节)(字符串/0结尾)
- 常量
在程序运行期间,其数值不会被改变的量
4.1字符常量
‘a’ , ‘b’ , ‘c’
4.2整型常量
二进制:0110
八进制:066
十进制:99 -2
十六进制:0x234
注意:默认情况下,整型常量都是有符号的
无符号的int型数:66U
长整型:66L
无符号的长整型:66UL
4.3浮点型常量
小数:1.23 0.00001 10000(浮点型常量包含整型常量)
指数形式:(1e-5) (1e+5)
4.4字符串常量
注意:字符串以’\0’作为结束符
4.5标识常量(宏)
#define 宏名 表达式
注意:
- 宏名一般用大写,小写也可以,因为为了和变量区分,所以用大写
- 宏后面没有分号
gcc -E .c -o .i
vim .i
宏只是单纯的替换
宏函数:(既有函数的特点,又有宏的特点)
#define 函数名(形参) 函数体
注意:宏只是单纯的替换,不会考虑运算的优先级问题,所以需要给每个形参加括号,整个表达式也加括号。
- 变量
如何定义一个变量?
存储类型 数据类型 变量名;
存储类型:决定了开辟的空间在内存中的哪个分区(局部变量不写默认auto,全局变量不写就是没有)
数据类型:决定了要开辟的内存空间的大小
变量名:开辟的空间的名字
5.1局部变量
定义在函数体内部的变量(任何函数体)
5.2全局变量
定义在函数体外的变量
5.3存储类型
存储类型:auto,extern,static,register
auto:修饰的变量存储在栈区,只能修饰局部变量
extern:修饰的变量存储在静态区(.bss和.data统称为静态区),只能修饰全局变量
static:修饰的变量存储在静态区,局部变量和全局变量都可以修饰
register:修饰的变量存储在寄存器中,只能修饰局部变量
总结:
- 除了static和register修饰的局部变量外,其他的都存在栈区
- 全局变量存储在静态区
- 静态变量存储在静态区(static修饰的变量就是静态变量)
5.4初始化
初始化:定义变量时对变量进行赋值
- int a = 10;//初始化
- int a;
a = 10;//赋值
总结:
- 全局变量没有初始化其默认值为0
- 局部变量没有初始化其默认值为随机值 (64位操作系统优化为0)
auto:修饰局部变量,存储在栈区
register:修饰局部变量,存储在寄存器中,建议把变量存储在寄存器,可以提高程序的运行速度,最终是否存储在寄存器中,取决于编译器,如果没有寄存器,存储在栈区。
extern:修饰全局变量,存储在静态区
程序可以由多个.c文件组成,但一个程序只能有且仅有一个main函数
作用:告诉编译器,这个全局变量已经在其他文件定义过了。
static:修饰的变量存储在静态区,既可以修饰局部变量也可以修饰全局变量
- static修饰局部变量时,延长了局部变量的生命周期,如果局部变量没有初始化,其值为0,如果初始化,只能初始化一次
- 修饰全局变量,只能在本文件内使用(限制了全局变量的作用域)
5.5生命周期和作用域
5.5.1生命周期
从什么时候开辟空间到什么时候释放空间
5.5.2作用域
使用的范围
局部变量:
生命周期:从定义开始,到模块(大括号)结束
作用域:大括号内
static修饰的局部变量:
生命周期:从定义开始,到程序结束
作用域:大括号内
全局变量:
生命周期:从定义开始,到程序结束
作用域:整个程序
static修饰的全局变量:
生命周期:从定义开始,到程序结束
作用域:本文件内
作业:
整理笔记
思维导图
Day2
数据类型转换
运算符
输入输出
- 数据类型转换
1.1强制类型转换(我们自己转的)
1.2隐式类型转换(编译器转的)
横向箭头:不管有没有进行混合运算,都势必进行转换,
注意:char,short使用的时候都按照int来使用
注意:float使用的时候按照double来使用
注意:竖向箭头只有进行混合运算时才会进行转换
案例:
1000 0000 0000 0000 0000 0000 0000 1000
1111 1111 1111 1111 1111 1111 1111 1000
- 运算符
单算移关与,异或逻条赋
单目运算符,算数运算符,左移右移,关系运算符,按位与,异或,按位或,逻辑运算符,条件运算符,赋值
2.1算数运算符
+, -, *, /, %, ++, --
注意:%不能用于浮点数
++在前:++a,先自加,再赋值
++在后:a++,先赋值,再自加
注意:如果a++或者++a自成立一条语句。都当作a=a+1
案例
2.2关系运算符
>, <, >=, <=, ==, !=
注意:0<a<5//error
等号的书写(****************)
常量写在等号左边
2.3逻辑运算符
&& || !
表达式1 && 表达式2
&&截断法则:有一个为假结果就为假,前一个为假,后面就不进行运算了
表达式1 || 表达式2
||截断法则:有一个为真,结果就为真,前一个为真,后面就不需要进行计算
2.4sizeof运算符
2.5三目运算符
表达式1?表达式2:表达式3
判断表达式1的值是否成立,如果成立,将表达式2的值作为整个表达式的值,否则,将表达式3的值作为整个表达式的值
int a = 1,b = 2,c = 3;
a = b++;//a=2,b=3 c = ++a;//a=2,c=2
2.6逗号运算符
表达式1,表达式2,表达式3,………………表达式n
从左到右依次计算表达式的值,将表达式n的值作为整个表达式的值
注意:逗号运算符的优先级是最低的,所以使用的时候加括号
- 设int i = 2,j = 3,k = 4,a = 4,b = 5,c = 3;则执行表达式(a=i<j) &&(b =j>k)&&(c=i,j,k),c的值是多少?
2.7位运算符
&,|,~,^,<<,>>
2.7.1与运算(&)(有0为0)
1101 0011
& 0110 0101
0100 0001
案例:将1101 0011 的0-2位清零
1101 0011
& 1111 1000
1101 0000
2.7.2或运算(|)(有1为1)
1101 0101
| 0110 1101
1111 1101
案例:将1101 0101的3-5位变成101
1101 0101
& 1100 0111
1100 0101
| 0010 1000
1110 1101
2.7.3取反
~1101 0010--------->0010 1101
2.7.4异或
相同为0,不同为1
1101 0010
^ 0100 1000
1001 1010
2.7.5左移
高位丢弃,低位补0
将1左移俩位
0000 0001--->0000 0100
2.7.6右移
无符号数:低位丢弃,高位补0
有符号数:低位丢弃,正数高位补0,负数高位补1
1000 0000 0000 0100
1111 1111 1111 1100
1111 1111 1111 1111
1000 0000 0000 0001------> -1
3.输入输出
函数:有独立功能的模块
标准的输入输出函数: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(“格式控制串”,地址表)
地址表:&+变量名
注意:
- scanf格式控制串,不要加修饰语,如果要加,原样输入
- 如果输入“%d%d”时要给多个变量赋值,在格式控制符直接没有间隔,那在输入的时候,以空格,回车,tab作为一个变量的输入结束
- 全部输入结束,必须以回车作为结束符
- 如果是“%c%c”,在输入的时候,不能有空格,回车,tab,因为空格,回车,tab也是字符
解决办法:
- 在“%c%c”之间加个空格,逗号(输入时原样输入)
- 加%*c,*代表是抑制符
3.3字符的输入输出
int getchar();
返回值:从键盘得到的ASCII码
char a;
a = getchar();
putchar(参数);
参数:你要输出的ASCII码
printf右结合,从右到左计算
首先:a++,第四位输出定格在2,a=3,
其次,a,第三位输出不能确定a=3
然后:++a,此时a=4,但第二位还不能确定
最后:第一个赋值确定为4,a=5,第二位第三位为5可以确定
总结:先赋值的第一次就可以确定,后赋值的要等运算完成,才能确定。
首先,++a等于7第一个
其次,第二个++a,影响到了前面的值,第二个等于8,所以第一个也为8
最后,后面的自加运算不受影响。
总结:只有第一个受到了第二个的影响。
- 自定义宏,用三目运算符实现比较俩个数的大小(使用输入输出)
- 设x为int变量,x = 10,计算x+=x-=x-x,x的值是多少?(x=20)
x+=2;
x=x+2
- 定义x和y为double型变量,x=1,y=x+3/2,y是多少?(2.0)
- 定义t为int型变量,则表达式t=1,t+5,t++的值是多少?(1,t=2)
Day3
控制语句
数组
三大结构:顺序结构,选择结构,循环结构
1.控制语句
1.1顺序结构
语句按照一定的先后顺序去执行
1.2选择结构
1.2.1单分支选择语句
if(表达式)
{
语句;
}
先判断表达式的值,如果表达式的值为真,则执行语句
1.2.2双分支if语句
if(表达式)
{
语句1;
}
else
{
语句2;
}
先判断表达式的值,如果表达式的值为真,执行语句1,否则执行语句2.
案例:输入一个年份,判断该年是平年还是闰年
1.2.3多分支if语句
if(表达式1)
{
语句1;
}
else if(表达式2)
{
语句2;
}
else if(表达式3)
{
语句3;
}
………………
else if(表达式n)
{
语句n;
}
else
{
语句n+1;
}
从上到下,依次判断表达式的值,如果表达式的值成立,就执行对应的语句
案例:输入一个成绩,判断成绩的等级
1.2.4switch语句
Switch(表达式)
{
case 标号1:
语句1;
case 标号2:
语句2;
…………
case 标号n:
语句n;
default:
语句n+1;
}
- 表达式不能是float类型
- 标号必须为常量
- 表达式=标号时,执行冒号后面的语句,直到switch,case语句结束,或者碰到break语句结束,所有的标号都不等于表达式时,执行default
案例:
写出下面程序的结果:
int a = 5;
switch(a)
{
case 2:printf(“2\n”);
default:printf(“不存在该选项\n”);
case 4:
case 1:printf(“1\n”);
case 3:printf(“3\n”);break;
case 0:printf(“0\n”);
}
案例:
输入年份,月份,输出该年该月有多少天?
作业:输入年份,月份,天数,输出是该年的第几天?
1.3循环结构
重复的去做某一件事
循环的三要素:循环的起始条件,循环的终止条件,循环变量的变化
1.3.1for循环
for(表达式1;表达式2;表达式3)
{
循环体;
}
表达式1:循环的起始条件
表达式2:循环的终止条件
表达式3:循环变量的变化
先执行表达式1的值,再执行表达式2的值,如果表达式2的值为真,执行循环体,然后执行表达式3的值,如此反复,直到表达式2的值为假,跳出循环
思考:表达式1,表达式2,表达式3能不能省略?
省略表达式1:
省略表达式2:(死循环)
省略表达式3:
注意:表达式1,2,3都是可以省略的,但是分号不能省略
案例:
for(i = 0;i < 2;i++)
for(j = 0;j <3;j++)
每一次循环i,j的值是多少?
案例:
*
***
*****
*******
自己定义行数进行打印。
1.3.2while语句
while(表达式)
{
循环体;
}
判断表达式的值是否成立,如果成立,执行循环体,否则,跳出循环
案例:求1-100之和
1.3.3do while语句
do
{
循环体;
}while(表达式);
总结:while和do while区别
while先判断,再执行,语句至少执行0次
do while先执行,后判断,语句至少执行1次
break,continue
break:1,跳出switch语句 2,跳出循环
Continue:结束本次循环,开始下一次循环
死循环
没有第一个i++就是死循环
1.3.4死循环
for(;1;)
{
;
}
while(1)
{
;
}
1.3.4goto语句
无条件跳转语句,一般格式为goto语句标号
语句标号:按照标识符命名规范
数组
思考?定义1000个int型变量,挨个定义,变量名;
- 概念
一组数据类型相同的元素的集合
特点:(1)数据类型相同(2)地址连续
打印地址%p
- 定义
存储类型 数据类型 变量名;
int a;//定义了一个整型变量
存储类型 数据类型 数组名[元素的个数];
int a[5];//定义了一个整型数组
存储类型:auto,static,extern,register
数据类型:数组中每一个元素的数据类型
数组的数据类型:数据类型 [元素个数]
数据类型:去掉变量名就是数据类型
数组所占内存空间=sizeof(数据类型)*元素个数
数组元素个数不同,数据类型也不同
注意:
- 数组名代表整个数组
- 数组名也代表数组首元素的地址
注意:元素个数必须是一个确定的数
- 初始化
3.1部分初始化
int a[5] = {1,2,3};
a[0] = 1, a[1] = 2, a[2] = 3, a[3] = ? a[4] = ?
在进行部分初始化的时候,未初始化部分的值为0,因此,利用这一特点可以给数组中元素清零。
int arr[100]= {0};
3.2全部初始化
int a[5] = {1,2,3,4,5};
int a[ ] = {1,2,3};//在进行全部初始化的时候,数组元素的个数是可以省略的,由后面赋值的具体个数来决定
总结:
- 数组定义在函数体内,没有初始化,其值为随机值
- 数组定义在函数体外,没有初始化,其值为0
- 如果数组被static修饰,没有初始化,其值为0
- 数组的访问
数组名[下标];
注意:下标是从0开始
案例:
- int a[5];
a[5] = {1,2,3,4,5};//error
- int a[5];
a[] = {1,2,3,4,5};//error,数组名代表数组首元素地址
- int a[ ] = {0};//表示你这个数组中只有一个元素,没有意义,但是正确的
- int a[ ] ;//数组的元素个数必须是一个确定的数
作业:
- 打印等腰三角形
- 9x9乘法表
- 选做(打印菱形)
Day4
冒泡排序
字符数组
字符串处理函数
二维数组
- 冒泡排序
思想:从左到右,俩俩依次比较,如果前一个数比后一个数大的话,交换位置,否则不变
int temp = 0;
temp = a[i];
a[i] = a[i+1];
a[i+1] = temp;//交换
- 字符数组
字符数组的本质:其实就是字符串,以’\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’};//字符数组清零
2.1字符数组的输出函数
%s:字符串
puts(数组名);
功能:将数组中的内容输出打印到终端,并且自动加换行
注意:遇到’\0’结束
2.2字符串的输入函数
gets(数组名);
功能:将键盘接收到的字符串存放在数组中,并在末尾自动加’\0’
注意:不会越界检查,所以输入时不要越界
回车被直接使用
2.3总结
2.3.1scanf和gets区别
gets是以回车作为结束符,但是scanf以回车,table,空格作为结束符
缓冲区:
gets:当完成字符串的输入后,会自动清空缓冲区内容
scanf:当完成字符串的输入后,缓冲区会遗留空格,回车,tab
注意:
gets首先会检查缓冲区有没有内容,如果有,直接拿来使用,否则输入
scanf是标准的输入函数,只能通过键入方式
2.3.2printf和puts的区别
puts会自动添加换行,而printf不会
- 字符串的处理函数
strlen, strcpy, strcat, strcmp
头文件:#include<string.h>
3.1求字符串的长度
strlen(数组名);
功能:求字符串的长度
返回值:求得的字符串的实际长度,不包含’\0’
strlen和sizeof的区别:
- strlen是求得的字符串的实际长度,不包含’\0’,而sizeof求得的是整个空间的大小
- sizeof是运算符,strlen是库函数
案例:求字符串的长度,不使用库函数
3.2字符串的拷贝函数
strcpy(数组1,数组2/字符串);
功能:将数组2的内容拷贝到数组1,包含’\0’,相当于完全拷贝
注意:数组1的容量大于数组2
strncpy(数组1,数组2/字符串2,n);
功能:将数组2的前n个字符拷贝到数组1中
注意:拷贝的字符不包含’\0’
3.3字符串的连接函数
strcat(数组1,数组2/字符串2);
功能:将数组2或者字符串2的内容连接到数组1中,数组1的’\0’会被覆盖
注意:数组1的容量大于数组2
strncat(数组1,数组2/字符串2,n);
功能:将数组2的前n个字符,连接到数组1中
3.4字符串的比较函数
strcmp(数组1/字符串1,数组2/字符串2);
功能:比较字符串1和字符串2的大小
返回值:
大于0:字符串1 > 字符串2
等于0:字符串1 == 字符串2
小于0:字符串1 < 字符串2
比较规则:
从左到右依次对字符串的ASCII码进行比较,直到遇到不同的ASCII码或者遇到’\0’结束
案例:
实现字符串的拷贝,不使用库函数
4.二维数组
数组:一组数据类型相同的元素的集合
整型数组:一组int型的数集合在一起
字符数组:一组char型的字符集合在一起
二维数组:一组(数组类型的)元素集合在一起
4.1概念
本质:表示元素为一维数组的数组(数组的数组)
数组特点:(1)数据类型相同(2)地址连续
4.2定义
存储类型 数据类型 数组名[元素个数];
存储类型 数据类型 数组名[行数][列数];
行数:有几个一维数组
列数:一维数组中有几个元素个数
int a[2][3];//定义了一个二维数组,数组的长度为2,每一个元素又是长度为3的int型数组
4.3数组的初始化
4.3.1部分初始化
int a[2][3] = {1,2};
4.3.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有俩个一维数组,但每一个一维数组中有几个元素,不能确定
4.4数组的访问
案例:打印杨辉三角 8x8
1
1 1
1 2 1
1 3 3 1
作业:
- 求一组数据中的最大值和次大值(不使用冒泡排序)
- 打印字符数组中每一个元素
- 实现字符串的连接(不使用库函数)
Day5
二维字符数组
函数
指针
- 二维字符数组
char dtr[30] = “hello”;
scanf(“%s”,dtr);
printf(“%d\n”,dtr);
存储类型 数据类型 数组名[行数][列数];
行数:(一维字符数组的个数)字符串的个数
列数:(一维字符数组中字符的个数)每一个字符串最多存放几个字符
char str[3][20];//定义了一个长度为3的数组,每一个元素都是长度为20的字符数组
案例:实现字符串的输入输出
- 函数
一个独立的功能模块
2.1为什么要使用函数
2.1.1是程序变得模块化
2.1.2.提高代码的复用率
2.2函数的分类
2.2.1库函数
printf,scanf,strlen
2.2.1.1引入头文件
#include<stdio.h>
#include<string.h>
2.2.1.2调用函数
strlen(str);
函数名(实际参数列表);
注意:参数有多少个,数据类型是什么,返回值
2.2.2自定义函数
2.2.2.1函数定义
存储类型 数据类型 函数名(形式参数列表)
{
函数体;
返回值;
}
存储类型:auto,static,extern,register
数据类型:函数返回值的数据类型
函数名:见名知意
形式参数列表:要实现功能所需要的参数,需要调用者传入(1,需要几个参数2,每个参数需要什么类型)
函数体:具体实现的功能
返回值:如果没有返回值,可以省略,不需要写return,数据类型void,如果有,有且仅有一个
2.2.2.2调用函数
函数名(实际参数列表)
注意:
- 需要将实参的值传递给形参,实参的个数和数据类型必须和形参一致
- 实参可以是变量,常量,表达式,必须是一个确定的值
- 实参和形参是俩块独立的内存空间
- 传参实际上是将实参的值拷贝给形参
- 形参是局部变量,在函数调用的时候被定义,函数调用结束后,释放空间
案例:
2.2.2.2.1调用时
2.2.2.2.2调用结束
2.2.3函数声明
如果函数没有在main函数之前,就需要在main函数之前进行声明
声明:将函数头直接拷贝至main函数之前,然后加分号
未加声明之前:
加上声明之后:
声明的作用:帮助编译器做语法检查
案例:封装函数实现+ - * /
- 输入
- 选择
- 调用模块
- 打印
- 指针
3.1什么是指针
指针是一种数据类型,是一种保存地址的数据类型
int a;//int:用来保存整型数的数据类型
char c;//char:用来保存字符的数据类型
float b;//float:用来保存小数的数据类型
指针 d;//指针:用来保存地址的数据类型
3.2什么是地址
内存分配的最小单位是字节,每一个字节都有一个编号,我们把这个编号叫做地址
地址的本质:内存单元的编号
指针:指针就是地址
指针的本质:内存单元的编号
3.3指针变量
int a;//整型变量
float b;//浮点型变量
char c://字符型变量
指针变量:专门用来保存地址(内存单元的编号)的变量
3.4指针的定义
存储类型 数据类型 *指针变量名;
int *p;
存储类型:auto,extern,static,register
数据类型:指针所指向的数据类型(去掉*和变量名,剩下的就是指针所指向的数据类型)
指针的数据类型:数据类型 *//int *
注意:
在所有32os,所有的指针占4个字节
在所有64os,所有的指针占8个字节
3.5赋值
char *p = &a;
char *p = NULL;
p = &a;
注意:对指针赋值时,一定要注意数据类型的匹配
思考?
- 什么是指针
是一种数据类型,保存地址的数据类型
- 地址是什么
字节(内存单元)的编号
- 什么是指针变量
专门用来保存地址(内存单元的编号)的变量
- 指针变量如何定义
存储类型 数据类型 *指针变量名
int *p;
- 指针变量赋值后能干什么
指针指向谁,就是保存谁的地址,赋值之后可以操作地址里面的元素。
3.6空指针
没有指向的指针(值为0的指针,就认为该指针没有指向)
注意:0号地址,禁止操作
要操作的话就改变指针指向
3.7野指针
不知道指向哪里的指针
局部变量没有初始化,其值是随机值
局部指针变量没有初始化,就是野指针
如何避免野指针的出现?初始化为NULL
int *p = NULL;
很危险!!!!!
Day6
- 值传递
案例:用子函数的形式,实现俩个整数的交换
- 地址传递
- 二级指针
3.1概念
指针的指针
二级指针的内存空间存放的是一级指针的地址
3.2定义
定义一级指针:存储类型 数据类型 *指针变量名;
数据类型:指针指向的数据类型
存储类型 数据类型 **指针变量名;
总结:
- 指针的数据类型,去掉变量名剩下的就是
int *p;//int *
int **pp;//int **
int ***ppp;//int ***
- 指针指向的数据类型,去掉一个*和变量名就是所指向的数据类型
int *p;//int
int **pp;//int *
int ***ppp;//int **
- 指针所能访问的空间大小,由指针所指向的数据类型决定
- int a;
int *p = &a;//*p所能访问的空间大小是4个字节
- char b;
char *p = &b;//*p所能访问的空间大小是1个字节
- int*p;
int **pp = &p;//**pp所能访问的空间大小是4个字节
- 指针和一维数组
4.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(指向的数据类型)
注意:
- 指针的算数运算只有在操作连续的内存空间时才有意义
- p是指针变量,以上的方法也适用于常量,但是++,--除外
(数组名:1.数组首元素地址(指针常量))
4.2通过指针常量来访问
数组名:指针常量,不能++,--
4.3通过指针变量来访问
访问数组:
总结:数组名和指针变量的本质区别:数组名是指针常量
案例1:
printf:右结合
案例2:
void input(int *p,int count)
- 冒泡排序
5.1指针的指向没有发生改变
5.2指针指向发生改变
作业:
- 用子函数的形式实现字符串的拷贝
- 用子函数的形式实现字符串的连接
(都使用指针移动方式)(不使用库函数)
Day7
- 指针和二维数组
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]:第一个一维数组的第一个元素的地址
- a,&a[0],&a[0][0]的值是一样的,但是意义不一样
a:int (*)[3]:指向一维数组的指针 a[0]:int *:指向一个整型变量 a[0][0]:int类型
- 为什么a不是int **?
a+1移动了一个数组(12byte)如果是int **,加1,移动4byte
- a指向a[0],a[0]又是一个一维数组,所以说a指向了一个一维数组
案例:
数组的输入,输出,排序,倒置,求最大值。(封装函数)一维数组
- 数组指针与指针数组
2.1数组指针
2.1.1概念
指向数组的指针
2.1.2定义
存储类型 数据类型 (*指针变量名)[元素个数];
存储类型: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相当于整个数组
2.1.3数组指针与一维数组
注意:数组指针几乎不操作一维数组,更多的操作二维数组,因为指针访问连续的内存空间才有意义,如果是一维数组,p+1就会越界
2.1.4数组指针与二维数组
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就是数组指针,
定义一个数组指针,也指向二维数组的首元素,首元素是一维数组
2.2指针数组
2.2.1概念
元素为指针的数组
2.2.2定义
存储类型 数据类型 *数组名[元素个数];
数据类型 *:数组中元素的数据类型
int *arr[3];
定义了一个数组,数组名叫arr,有三个元素,每一个都是int *
2.2.3指针数组与二维数组
作业:
查找指定字符在字符串中第一次出现的位置
最后一次出现的位置
封装子函数实现
去掉break,就是最后出现的位置
Day8
- const修饰的指针
const:只读
const用来修饰变量,修饰的变量只能读,不能写
案例:const是否在常量区
const修饰的指针:
总结:
- const int *p = &a;//const修饰的是值,*p不能被改变
- int *const p = &a;//const修饰的是地址,p不能被改变
- const int *const p =&a;//const修饰值和地址,*p和p都不能被改
int const *p;
左值右址
const在*左边:*p不能被改变
const在*右边:p不能被改变
- main函数传参