第六章 指针
目录
认识
指针=地址
*(&a)取值运算符:把他后面跟的内存地址中的数据取出来
如何定义一个指针变量
int *p;——*只产生在指针变量定义或者声明的时候
如何使用一个指针变量
*的运算作用
变量访问的两种方式
- 直接访问——直接按变量名访问
- 间接访问——将变量i的地址存放在另一个变量中,然后通过变量来找到变量i的地址,从而访问i变量
int a =10;
int *p;
p=&a;
为什么需要指针
封装函数,实现两个数的交换
#include<stdio.h>
void changedata(int *pdata,int *pdata2)
{
int tmp;
tmp=*pdata;
*pdata=*pdata2;
*pdata2=tmp;
}
int main()
{
int data=10;
int data2=20;
printf("交换前data=%d,data2=%d",data,data2);
changedata(&data,&data2);
printf("交换后data=%d,data2=%d",data,data2);
return 0;
}
指针指向固定区域zz
#include<stdio.h>
int main()
{
int a =10;
printf("address of a is 0x%p\n",&a);
int *p=(int*)0x000000000061FE33;
printf("p=0x%p\n",p);
return 0;
}
指针引用数组
- 定义一个指针变量指向数组
int a[10]={12,432,543213,23,12,3432,123,12,43,232};
int *p;
p=&a[0];
- 在c语言中数组名代表数组中首元素的地址,因此下面两个语句等价
p=&a[0];//p是a[0]的地址
p=a;//p的值是数组a的首元素(即a[0])的地址
- 指针的增量和数组的关系
printf("首元素是%d\n",*p);
printf("1元素是%d\n",*(p+1));
printf("2元素是%d\n",*(p+2));
- 通过指针引用数组元素
- 下标法
- 指针法
- 偏移
- 取内容
见怪不怪的内容 :
- 指针当作数组名 ,下标法访问
int arr[3]={1,2,3}; int *p=arr; printf("%d",p[2]);
- 数组名拿来加
数组名和指针的区别
arr++不可行 指针常量不能++,指针变量可以++
8个字符表示一个地址
两种方法效率对比
系统在使用数组下标对数组成员变量进行访问时,开销较大,指针的访问效率时远远大于数组名的访问效率的。
但是只有在指针正确访问时,才成下标法更有效率。
下标法更容易理解,在可读性方面,也更加具有优势,具体怎么选择,也没有一定的说法
练习:
函数封装数组初始化,遍历
#include<stdio.h>
void intiarray(int *parr,int size)
{
int i;
for(i=0;i<size;i++)
{
printf("请输入第%d个元素\n",i+1);
scanf("%d",parr);
parr++;
}
}
void printarray(int *parr,int size)
{
int i;
for(i=0;i<size;i++)
{
printf("请第%d个元素是%d\n",i+1,*parr);
parr++;
}
}
int main()
{
int array[5];
int size=sizeof(array)/sizeof(array[0]);
intiarray(array,size);
printarray(array,size);
return 0;
}
将数组中的n个元素按逆序存放
#include<stdio.h>
void intiarray(int *parr,int size)
{
int i;
for(i=0;i<size;i++)
{
printf("请输入第%d个元素\n",i+1);
scanf("%d",parr);
parr++;
}
}
void printarray(int *parr,int size)
{
int i;
for(i=0;i<size;i++)
{
printf("请第%d个元素是%d\n",i+1,*parr);
parr++;
}
}
void revangearray(int *parr,int size)
{
int i,j;
int tmp;
for(i=0;i<size/2;i++)
{ /*
j=size-1-i;
tmp=parr[i];
parr[i]=parr[j];
parr[j]=tmp;
*/
j=size-1-i;
tmp=*(parr+1);
*(parr+i)=*(parr+j);
*(parr+j)=tmp;
}
}
int main()
{
int array[5];
int size=sizeof(array)/sizeof(array[0]);
intiarray(array,size);
revangearray(array,size);
printarray(array,size);
return 0;
}
二维数组
父子数组——把二维回归到一维
int a [3][4]举例 a[0]既是数组名也是子数组的首地址
&a[0][0]=a[0]
a是 行的地址 a+1是整行加1到下一行
a[0]是子数组的地址
*a = a [0] a[0]=*(a+1) a[0]+1=*(a+1)+1
#include<stdio.h>
int main()
{
int arr[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12,}};
/*printf("arr是父亲地址;%p,偏移1后是%p\n",arr,arr+1);
printf("arr[0]是子数组地址%p,偏移1后地址是%p\n",arr[0],arr[0]+1);
printf("arr[0]是子数组地址%p,偏移1后地址是%p\n",*(arr+0),*(arr+0)+1);
*/
int i;
int j;
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
{
printf("add:0x%p,data=%d \n",&arr[i][j],arr[i][j]);
printf("add:0x%p,data=%d \n",arr[i]+j,*(arr[i]+j));
printf("add:0x%p,data=%d \n",*(arr+i)+j,*(*(arr+i)+j));
}
}
return 0;
}
数组指针
#include<stdio.h>
int main()
{
int arr[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12,}};//arr++
int i,j;
int *p;//p++
//p=&arr[0][0]
//p=arr;
//能不能定义一个指针,让指针偏移的时候也偏移相应对应大小的数组
//数组指针,定义一个指针,指向一个数组
int (*p2)[4];
p2=arr;
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
{
printf("%d\n",*(*(p2+i)+ j));
}
}
return 0;
}
数组指针才是真正等同于二维数组名
练习
#include<stdio.h>
void tipsinput(int *pm,int *pn)
{
printf("请输入行列值\n");
scanf("%d%d",pm,pn);
puts("done\n");
}
int getthedata(int (*p)[4],int hang,int lie)
{
int data;
data=*(*(p+hang)+lie);
return data;
}
int main()
{
int arr[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12,}};//arr++
int ihang,ilie;
int data;
//1.输入行列值
tipsinput(&ihang,&ilie);
//2.找出对应行列值的那个数
data=getthedata(arr,ihang,ilie);
//3.打印
printf("%d行%d列的值是%d",ihang,ilie,data);
return 0;
}
函数指针
- 定义函数地址——如果在程序中定义了一个函数,在编译时,编译系统为函数代码分配一段存储空间,这段存储空间的起始地址(又称入口地址)称为这个函数的指针
函数名就是地址
- 如何定义一个函数指针变量——和普通变量一样 ——
int a;int *p; char c;char *p; int getdata(int a,int b);int (*p)(int a,int b);
- 使用函数指针——函数调用概念和变量一样——
直接访问:变量名(函数名),间接访问:指针(函数指针)
#include<stdio.h>
void printwelcome()
{
puts("welcome");
}
int main()
{
void(*p)();//定义一个函数指针变量
p = printwelcome;//指向函数
printwelcome();
(*p)();//调用
return 0;
}
练习
有两个整数a和b由用户输入1,2或3.如输入1,程序就给出a和b大者,输入2,就给出a和b中小者,输入3,则求a和b之和
#include<stdio.h>
int getmax(int data1,int data2)
{
return data1>data2?data1:data2;
}
int getmin(int data1,int data2)
{
return data1<data2?data1:data2;
}
int getsum(int data1,int data2)
{
return data1+data2;
}
int datahandler(int data1,int data2,int (*pfunc)(int data1,int data2))
{
int ret;
ret=(*pfunc)(data1,data2);
return ret;
}
int main()
{
int a=10;
int b=20;
int cmd;
int ret;
int (*pfunc)(int data1,int data2);
printf("请输入1(取最大值),2(取最小值),3(求和)\n");
scanf("%d",&cmd);
switch(cmd)
{
case 1:
pfunc=getmax;
break;
case 2:
pfunc=getmin;
break;
case 3:
pfunc=getsum;
break;
}
ret=datahandler(a,b,pfunc);
printf("result=%d\n",ret);
return 0;
}
指针数组
一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都存放一个地址,相当于一个指针变量。下面定义一个指针数组:int *p[4]
由于[]比*优先级高,因此p先与[4]结合,形成p[4]的形式,这显然是数组的形式,表示p数组有四个元素。然后在与p前面的*结合,*表示此数组是指针类型的,每个数组元素(相当于一个指针变量)都可以指向一个整型变量。
tips:不要写成int (*p)[4]——类型名 *数组名[数组长度]
注意和数组指针的区别
#include<stdio.h>
int main()
{
int a=10;
int b=20;
int c=30;
int d=40;
int *p[4]={&a,&b,&c,&d};
int i;
for(i=0;i<4;i++)
{
printf("%d ",*(p[i]) );
}
return 0;
}
指针函数
一个函数可以返回一个整型值、字符值、实型值等,也可以返回指针型的数据,即地址其概念与以前类似,只是返回的值的类型是指针类型而已。
例如“int * a(int x,int y);”,a 是函数名,调用它以后能得到一个 int *型(指向整型数据)的指针,即整型数据的地址。x和y是函数a的形参,为整型。
请注意在*a两侧没有括号,在a的两侧分别为* 运算符和()运算符。而()优先级高于*,因此a先与()结合,显然这是函数形式。这个函数前面有一个*,表示此函数是指针型函数(函数值是指针)。最前面的 int 表示返回的指针指向整型变量。
例题
#include<stdio.h>
int* getposperson(int pos,int(*pstu)[4])//函数指针,返回指针的函数
{
int *p;
p=pstu+pos;
return p;
}
int main()
{
int scores[3][4]={{12,657,56,534},{213,543,324,657},{432,543,765,845}};
int *ppos;
int pos;
printf("请输入你想知道第几个学生的成绩");
scanf("%d",&pos);
ppos=getposperson(pos,scores);
for(int i=0;i<4;i++)
{
printf("%d ",*ppos++);
}
return 0;
}
二级指针
写法 int **p
#include<stdio.h>
int main()
{
int data=100;
int *p=&data;
printf("data的地址是:%p\n",&data);
printf("p保存data的地址是:%p,内容是%d\n",p,*p);
printf("p的地址是:%p\n",&p);
int *pp=&p;
printf("pp保存的p地址是:%p\n",pp);
printf("*pp是%p\n",*pp);
int **p2;
p2=&p;
printf("p2保存的p地址是:%p\n",p2);
printf("*p2是%p\n",*p2);
printf("**p2来访问data:%d\n",**p2);
return 0;
}
和一级指针一样 区别就是:保存的是指针的地址
上一个题目用二级指针的写法
#include<stdio.h>
void getposperson(int pos,int(*pstu)[4],int **ppos)//函数指针,返回指针的函数
{
*ppos=(int *)(pstu+pos);
}
int main()
{
int scores[3][4]={{12,657,56,534},{213,543,324,657},{432,543,765,845}};
int *ppos;
int pos;
printf("请输入你想知道第几个学生的成绩");
scanf("%d",&pos);
getposperson(pos,scores,&ppos);
for(int i=0;i<4;i++)
{
printf("%d ",*ppos++);
}
return 0;
}
二级指针和二维数组的关联
二级指针不能简单粗暴的指向二维数组
#include<stdio.h>
int main()
{
int scores[3][4]={{12,657,56,534},{213,543,324,657},{432,543,765,845}};//int (*p)[4]
int **p;
p =scores;
printf("scores:%p\n",scores);
printf("p=%p\n",p);
printf("*p=%p\n",*p);//*p是野指针不是认为的会编程列地址
printf("*scores:%p\n",*scores);
return 0;
}