第八章:指针
*:1:标志、乘、指针取内容值
一、指针变量定义
C语言有两种变量:其中变量(普通变量)存储内容值;地址变量(指针变量)存储地址值
-
定义的格式:类型名 *指针变量名;
-
如:int a, b, *p1, *p2;
-
在定义指针变量时,指针变量名前的“*”表示现定义的是一个指针类型的变量。星号并不是指针变量名的一部分,只有一个标志
-
指针变量专门用来存地址,禁止将 个整型值直接 一个整型值直接 赋给一个指针量
-
-
指针变量的引用
-
“&”取地址运算符,通过&运算符可以取出普通变量的地址
-
“*” 指针运算符,*可以取出指针变量所指向的普通变量的值,(间接引用普通量)
- 可以通过赋值使一个指针变量“指向”某一普通变量( 指针变量=&普通变量)
int a=10; int *p; p=&a;
- 在C语言中正确的做法是先让指针变量指向一个确定的存储单元后,再通过该指针变量引用它所指向的存储单元
int *p; *p=200; /*危险*/
- 变量名(普通变量、指针变量)都表示其存储单元内的值
p1=p2; /*p1指向了p2 所指向的单元*/ *P1=*P2 /*读p2的值写给p1*/
-
-
*的作用
-
做乘运算符(双目)
-
做标志
int a=10; int *p=&a;
- 做指针运算符
int a=10; int *p=&a; *p=20;
-
-
若指针变量p指向变量a,即将变量a的地址赋给了指针变量p
如:int a=20,*p=&a
等价表
A、*p<=>a
B、p<=>&a
C、&*p〈=〉&a〈=〉p
D、*&a <=> *p a <=>
E、(*p)++ a++ /*都是给变量a的值加1*/
(*p)-- a--
++(*p) ++a ++*p
--(*p) --a --*p
- 所有的指针变量在内存中分配的字节数相同,都为二字节。sizeof()
float *p1;
float *p2;
sizeof(p1) /*2*/
sizeof(p2) /*2*/
-
案例:
- 指针变量声明后需要初始化才能使用
/*给出一个错误案例*/ #include<stdio.h> 请输出下列程序的结果 void swap(int *p1,int *p2) { int *temp ; *temp=*p1; *p1=*p2; *p2=*temp;} main() { int a,b; int *p1=&a,*p2=&b; scanf("%d%d",p1,p2); swap(p1,p2); printf("%d,%d",*p1,*p2); }
- 形参用完后会被释放掉
#include<stdio.h> 请输出下列程序的结果 void swap(int *p1,int *p2) { int temp; temp=*p1; *p1=*p2; *p2=temp; } main() { int a,b; int *p1=&a,*p2=&b; scanf("%d%d",p1,p2);/*输入5,2*/ swap(p1,p2); printf("%d,%d",*p1,*p2);/*输出5,2*/ }
二、一维数组与指针变量
(1) int fun(int a[10])<=>int fun(int *a )<=>int fun(int a[ ])
若数组做为形参,则将数组名做指针变量来处理
- 指向数组元素的指针变量
由于数组元素与普通一样,所以定义指向数组元素的指针变量
与定义指向普通变量的指针变量完全一一样
如:
int s[10] a b; s[10],a,b;
int b[3][6];
int *p;
p=&a;
p=&s[2];
p=&b[2][4];
-
指向一维数组的指针变量
-
在C语言中规定:数组名代表数组的首地址,而且是一个地址常量,如:int a[10];
int *p; P=a; <=> P=&a[0]; -
当指针变量指向数组中的某一个元素时,指针变量加1后指向数组的下一个元素,指针变量减1时指向数组中前一个元素,如:float a[10]; float *p; p=&a[4]; 则p-3 指向 a[1]
-
当指针变量指向数组时,下标运算([ ])用于数组也可用于指针变量后
int a[N],*p=a; p+i<=>a+i<=>&a[i] *(p+i)<=>*(a+i)<=>a[i]<=>p[i] ------------------------- *p++<=>*(p++)
举例指针在数组中的使用:
main() { int *p,a[3],I; p=a; for(i=0;i<3;i++) scnaf("%d",p++); printf("\n\n"); for(p=&a[0];p<a+3) printf("%d ",*p++); }
- 若两个指针变量指向同一个数组,则这两个指针变量可以进行大小比较
char s[10]; char *p1=s+3,*p2=&s[7]; p1>p2 =>0 p1<p2 =>1 p1-p2=>-4 p2-p1=>4
-
在形参中的数组实际上是一个指针变量,并不是真正的数组,因为该“数组名”的值是可以改变的,而真正的数组名的值是不能改变的
-
若形参是数组或指针变量,则在函数中可以通过该形参改变实参的值
-
三、指向多维数组的指针变量
&与*互逆
*与[]等价
&与[]互逆
指向二维数组的指针,先看是列指针还是行指针
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fCYyRfmO-1646233686950)(C:\Users\tengfei\Pictures\Camera%20Roll\2022-02-09_21-03-31.png)]
-
指向多维数组元素的指针变量
如: int a[3][4];
-
a+1是跳过一行。因为二维数组名是行指针,加1是跳过一行不是一个元素
-
只有列指针才是“真正”指向元素。即指向某一个元素的存储单元
-
一维数组名表示的是列指针;二维数组名表示的是行指针
若a是一个二维数组,则有:
-
a+i是行指针,即指向的是一整行。若对它加1则指向下一行
-
*(a+i) 和a[i]一样,都是一个列指针即指向的是一个元素
-
*(a+i)+j和a[i]+j一样,都表示元素a[i][j]的地址。即与&a[i][j]等价
-
*(*(a+i)+j)
、*(a[i]+j)
、(*(a+i))[j]和a[i][j]一样,都表示元素a[i][j]
-
如:int a[3][4]; int *p=&a[0][3];
-
则:p+1指向元素a[1][0]; p-2指向元素a[0][1]
-
常用于取二维数组a元素地址的方式:&a[i][j]、a[i]+j、*(a+i)+j
main() { int a[3][3]={1,2,3,4,5,6,7,8,9},*p; for(p=a[0];p<a[0]+9;p++) /*a[0]等价于&a[0][0],其中&与[]抵消*/ printf("%d",*p); }
-
2. 指向由m个元素组成的一维数组的指针变量
定义指向由m个元素组成的一维数组的指针变量的格式:基类型 (*指针变量名)[m];
如:`int a[5][7];int (*)[7]; int (*p)[7];p=a;`
```c
main()
{
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12} ;
int (*p)[4];
p=a;
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
printf("%d",p[i][j]);
printf("\n");
}
}
四、指向字符串的指针变量
一个字符串就是第一个字符的地址
字符串常量:C语言对字符串常量是按首地址处理字符串常量
1) char str[]="China" /*合法,有双引号做标识可省略花括号*/
2) char *p=“China”;/*合法,字符串就是第一个字符的地址,用P指针来接收*/
3) str="Chinese"; /*不合法,str是个常量,不可更改*/
4) p="Chinese"; /*合法,p改变了它的指向*/
5) char *p={"China"} /*不合法,加了花括号系统不会认为是字符串了,指针变量得地址而不是花括号*/
6) char str[]={"China"} /*合法,有双引号做标识可省略花括号,也可不省略*/
main()
{
char s[20]="abcdefGHI";
char *p="1234567";
printf("%s\n",s+2);
printf("%s\n",p+5);
printf("%s\n",strcat(s+3,p+4));
printf("%d\n",strlen(p+2));
printf("%s\n",strcpy(p+3,s+9));
printf("%s\n",s);
scanf("%s",s+3); /*输入:ABC DEF<回车>*/
printf("%s",s);
}
五、指向函数的指针变量
函数名与数组名一样,是起始地址,而且是一个地址
定义:类型名 (*指针变量名)(函数参数列表);
类型名是指函数返回值类型
例如:int (*p)(int,int);
类型名 (*指针变量名)(); //定义指向函数的指针变量
类型名 普通变量名; //定义普通变量
类型名 数组名[]; //定义数组
类型名 *指针变量名;//定义指针变量
类型名 函数名( ) //定义函数
{。。。。}
类型名 (*指针变量名)[M]; //定义指向行的指针变量
int min(int a,int b)
{ return a>b?b:a;}
int max(int a,int b)
{ return a>b?a:b ; }
main()
{ int x=6,y=10;
int (*p)(int,int);
p=max;
printf("%d",max(x,y));
printf("%d",p(x,y)); /*等价于printf("%d",(*p)(x,y));*/
p=min;
printf("%d",min(x,y));
printf("%d",p(x,y));
}
注意:
-
在定义指向函数的指针变量时,要注意有两个小括号必须要有,不需要定义形参
-
单独的函数名代表该函数的首地址(函数的入口地址)
-
函数的指针变量只能指向函数的入口处(函数的首地址),不能指向函数中的某条指令。(另对指向函数的指针变量加1是没有意义的)。
-
给指向函数的指针变量赋值时,只写函数名即可,不必写参数。
六、返回指针的函数
定义:类型名 *函数名(形参列表){}
int *fun(int *x,int *y)
{
if(*x<*y)
return x;
else
return y;
}
main()
{
int a=7,b=8,*p,*q,*r;
p=&a; q=&b;
r=fun(p,q);
printf("%d,%d,%d\n",*p,*q,*r);
}
七、指针数组和指向指针的指针变量
- 指针数组
若一个数组的所有元素均为指针类型(地址),则称为指针数组
格式:类型名 *数组名[常量表达式];
例:int *s[10];
它的每个 个 类 元素都是一个指针类型(地址),即它的每个元素都 相当于一个指针变量
main()
{
char ch[3][4]={"123","456","78"},*p[3];
int i;
for(i=0;i<3;i++)
p[i]=ch[i];
for(i=0;i<3;i++)
printf("%s",p[i]);
}
/*输出:12345678*/
- 指向指针的指针变量
用来存放指针变量地址的指针变量称为指向指针的指针变量
格式:基类型名 **指针变量名;
如:
int a=3;
int *p=&a;
int **k=&p;
/*
* 则:*k得到变量p(变量a的地址)
* **k 得到变量a的值(a的数据3
*/
八、空指针
空指针所指向的房子不存在
-
指针变量可以有空值,即指针变量不指向任何变量,不指向任何有用的存储单元
-
在系统中已将NULL定义为0,即NULL的值为0
-
当一个指针变量的值为空指针时,我们不能引用它所指向的存储单元
#include<stdio.h>
main()
{
int a[]={1,2,3,4,5,6,7,8,9,10,11,12};
int *p=a+5,*q=NULL;
*q=*(p+5);
printf("%d %d\n",*p,*q); /*程序报错,*q没有指向任何内存空间,所以无法进行赋值*/
}
如:
int a=3;
int *p=&a;
int **k=&p;
/*
* 则:*k得到变量p(变量a的地址)
* **k 得到变量a的值(a的数据3
*/
八、空指针
空指针所指向的房子不存在
-
指针变量可以有空值,即指针变量不指向任何变量,不指向任何有用的存储单元
-
在系统中已将NULL定义为0,即NULL的值为0
-
当一个指针变量的值为空指针时,我们不能引用它所指向的存储单元
#include<stdio.h>
main()
{
int a[]={1,2,3,4,5,6,7,8,9,10,11,12};
int *p=a+5,*q=NULL;
*q=*(p+5);
printf("%d %d\n",*p,*q); /*程序报错,*q没有指向任何内存空间,所以无法进行赋值*/
}