C/C++指针入门详解(一)

一、引言

指针是一个地址,这个地址不仅可以是变量的地址,也可以是其它数据结构的地址。为了方便调用这个地址,C/C++是通过指针变量来使用这个地址的。而我们常说的指针,其实在一定程度上说的是指针变量,但是我们必须清楚认识到指针和指针变量是有着严格的不同的,是两个完全不同的概念。
在C/C++语言中, 数组、数据结构和函数往往都占有一组连续的内存单元,通过访问指针变量取得了数组或函数的首地址,也就找到了该数组或函数。 但是用“地址”这个概念并不能很好地描述一种数据类型或数据结构, 而“指针”虽然实际上也是一个地址,但它却是一个数据结构的首地址,它是“指向”一个数据结构的,因而表示更为明确。这也是引入“指针”概念的一个重要原因。
如下图所示,有两个变量a和b,其在内存中的地址分别是0x1000和0x10040,这两个地址就是指针。
在这里插入图片描述
如果想通过地址来访问变量a或者b,可以提高程序的运行速度,而为了方便使用这些内存地址,在C/C++中就引入了指针变量的概念,用指针变量存储a和b等这些变量的地址。例如用指针变量p存储变量a的地址,用指针变量q存储变量b的地址,则得到如下的图。
在这里插入图片描述
如果想通过直接访问内存地址的方式获取变量a的值,就可以使用*p的方式来获取。如果变量a是一个数组,则p就是指向数组a首地址的指针变量,也就是指针变量的值是数组a的首地址。

二、指针的特点

1.指针是C/C++语言中广泛使用的一种数据类型。
2.运用指针编程是C/C++语言最主要的风格之一。
3.利用指针变量可以表示各种数据结构;
4.能很方便地使用数组和字符串;
5.能象汇编语言一样处理内存地址,从而编出精练而高效的程序。

因此,指针极大地丰富了C/C++语言的功能。学习指针是学习C/C++语言中最重要的一环, 能否正确理解和使用指针是我们是否掌握C/C++语言的一个重要标志。

三、引入指针的优势

1.提高程序的编译效率和执行速度
C/C++中的变量和数据都是存储在计算机内存中,而访问数据都是通过变量来实现。每个变量都是存储在内存中固定的位置,且拥有唯一的地址。通过变量获取数据,需要先转换到内存地址,再提取数据。C/C++增加了一种可以直接访问内存地址中数据的方法——指针,这样使得程序运行效率更高。
2.数组动态化
即动态数组,也就是可以根据实际需要为数组动态的分配存储空间。
例如:数组求和问题。每一次参与求和运算的元素个数不相同,如果使用静态数组来实现,则数组的长度不好定,如果给的太大,可能会浪费空间,如果太小又无法存储全部的元素。为了方便实现该运算就可以根据每一次参与运算的元素个数动态分配存储空间。
3.函数参数的双向传值
对于函数的参数,如果是普通的变量,则只能是由“实参”传递给“形参”,反之不可以。但是指针可以实现参数的双向传递功能,也就是通过指针可使主调函数和被调函数之间共享变量或数据结构,便于实现双向数据通讯。
4.快捷交换两个数组的元素
对于静态数组,想交换一下这两个数组的内容,只能逐个元素交换。但是指针却是可以实现直接交换两个数组的内容。就像2位老师分别给两个班级讲授不同的课程,如果下一节课两个班级的课对调,那么是老师不动而学生换教室呢,还是学生不动老师换教室?哪个操作更简单?答案是很显然的,即老师换教室而学生不换教室。班级的学生如果看做是数组的话,而任课老师是数组名,这里就相当于交换了两个数组名。但是静态数组是不允许这样操作的,而引入指针后就可以轻松实现此功能。因为此时老师就是指针变量,而班级就是指针变量对应存储空间里的数据。
5.在类的组合中指针发挥了重要作用
如果有两个类,如下:

class A;
class B
{
    
    
private:
	A *p1, *p2;
	......
};

此时类A作为类B的前导声明,而在类B中又定义了A的对象,则A的对象必须使用指针,即指针对象。

四、指针变量的定义

1.指针和指针变量的含义
指针:地址
指针变量:存放变量地址的变量。作用是用来指向另外一个变量。并且指针变量在使用前必须初始化。
2.指针变量的定义

    类型说明符  *指针变量名;

其中,* 表示这是一个指针变量,类型说明符表示该指针变量所指向的变量的数据类型。
例如:

int *pr;

