概念
有时你申请一个变量(如int x;
),计算机就会随机给你分配一个空间,你可以在那个空间内改变数值。就像计算机给了一个叫做a的小房子,但是只能在里面放int类型的值。比如你输入x=-1
,计算机首先会把小房子找到,然后把-1放在里面。(如果里面有其他值,就拿出来)
那这个小房子的地方在哪里呢,就存放在地址里。每一个变量都有一个地址(即是它在内存中的位置)。所谓指针就是一个箭头→,指向一个地址,然后这个地址里有一个值。
声明
在类型后加上星号(*
)即可,当然指针也分类型的,即:
int* a;//一个指向int类型房子的指针a(当前还是随机值)
char* b;//一个指向char类型房子的指针b
如果你不知道类型,也可以用void* c;
int* a,b;
有些人要问了,如果写成上面那种形式的话,那a,b各是什么类型呢?
答案是a是int类型指针,b是int类型的普通变量。
所以通常申请指针这样写:
int *a,*b;
注意定义指针后指针会指向一个随机的位置,如果你对这个位置进行不正确的操作,就有可能发生内存错误。
操作
赋值
首先我们要学习一个取地址的运算符&
,它可以获取变量的地址。有些小伙伴或许并不陌生了,因为我们平常输入的时候都会用到它:
scanf("%d",&x);
这个句子的意思就是找到x所在的地址,然后把输出的int值放在里面。
你甚至可以输出一个地址,用printf和格式控制符1或者也可以用cout输出,如:
int a;
printf("%p",&a);
cout<<&a;
运行结果:
输出
*a
表示a指向的值。
#include<cstdio>
int main()
{
int *a;
*a=0;
printf("执行 *a=0 后,\n");
printf("指针的地址: %p\n",&a);
printf("指针指向的值: %d\n\n",*a);
*a=1000;
printf("执行 *a=1000 后,\n");
printf("指针的地址: %p\n",&a);
printf("指针指向的值: %d\n\n",*a);
//&a=1000的写法是错误的
}
运行结果:
加减操作
指针可不可以进行加减操作呢,我试了一下,答案是可以的。其实想起来很简单,把地址前移或后退一段地址,是可以的。
#include<cstdio>
int main()
{
int a,*b;
b=&a;
printf("指针指向的地址: %p\n\n",b);
b=b+1;
printf("指针指向的地址: %p\n\n",b);
}
运行结果:
因为b指向的是一个int类型,这里就是相当于把b的地址后移一个int类型,就是4,所以我们结果看到的是0028FF48
,而不是0028FF45
。
所以同理
#include<cstdio>
int main()
{
bool a,*b;
b=&a;
printf("指针指向的地址: %p\n\n",b);
b++;
printf("指针指向的地址: %p\n\n",b);
}
运行结果:
bool类型大小为1,于是。。。
数组
以前我们操作通常都是酱紫。
#include<cstdio>
int n,a[100];
int main()
{
printf("输入:\n");
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
printf("-------------------------\n");
printf("输出:\n");
for(int i=1;i<=n;i++)
printf("%d ",a[i]);
}
学了指针,可以酱紫。
#include<cstdio>
int n,a[100];
int main()
{
printf("输入:\n");
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
printf("-------------------------\n");
printf("输出:\n");
for(int i=1;i<=n;i++)
printf("%d ",*(a+i));
}
运行结果:
指针应用
指针与数组
我们知道,当定义数组int a[100];
时,系统会连续开10个空间,所以:
#include<cstdio>
int main()
{
int a[100]={1,2,3,4,5,6,7,8,9,10};
int *p;
for(p=&a[0];p<&a[0]+10;p++)
printf("%d ",*p);
}
运行中:
当运行到第一次for循环是,p指针指向a[0]
的地址0x28ff10
,其指向的值就是1。
重点:&a[0]+10
是什么意思?
就是a[0]
的地址后移10位的地址,所以p小于&a[0]+10
,本质上就相当于最初的输出的方法。其中a[0]
也可以换做a
,也就是数组的首地址。
#include<cstdio>
int main()
{
int a[100]={100,200,300,400,500,600,700,800,900,1000};
printf("%d\n",*a);
printf("%d\n",*a+5);
printf("%d\n",*(a+5));//这条语句与上条有区别
}
运行结果:
其中*a+5
和*(a+5)
的意义不同,*a+5
的意思是a指向的值加5,所以得到的是100+5=105.*(a+5)
的意思是a的地址加5,也就是&a[5]
,指向的值是600.
指针与函数
有时指针可以看成一个数组。如:
#include<cstdio>
int n;
int sum(int *A){
int _sum=0;
for(int i=1;i<=n;i++)
_sum+=A[i];
return _sum;
}
int main()
{
int A[100];
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&A[i]);
printf("%d",sum(A));
}
运行结果:
函数指针
函数指针甚至还可以指向函数,但指针的参数要和原函数一样。
注意:函数指针必须加上括号,否则系统会以为你定义了一个int类型的指针。于是乎。。。
#include<cstdio>
int n;
int (*p)(int*);//这里是函数指针
//int *p(int*);//不能这样写
int sum(int *A){
int _sum=0;
for(int i=1;i<=n;i++)
_sum+=A[i];
return _sum;
}
int main()
{
int A[100];
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&A[i]);
p=sum;
printf("%d",p(A));
//也可以这样写:
//printf("%d",(*p)(A));
}
运行结果:
结构体指针
首先关于结构体基础知识,请—>点击<—
声明结构体指针就直接在名称前加*
即可,指向结构体成员有两种方法,以程序为例。
#include<cstdio>
struct node{int a,b;}x,*y;
int main()
{
y=&x;x.a=123;x.b=987;
printf("%d\n",(*y).a);//第一种写法,一定要加括号哦!
printf("%d\n",y->b);//第二种写法,不要加星号哦!
}
运行结果:
双重指针
如下:
#include<cstdio>
int main()
{
int a=100,*b,**c;
b=&a;c=&b;
printf("%p %p %p %p\n",&a,b,*c,&c);
printf("%d %d %d\n",a,*b,**c);
}
运行结果: