NOIP学习小站
西安交通大学附属中学航天学校

指针

指针其实存储的是变量在内存中的地址,将指针指向变量后,就可以使用该指针来获取变量的值或者为变量赋新值。

一、指针的基本用法

指针是用来指向某个变量的,将指针指向变量后,指针存储的是该变量在内存中的地址。要使用指针,首先要申明指针,然后就可以将指针指向变量。将指针指向变量后,就可以使用该指针来获取变量的值或者为变量赋新值:

#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对应的形式参数就是指针类型。