表示p是一个整型的指针变量,它的值是某个整型变量的地址,或者说p指向一个整型变量。

char *q;

表示q是一个字符型的指针变量,指向了存储字符型变量的首地址。
需要说明的是,由于在 C/C++语言中,地址本身是整型数据,因此对上述的指针变量p和q而已,他们本身也是变量,但因为存储的是地址,因此p和q在内存中所占用的存储空间大小和存储整型数据的空间大小相同。这可以利用sizeof函数来测试。例如在DEV-C++环境下,sizeof(q)和sizeof§的值均是4,即存储变量p和q的存储空间的长度均是4个字节。
3.指针变量的运算
两个有关的运算符分别是 & 和 *,

& :取地址运算符
    *  :指针运算符(或称”间接访问”运算符), 其结合性为自右至左 。
  • 运算符之后跟的变量必须是指针变量。
    需要注意的是指针运算符 * 和指针变量说明中的指针说明符 * 不是一回事。在指针变量说明中,* 是类型说明符,表示其后的变量是指针类型。而表达式中出现的 * 则是一个运算符用以表示指针变量所指的变量。
    4.指针变量之间的运算
    1)指针变量自身的算术运算
    假设p是一个指针变量,且指向一个数组a,则:
    a)p++表示指针偏移一个存储单元,即指向下一个元素的地址。一个存储单元即是数组一个元素所占的字节数,和数组的类型有关。
    b)p++相当于(p++),即先使用p,然后执行p = p + 1。
    c)p+i则表示指针偏移i个存储单元。
    d)
    (p+i)或p[i]表示的是数组元素a[i]。
    e)(p)++是先取出p,然后让*p加1。
    2)指针变量之间的算术运算
    两个指针变量只能做减法运算!!!
    两指针变量相减所得之差是两个指针所指数组元素之间相差的元素个数。实际上是两个指针值(地址) 相减之差再除以该数组一个元素的存储空间长度。
    例如p和q是指向同一浮点数组的两个指针变量,设p的值为0x2010,q的值为0x2000,而浮点数组每个元素占4个字节,所以p-q的结果为(0x2010 - 0x2000)/4=4,表示p和q之间相差4个元素。
    3)指针变量之间的关系运算
    两指针变量可以进行关系运算。如果p和q是指向同一数组的两指针变量,则可以进行如下关系运算:
    p == q 表示p和q指向同一数组元素,通常用于数据的缓存。
    p > q 表示p处于高地址位置
    p < q 表示p处于低地址位置

五、指针的相关应用范例

指针的在实际软件开发中应用非常普遍,这样应了那句“不会指针就没有真正掌握C/C++”,当然这话说的有点绝对,其实即使会了指针,也未必精通C/C++,因为单纯的学习编程语言容易,但是要熟练应用,你又会发现设计算法是最难的。
1.直接访问内存
需要说明的是,使用指针变量就是直接访问内存地址。此处仅仅用一个简单的例子演示说明而已。
例1:利用指针指向存储某个变量的地址,然后利用指针直接读取内存中的数据。

#include "stdio.h"
int main()
{
    
    
	int a = 100;
	int *p;
	p = &a;	//指针变量p指向变量a的首地址。
	printf("*p = %d\n", *p);//输出*p的值,即p指向内存地址中的变量 
	return 0; 
}

运行结果:
在这里插入图片描述
2.动态数组
例2:从键盘读入一个整数表示动态数组元素的个数,之后依次读入数组的元素,并向屏幕输出数组。

#include"stdio.h"
#include"malloc.h"
int main()
{
    
    
	int i, num;
	int *p;
	printf( "Input array's length:" );
	scanf( "%d", &num );
	//为指针变量p(动态数组)分配存储空间,长度是num 
	p = (int *)malloc( num * sizeof(int) );
	
	//为动态数组p赋值 
	for( i = 0; i < num; i++ )
	{
    
    
		printf( "Input No. %d element: ", i );
		scanf( "%d", &p[i] ); 
	}
	//输出动态数组p中的元素 
	for( i = 0; i < num; i++ )
	{
    
    
		printf( "Output No. %d element: %d\n", i, p[i] );
	}  
	/*下面代码演示了指针的其它操作 
	//上述操作也可以用下面代码实现。
	//但是存在个问题就是容易“丢失”数组的首地址,为了避免此问题,可以再引入一个指针缓存p 
	int *q; 
	q = p;
	//为动态数组p赋值 
	for( i = 0; i < num; i++ )
	{
		printf( "Input No. %d element: ", i );
		scanf( "%d", p++ );//指针变量p连续向后移动一个存储单元 
	}
	//输出动态数组p中的元素 
	for( i = 0; i <num; i++ )
	{
		printf( "Output No. %d element: %d\n", i, *(q+i) );//*(q+i)表示动态数组p中的第i个元素 
	} 
	*/
	return 0; 
}

