1.引入
int a;(变量名,变量的值,变量的地址)
a = 1024;//把数值1024存放到变量a对应的地址中
b = a;//取变量a的值赋值给b
=>在C语言中,任意一个变量名,都有两层含义
(1)代表该变量的存储单元地址 左值
(2)代表该变量的值 右值
对于一个变量的访问,只有两种情况:
(1)把一个值写到变量的地址中 写
(2)从变量的地址中取值 读
如果我们知道一个变量的地址,是不是就可以通过该变量的地址去访问这个变量
可以
如果通过这个地址去访问这个地址对应的对象?
指针
对象的访问有两种方式
直接访问:通过对象名去访问
a = 1024;
b = a;
缺陷?
作用域的范围内才能访问
间接访问:
通过对象的地址去访问 =》指针
2.什么是指针?
地址:分配给每个变量的内存单元都有一个编号,这个编号就是我们说的地址。按字节来编号。
在C语言中,指针的概念与地址差不多,可以认为指针就是一个地址编号,
一个变量的地址,也可以成为变量的“指针”
通过一个对象的地址来间接访问这个对象
首先是不是要先保存这个对象的地址
3.指针变量
指针变量就是一个变量,只不过它是用来保存另外一个对象的地址
指针变量的定义:
指向的变量的类型 *指针变量名(*用来标识该变量是一个指针变量)
"指向的变量的类型" :
“指向” :保存谁的地址,就指向谁
p保存a的地址,p指向a
eg:
假如我们要定义一个指针变量p,来保存a的地址
int a;
int *p;
指针变量的类型:
它所指向变量的类型 *
4.与指针相关的操作符
&(取地址符) :单目运算符
&对象
取某个C语言对象(变量,数组,函数...)的地址
p = &a;
*(指向运算符)
*地址 《=》地址对应的那个变量
int a = 5;
int *p = &a;
*&a <=> a
=>*&直接约掉
&*&a <=> &a
int a;
typeof(a) =>int
int *p;
typeof(p) =>int*
练习:
写一个函数将两个变量的值交换
void change(int x,int y)
{
int t;
t = x;
x = y;
y = t;
}
void change2(int* x,int* y)
{
int t;
t = *x;//*x =*&a
*x = *y;
*y = t;
}
int main()
{
int a = 4,b = 5;
printf("before:a = %d b = %d ",a,b);
change2(&a,&b);
printf("after:a = %d b = %d ",a,b);
}
5.指针变量作为函数参数
传的是 “值”
C语言中函数参数的传递只能是 “传值调用”
把实参的值 赋值给 形参
int main()
{
int *p;//定义了一个指针变量,p会对应一个存储单元
*p = 1024;
printf("%d ",*p);
}
6.数组与指针
数组元素与普通变量是一样的,也有自己的地址。数组元素有左值和右值
并且数组元素间的地址是连续的,数组名可以代表首元素的地址
int a[10];
假如,我要定义一个指针变量p,来保存a[0]的地址
typeof(a[0]) *p;
int *p;
p = a;//p = &a[0]
*p = 1024;//a[0] = 1024
能不能通过p去访问a[1]
可以
*p <=> *&a[0]
指针作加减:
p+i (p是一个指针,i是一个整数)
是加减i个指向单元的长度
p+1 => 往后面移了一个单元
p == &a[0]
p+1 => 往后面移了一个int
p+i 《=》 &a[i] 《=》 a+i
*(p+i) <=> a[i] 《=》 *(a+i)
练习:
int a[5];
定义一个指针变量p保存a[0]的地址,把100赋值给a[0],有几种表示方法? 4.c
int *p = &a[0];
a[0] = 100;
*p = 100;
*a = 100;
*&a[0] = 100;
**&p = 100;
p[0] = 100;
结论:
p[i] <=> *(p+i) =>*&a[i] <=> a[i]
eg:
数组的赋值 5.c
int a[5];
for(i = 0;i < 5;i++)
{
scanf("%d",a+i);
}
int *p = &a[2];
p[2] =>*(p+2) =>*(&a[2]+2) =>*&a[4] =>a[4]
7.再论数组名与指针
数组名是一个常量指针,是指针就有类型
* 数组名是一个什么类型的指针?
指针的类型决定指针作加减的时候,移动的单位长度
☆数组名可以看做是指向数组第一个元素类型的常量指针,数组名在数值上为第一个元素的地址
int a[10];
a当做是一个数组名,代表整个数组
typeof(a) =>int[10]
sizeof(a) =>40
a当做指针来看
typeof(a) =》 typeof(a[0])*
=》int*
在数值上 &a[0]
练习:
1.
int a[10] = {1,2,3,4,5,6,7,8,9,0};
printf("a = %p a+1 = %p &a+1 = %p ",a,a+1,&a+1);
a =>&a[0]
a+1 =>&a[0]+1 =>&a[1]
&x 是一个指针,保存的x的地址
=》这个指针是指向x
typeof(&x) =>typeof(x)
&a =>是一个指针,保存的a的地址
这个指针是指向a
=>这个指针的类型
=>typeof(a)*
=> int[10]*
&a+1 =>往后移了一个int[10],也就是40个字节
2.
int b[3][4] = {1,2,3,4,5,6};
printf("b = %p b+1 = %p &b[0]+1 = %p &b+1 = %p ",b,b+1,&b[0]+1,&b+1);
b 是一个数组名,是一个指向第一个数组元素的常量指针
&b[0] 第0行的首地址 数值上 &b[0][0] x
b+1 =>&b[0]+1 =>&b[1] 第1行的首地址 x+16
&b[0] =》指针
typeof(&b[0]) =>typeof(b[0])* =>int[4]*
&b[0]+1 =>&b[1] 第1行的首地址 x+16
&b+1
typeof(&b) =>typeof(b)*
=>int[4][3]*
&b+1 =>往后移了1个int[4][3] x+48
8.多维数组与指针
数组名可以看做是指向数组第一个元素类型的常量指针,数组名在数值上为第一个元素的地址
假设有
int a[3][4];
这个二维数组a是不是可以看成是元素为int[4]的类型的一维数组
a[0] a[1] a[2]
*(a+i) <=>a[i] =>指向的是一个一维数组
再次指向一个才能指向二维数组的元素
*(*(a+i)+j) <=>a[i][j]
=>
a+i :a是只能看成指针
typeof(&a[0]) =>typeof(a[0])*
=>int[4]*
a<=> &a[0]
a+i=>&a[i]
*(a+i) =>*&a[i] =>a[i]
*(a+i)+j =>a[i]+j
*(a+i) 绝对是一个指针
typeof(*(a+i)) =>typeof(a[i])
=>typeof(a[i][0])*
=>int*
*(*(a+i)+j) =>*(a[i]+j) =>*(&a[i][j]) =>a[i][j]
===============
int a[3][4];
表达式 表达式的类型 表达式的含义 表达式的值
a+i (&a[i]) int[4]* 第i行的首地址 &a[i][0]
*(a+i)+j int* 第i行第j列的地址 &a[i][j]
*(*(a+i)+j) int 第i行第j列的元素 a[i][j]
作业:
下面例子是我之前做的,错误示范,后面才是正确的答案
int a[3][4];
表达式 表达式的类型 表达式的含义 表达式的值
a int[4][3] 一个int[4][3]类型的数组 未知&a[0][0]
a[0] int[4]*int[4] 第一行的首地址代表第0行 &a[0][0]
&a[0][0] intint* a[0][0]的起始地址 未知&a[0][0]
a+1 int[4]* 第二行的起始地址 &a[1][0]
&a[1] int 第二行首地址的地址值第1行的首地址 未知&a[1]
&a int 数组首地址 未知
&a+1 int 数组a再加上a数组的大小 数组a的起始地址加上a数组的大小
a[1]+2 int [4]* 从a[1][0]的地址开始向后移动32字节 从a[1][0]的地址开始向后移动32字节
*(a+1)+2 int* &a[1][2] 未知
&a[1][2] int a[1][2]的存放地址 未知
*(a[1]+2) 未知 从a[1][0] 地址开始向后移动32字节开始的指向的那个元素 从a[1][0] 地址开始向后移动32字节开始的指向的那个元素
*(*(a+1)+2) int a[1][2]的值 要看输入进a[1][2]的值为多少
答案
作业:
int a[3][4];
表达式 表达式的类型 表达式的含义 表达式的值
a typeof(a) =>int[4][3] 代表整个数组 &a[0] =>&a[0][0]
当做指针
int[4]*
a[0] a[0]又是一个数组(一维数组) 代表第0行 &a[0][0]
int[4]
指针:
typeof(a[0][0])*
=>int *
&a[0][0] int* a[0][0]的地址 &a[0][0]
a+1 &a[0]+1=>&a[1] 第1行的首地址 &a[1] =>&a[1][0]
int[4]*
&a[1] int[4]* 第1行的首地址 &a[1] =>&a[1][0]
&a &a肯定是一个指针 &a:整个数组的地址 &a =>&a[0] =>&a[0][0] 数值
int[4][3]*
&a+1 int[4][3]* 整个数组a的地址的下一个地址 数值上比&a多48
a[1]+2 &a[1][0]+2 =>&a[1][2]
int* a[1][2]的地址 &a[1][2]
*(a+1)+2
&a[1][2] int *
*(a[1]+2) *(&a[1][2]) =>a[1][2] 元素a[1][2] a[1][2]
int
*(*(a+1)+2) int 元素a[1][2] a[1][2]
`
9.指针数组与数组指针
(1)指针数组
是一个数组,里面的每一个元素都是指着类型
int*p[4];//定义了一个数组,数组名为p里面有4个元素,每个元素都是int*类型
(2)数组指针
一个指向数组的指针,称为数组指针
int a[4];//定义了一个数组
要定义一个指针变量p来保存数组a地址,如何定义?
typeof(a) *p;
=>int[4] *p;
=>int(*p)[4]
p = &a;
p+1 => 在数值上比p多了16
区别:
int a[10];
int(*p)[10] =>*((*p)+i) =>int
int*p[10] => *(*(p+i)) =>int
10.字符串与指针
c语言中没有字符串这个类型。C语言的字符串是通过char*来实现
c语言中字符串 是用“” 引起来的一串字符,并且字符串后面默认加上' '(字符串结束符)
只需要保存字符串的首地址,从首地址开始找找到第一个