指针其实存储的是变量在内存中的地址,将指针指向变量后,就可以使用该指针来获取变量的值或者为变量赋新值。
一、指针的基本用法
指针是用来指向某个变量的,将指针指向变量后,指针存储的是该变量在内存中的地址。要使用指针,首先要申明指针,然后就可以将指针指向变量。将指针指向变量后,就可以使用该指针来获取变量的值或者为变量赋新值:
#include<iostream> using namespace std; int main() { int n = 123; int *p; //申明int*类型指针p,这样的指针可以指向int类型变量 p = &n; //通过取地址运算将指针p指向变量n cout<<n<<endl; cout<<*p<<endl; //通过指针获取其指向的变量的值 *p = 0; //通过指针修改其指向的变量的值 cout<<n<<endl; cout<<*p<<endl; (*p)++; //指针指向变量的值自加,这里的括号不能省略 cout<<n<<endl; cout<<*p<<endl; int *q = p; //指针q的值赋值为指针p的值,q也指向p指向的变量n *q = *q + 1; cout<<n<<endl; cout<<*p<<" "<<*q<<endl; return 0; }
指针实际上存储的是其指向的变量在内存中的地址,运行下面的程序:
#include<iostream> using namespace std; int main() { int n = 123; int *p; //申明int*指针p,可以指向int类型变量 p = &n; //指针p指向变量n cout<<n<<" "<<*p<<endl; //变量的值 cout<<&n<<" "<<p<<endl; //变量在内存的地址 return 0; }
程序运行结果如下(自行测试时第2行可能和下面的结果不一致):
123 123 0x6dfe78 0x6dfe78
第2行输出的就是变量n在内存的地址(是一个十六进制数)。
对于scanf语句:scanf("%d",&n);
,我们已经知道int类型变量n前面必须加上取地址符号&,现在我们清楚了,这里&n其实是指向变量n的指针,scanf函数要求从第2个参数开始后面的参数必须是指针,所以需要在普通变量前加上取地址符号&。
二、数组与指针
先来看下面的程序:
#include<iostream> using namespace std; int main() { int a[5] = {1,2,3,4,5}; cout<<a<<endl; return 0; }
运行程序会发现,直接输出数组名a,输出的是一个十六进制的地址,原来数组名其实也是一个指针(其实是一个常量指针),指向的是数组的第0个元素。那么指针a+i指向的是数组第i个元素a[i]。来看下面的程序:
#include<iostream> using namespace std; int main() { int a[5]; for(int i=0;i<5;i++){ scanf("%d",&a[i]); } for(int i=0;i<5;i++){ printf("%d ",a[i]); } return 0; }
#include<iostream> using namespace std; int main() { int a[5]; for(int i=0;i<5;i++){ scanf("%d",a+i); } for(int i=0;i<5;i++){ printf("%d ",*(a+i)); } return 0; }
现在我们应该清楚scanf输入字符串时:char str[1010];scanf("%s",str);
,这里用字符数组存储字符串,str已经是指针了,就不能在str前添加取地址符号&。
需要特别注意的是,数组名是一个常量指针,可以将数组名赋值给其他指针,但是不能将其他指针赋值给数组名:
#include<iostream> using namespace std; int main() { int a[5]; //指针pa赋值为数组名a,pa指向a[0] int *pa = a; for(int i=0;i<5;i++){ scanf("%d",pa+i); } for(int i=0;i<5;i++){ printf("%d ",*(pa+i)); } return 0; }
#include<iostream> using namespace std; int main() { int a[5],n = 100; int *pa = &n; a = pa; //编译错误 return 0; }
三、结构体与指针
当然可以用一个指针指向结构体变量:
#include<iostream> using namespace std; struct Circle{ double r; Circle(){} Circle(double r){ //构造函数 this->r = r; //this是指向结构体自身的指针 } double length(){ //成员函数,计算返回圆周长 return 2*3.14*r; } double area(){ //成员函数,计算返回圆面积 return 3.14*r*r; } }; int main() { Circle c(12.56); cout<<c.length()<<" "<<c.area()<<endl; Circle *p = &c; cout<<(*p).r<<" "<<(*p).length()<<" "<<(*p).area()<<endl; //原始写法 cout<<p->r<<" "<<p->length()<<" "<<p->area()<<endl; //简便写法 return 0; }
甚至可以使用new关键字来直接生成指向结构体的指针:
#include<iostream> using namespace std; struct Circle{ double r; Circle(){} Circle(double r){ //构造函数 this->r = r; } double length(){ //成员函数,计算返回圆周长 return 2*3.14*r; } double area(){ //成员函数,计算返回圆面积 return 3.14*r*r; } }; int main() { Circle *p = new Circle(12.56); cout<<(*p).r<<" "<<(*p).length()<<" "<<(*p).area()<<endl; //原始写法 cout<<p->r<<" "<<p->length()<<" "<<p->area()<<endl; //简便写法 return 0; }
四、函数参数与指针
如果函数的参数是指针,那么在函数体内通过指针参数来修改指向变量的值,能够影响实际参数的值。将参数设置为指针类型也是函数参数采用地址传递的常用方法。
仍然以交换两个变量的值的函数为例,如果函数参数为指针,那么可以这样设计(注意和前面形式参数名前加&——引用传参——的区别):
#include<iostream> using namespace std; //参数是指针(地址传递) void swap(int *x,int *y){ int t = *x; *x = *y; *y = t; } int main() { int a = 1,b = 2; cout<<a<<" "<<b<<endl; swap(&a,&b); cout<<a<<" "<<b<<endl; return 0; }
#include<iostream> using namespace std; //引用传参 void swap(int &x,int &y){ int t = x; x = y; y = t; } int main() { int a = 1,b = 2; cout<<a<<" "<<b<<endl; swap(a,b); cout<<a<<" "<<b<<endl; return 0; }
前面我们已经遇到过函数的参数是数组的情况,此时在函数体内修改形式参数数组元素的值,实际参数数组元素也会受影响。其实数组参数可以直接写成指针形式:
#include<iostream> using namespace std; int a[1001]; void read(int arr[],int n){ for(int i=0;i<n;i++) cin>>arr[i]; } void exec(int arr[],int n){ for(int i=0;i<n;i++) arr[i] *= arr[i]; } void out(int arr[],int n){ for(int i=0;i<n;i++) cout<<arr[i]<<" "; cout<<endl; } int main() { int n; cin>>n; read(a,n); out(a,n); exec(a,n); out(a,n); return 0; }
#include<iostream> using namespace std; int a[1001]; void read(int *arr,int n){ for(int i=0;i<n;i++) cin>>arr[i]; } void exec(int *arr,int n){ for(int i=0;i<n;i++) arr[i] *= arr[i]; } void out(int *arr,int n){ for(int i=0;i<n;i++) cout<<arr[i]<<" "; cout<<endl; } int main() { int n; cin>>n; read(a,n); out(a,n); exec(a,n); out(a,n); return 0; }
现在回顾一下使用scanf函数输入数据存储到变量中:
int n; scanf("%d",&n);
调用scanf函数后,变量n的值会被修改成输入的整型数据(实际参数的值在函数内部被修改),scanf函数中实际参数n对应的形式参数就是指针类型。