运行结果:
在这里插入图片描述
3.函数参数的双向传值
例3:利用函数实现对一组整数进行逆序。

#include"stdio.h"
#define N 5 
void InverseArr( int *p, int n );
int main()
{
    
    
	int i, arr[N]={
    
     1, 2, 3, 4, 5 };
	int *p = arr;
	InverseArr( p, N );
	printf( "inverse element:" );
	for( i = 0; i < N; i++ )
	{
    
    
		printf( " %5d", *p++ );
	}
	return 0; 
}
//参数p既是输入参数,也是输出参数
void InverseArr( int *p, int n ) 
{
    
    
	int temp;
	for( int i = 0; i < n/2; i++ )
	{
    
    
		temp = p[i];
		p[i] = p[n-i-1];
		p[n-i-1] = temp;
	}
}

运行结果:
在这里插入图片描述
例4:利用指针实现函数参数的双向传值(C++)

#include<iostream>
using namespace std;
//交换m和n所指向地址中数据
void swap(int *m, int *n)
{
    
    
	int temp;
	temp = *m;
	*m   = *n;
	*n   = temp;
}
int main()
{
    
    
	int a = 5, b = 10;
	swap( &a,  &b );
    cout<<"swaped:"<<endl;
	cout<<"a= "<<a<<"  b= "<<b<<endl;
	return 0;
}

运行结果:
在这里插入图片描述
4.交换两个数组
例5:有两个数组,利用指针变量交换数组元素。即指针变量p和q分别指向两个数组,利用更改指针变量值的方式交换p和q指向的数组,以达到交换两个数组元素的目标。

#include"stdio.h"
#define N 5
#define M 6 
void InverseArr( int *p, int n );
int main()
{
    
    
	int i, *p, *q;
	int arr1[N]={
    
     1, 2, 3, 4, 5 };
	int arr2[M]={
    
     6, 7, 8, 9, 10, 11 };
	p = arr1;
	q = arr2;
	printf( "\n交换前—数组p的数据:" );
	for( i=0; i<N; i++ )
	{
    
    
		printf( "%5d", p[i] );
	}
	printf( "\n交换前—数组q的数据:" );
	for( i=0; i<M; i++ )
	{
    
    
		printf( "%5d", q[i] );
	}
	//交换两个数组 
	p = arr2;
	q = arr1;
	printf( "\n交换后—数组p的数据:" );
	for( i=0; i<M; i++ )
	{
    
    
		printf( "%5d", p[i] );
	}
	printf( "\n交换后—数组q的数据:" );
	for( i=0; i<N; i++ )
	{
    
    
		printf( "%5d", q[i] );
	}
	return 0; 
}

运行结果:
在这里插入图片描述
5.类的组合之前导类声明
例6:利用前导类实现计算坐标屏幕上两个点直接的距离。其中坐标点为一个类,计算点之间的距离为另外一个类。

#include<iostream>
#include<cmath>
using namespace std;
class CPoint;
class CDistance
{
    
    
private:
	CPoint *p1, *p2;
	double dist;
public:
	CDistance(CPoint *a, CPoint *b);
	double GetDist()
	{
    
    	return dist;}
};
class CPoint
{
    
    
private:
	int m_x, m_y;
public:
	CPoint(int x, int y)
	{
    
    	
		m_x = x; 
		m_y = y;
	}
	int GetX(); 
	int GetY();
};
int CPoint::GetX()
{
    
    	
	return m_x;
}
int CPoint::GetY()
{
    
    	
	return m_y;
}
CDistance::CDistance(CPoint *a, CPoint *b):p1(a), p2(b)
{
    
    
	double x, y;
	x = double(p1->GetX() - p2->GetX());
	y = double(p1->GetY() - p2->GetY());
	dist = sqrt(x*x + y*y);
}
int main()
{
    
    
	CPoint pa(1,1), pb(3,5);
	CDistance dis(&pa, &pb);
	cout<<"Distance of the two points is ";
	cout<<dis.GetDist()<<endl;
	return 0;
}

运行结果:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/sunnyoldman001/article/details/128061186