数组是有一定顺序关系的数组元素的集合,数组元素在内存中的存储地址是连续的。指针的加减运算特别适合处理这种存储在连续内存空间内的相同类型的数据。
数组元素的访问
假设有一个声明:int a[10];,声明了一个一维整型数组,有10个元素。数组名a代表了数组a的首地址,它是一个指针常量,也就是不能改变,所以,注意,不能进行自增自减运算。首地址就是数组第一个元素的地址,所以a和&a[0]是完全等价的。a中的10个元素是连续存放在内存中的,所以用数组名结合简单的算术运算就可以访问数组的元素。a是数组第一个元素的地址,那么第i+1个元素也就是下标为i的元素的地址就是a+i,第i+1个元素就是*(a+i)。例如,a就是a[0],(a+7)就是a[7]。
- 使用数组名和下标访问的方式
int main()
{
int a[5];
int i;
for(i=0; i<5; i++)
cin>>a[i];
cout<<endl;
for(i=0; i<5; i++)
cout<<a[i]<<" ";
return 0;
}
- 使用数组名和指针运算访问的方式
int main()
{
int a[5];
int i;
for(i=0; i<5; i++)
cin>>a[i];
cout<<endl;
for(i=0; i<5; i++)
cout<<*(a+i)<<" ";
return 0;
}
- 使用指针变量访问的方式
int main()
{
int a[5];
int *p;
int i;
for(i=0; i<5; i++)
cin>>a[i];
cout<<endl;
for(p=a; p<(a+5); p++)
cout<<*p<<" ";
return 0;
}
指针数组
数组中的元素或者是基本类型的变量或者是类的对象,同理,数组元素也可以是指针变量,而且每个指针变量的类型是相同的,这样的数组就是指针数组。
声明一维指针数组的语法形式是:
数据类型 *数组名[下标表达式];
数据类型指明指针数组元素的类型,就是确定每个元素指针指向什么类型的数据。数组名也是这个指针数组的首地址。下标表达式指明数组元素的个数。需要先赋值才可以引用。
#include <iostream>
using namespace std;
int main()
{
int array1[]={1,2,3};//声明一个数组
int array2[]={2,4,6};//声明另一个数组
int *p[2]; //声明整型指针数组
p[0]=array1; //初始化指针数组元素
p[1]=array2;
//输出两个数组的全部元素
for(int i=0;i<2;i++) //对指针数组元素循环
{
for(int j=0;j<3;j++)//对每个一般数组循环
{
cout<<p[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
二维数组在内存中是按照行优先的方式顺序存放的,所以二维数组我们可以理解为一维指针数组,每个指针元素分别指向二维数组的一行,这个一维指针数组的元素个数就是二维数组的行数。
用数组指针访问二维数组元素的方式是:((array+i)+j)。
指针用作函数参数
指针作函数形参时,我们调用此函数将实参值传递给形参后,实参和形参指针变量将指向相同的内存地址,那么在被调函数中对形参指针所指向的对象的改变会一样影响主调函数中实参指针指向的对象。
指针用作函数参数的作用:
- 使形参指针和实参指针指向相同的内存地址,在被调函数中可以使用主调函数中的数据并可以改变主调函数中的数据,达到数据双向传递的效果。当然,前面讲过的引用也可以实现相同的作用。
- 用指针作函数参数传递数据可以减少参数传递的开销,引用当然也可以实现这些。
- 可以通过指向函数的指针来传递函数代码的首地址。
指针和引用很多时候作用是一样的,引用相对指针来说可读性更好,但有时还是需要使用指针。
#include<iostream>
using namespace std;
void Swap(int *a, int *b);
int _tmain(int argc, _TCHAR* argv[])
{
int x=5, y=10;
cout<<"x="<<x<<" y="<<y<<endl;
Swap(&x, &y);
cout<<"x="<<x<<" y="<<y<<endl;
return 0;
}
void Swap(int *a, int *b)
{
int t;
t=*a;
*a=*b;
*b=t;
}
指针型函数
函数都有自己的类型,除void类型的函数外都有自己的返回值。函数的返回值也可以是指针。返回值为指针类型的函数就是指针型函数。普通函数只能返回一个变量或对象,但指针型的函数可以在函数调用结束时将大量数据从被调函数返回到主调函数中,这就是它的好处。
注意不要返回局部变量的地址,因为出了被调函数局部变量就释放了,返回的地址中存放的内容也是无效的了。
声明指针型函数的语法形式是:
数据类型 *函数名(参数表)
{
函数体
}
数据类型指明了函数返回的指针的类型,“ * ”和函数名说明这是一个指针型函数,参数表是函数的形参列表。
函数指针
程序运行时,数据会占用内存空间,实际上程序执行时代码也会调入内存,也会占用内存空间。函数名就是函数代码占用内存空间的首地址。函数指针就是用于存放函数代码首地址的变量。
我们也可以使用函数指针来调用函数,它和函数名实现的作用是一样的。声明方式也类似,也需要指明函数的返回值、形参列表,声明的语法形式是:
数据类型 (*函数指针名)(形参列表);
- 数据类型指明了函数指针所指向函数的返回值类型,函数指针名给出函数指针的名称,形参列表则说明了函数指针所指函数的形参类型和形参个数。
函数指针和一般的指针一样也要在使用之前先赋值,让它指向一个函数代码的首地址。为函数指针赋值的语法形式是:
函数指针名 = 函数名;
- 被指向的函数需要是声明过的已存在的,和函数指针必须具有相同的返回值类型、形参个数和形参类型。给函数指针赋值之后我们就可以通过函数指针名来调用它指向的函数了。
#include <iostream>
using namespace std;
void show(int x)
{
cout << x << endl;
}
void (*show_pointer)(int); // 声明一个void类型的函数指针
int main()
{
int a = 10;
show_pointer = show; // 函数指针show_pointer指向show
show_pointer(a); // 函数指针调用
return 0;
}