指针与数组


定义指向数组元素的指针

定义与赋值
int a[10],*pa;
pa=&a[0];或者pa=a;
等效的形式

  • pa就是a[0],* (pa+1)就是a[1]….* (pa+i)就是a[i]
    !!!重点!!!
    a[i],* (pa+i),* (a+i),pa(i)都是等效的

指针数组:数组的元素是指针类型

Point * pa[2];
是由pa[0],pa[1]两个指针组成的

实例

#include <iostream>
using namespace std;
int main(){
	int line1[]={1,0,0}
	int line2[]={0,1,0}
	int line3[]={0,0,1}
	//定义整型指针数组并初始化
	int * pline[3]={line1,line2,line3};
	//输出
	cout<<"Matrix test:"<<endl;
	for (int i=0;i<3;i++){
		for (int j=0;j<3;j++){
			cout<<pline[i][j]<<" ";
		}
		cout<<endl;
}


为什么需要用指针作参数?
需要数据双向传递时(引用也可以达到此效果)
需要传递一组数据,只传首地址运行效率比较高

例子:读入三个浮点数,将整数部分和小数部分分开输出

#include <iostream>
using namespace std;
void splitFloat(float x,int *intPart,float *fracpart){
	*intPart=static_cast<int>(x);
	*fracpart=x-*intPart;
}
int main(){
	cout<<"Enter 3 float point numbers:"<<endl;
	for (int i=0;i<3;i++){
		float x,f;
		int n;
		cin>>x;
		splitFloat(x,&n,&f);//变量地址作实参
		cout<<"Integer Part="<<n<<"fracpart="<<f<<endl;

	}
}

例子:指向常量的指针作形参
传递地址且不需要主调函数中的数值是无法修改的
这个时候把指针定义为常量指针,只为了输出。
最小授权原则,对外的访问授权够用就好。

#include<iostream>
using namespace std;
const int N=6;
void print(const int *p ,int n);
int main(){
	int array[N];
	for (int i=0;i<N;i++)
		cin>>array[i];
	print(array,N);
	return 0;
}
void print(const int *p,int n){
	cout<<"{"<<*p;
	for (int i=1;i<n;i++)
		cout<<","<<*(p+i);
	cout<<"}"<<endl;
}

指针函数的定义形式:

存储类型 数据类型 *函数名 ( )
{
//函数体语句
}
!!不要将非静态局部地址用作函数的返回值
错误的例子:
在子函数中定义局部变量后将其地址返回给主函数,就是非法地址
错误例子:


int main(){
	int *function();
	int *ptr=function();
	*ptr=5;//危险的访问
	return 0;
}
int *function(){
	int local=0;//非静态局部变量作用域和寿命仅限于本函数体内
	return &local;
}//函数结束运行时,变量local被释放

正确例子:
动态分配地址,函数有效

#include<iostream>
using namespace std;
int main(){
	int *newintvar();
	int *intptr=newintvar();
	*intptr=5; //访问的是合法有效的地址
	delete intptr;//如果忘记在这里释放,会造成内存泄漏
	return 0;

}
int *newintvar(){
	int *p=new int();
	return p;//返回的地址指向的是动态分配的空间
}//函数运行结束时,p中的地址仍然有效

函数指针

定义形式:
存储类型 数据类型 (* 函数指针名)();
含义:
函数指针指向的是程序代码存储区。

函数指针的典型用途–实现函数回调
通过函数指针调用的函数
例如将函数的指针作为参数传递给一个函数,使得在处理相似事件的时候可以灵活的使用不同的方法
调用者不关心谁是被调用者
需知道存在一个具有特定原型和限制条件的被调用函数

#include<iostream>
using namespace std;
int compute(int a,int b,int (*func)(int,int))
{
	return func(a,b);
}
int max(int a,int b)
{
	return ((a>b)?a:b);
}
int min(int a,int b)
{
	return ((a<b)?a:b);
}
int sum(int a,int b)
{
	return a+b;
}
int main(){
	int a ,b,res;
	cout<<"请输入整数a:";cin>>a;
	cout<<"请输入整数b:";cin>>b;
	res=compute(a,b,&max);
	cout<<"Max of "<<a<<"and"<<b<<" is"<<res<<endl;
	res=compute(a,b,&min);
	cout<<"Min of "<<a<<"and"<<b<<" is"<<res<<endl;
	res=compute(a,b,&sum);
	cout<<"Sum of "<<a<<"and"<<b<<" is"<<res<<endl;
	

}

this指针

隐含于类的每一个非静态成员函数中
指出成员函数所操作的对象
当通过一个对象调用成员函数时,系统先将该对象的地址赋给this指针,然后调用成员函数,成员函数对对象的数据成员进行操作时,就隐含使用了this指针
例如:
Point类的getX函数中的语句
return x;
相当于
return this->x;

动态申请内存操作符 new

new 类型名T(初始化参数列表)
功能:
在程序执行期间,申请用于存放类型T对象的内存空间
并依初值列表赋以初值
成功:T类型的指针,指向新分配的内存
失败:抛出异常

释放内存操作符 delete

功能:释放指针p所指向的内存。
p必须是new操作的返回值

例子:
动态创建对象举例

#include<iostream>
using namespace std;
int main(){
	cout<<"Step One:"<<endl;
	Point *ptr1=new Point;//调用默认构造函数
	delete ptr1;//删除对象,自动调用析构函数
	cout<<"Step Two:"<<endl;
	ptr1= new Point(1,2);
	delete ptr1;
	return 0;
}

分配和释放动态数组
分配:new 类型名T [数组长度]
数组长度可以是任何整数类型表达式,在运行时计算
释放:delete []数组名p
释放指针p所指向的数组
p必须是用new分配得到的数组首地址

动态创建多维数组

#include <iostream>
using namespace std;
int main(){
	int (*cp)[9][8]=new int [7][9][8];
	for (int i=0;i<7;i++)
		for (int j=0;j<9;j++)
			for(int k=0;k<8;k++)
				*(*(*(cp+i)+j)+k)=(i*100+j*10+k);
	for (int i=0;i<7;i++){	
		for (int j=0;j<9;j++){
			for(int k=0;k<8;k++)
				cout<<cp[i][j][k]<<" ";
			cout<<endl;
		}
		cout<<endl;
	}
	delete []cp;
	return 0;
}

将动态数组分装成类

更加简洁,便于管理
可以在访问数组元素前检查
下标是否越界

C++11的智能指针

unique_ptr:
不允许 多个指针共享资源,可以用标准库中的move函数转移指针
shared_ptr:
多个指针共享资源
weak_ptr:
可复制shared_ptr,但其构造或者释放对资源不会产生影响

vector

为什么需要vector
封装任何类型的动态数组,自动创建和删除
数组下标越界检查
vector对象的定义
vector<元素类型>数组对象名(数组长度)
例子
vetorarr[5]
对象的使用
对数组元素的引用
与普通数组具有相同形式
vector对象名[下标表达式]
vector数组对象名不表示数组首地址
获得数组长度
用size函数
vector对象名.size

例子:计算平均值
for条件语句还是习惯加花括号
否则容易出问题

#include<iostream>
#include <vector>

using namespace std;
double average(const vector<double>&arr)
{
	double sum=0;
	for (unsigned i=0;i<arr.size();i++)
		sum+=arr[i];
		cout<<"sum="<<sum<<endl;
		cout<<"size="<<arr.size()<<endl;
	return sum/arr.size();
}
int main(){
	unsigned n;
	cout<<"n= ";
	cin>>n;
	vector <double>arr(n);
	cout<<"please input"<<" "<<n<<" "<<"real numbers:"<<endl;
	for (unsigned i=0;i<n;i++)
		cin>>arr[i];
		cout<<"Average ="<<average(arr)<<endl;
		return 0;
}

浅层复制

实现对象间数据元素的一一复制

深层复制

返回时构造临时对象,动态分配将临时对象返回到主调函数,然后删除临时对象

#include <iostream>
#include <vector>
using namespace std;
class IntNum{
public:
   IntNum(int x=0):xptr(new int (x)){
      cout<<"Calling constructor..."<<endl;
   }
   IntNum(const IntNum &n):xptr(new int(*n.xptr)){
      cout<<"Calling copy constructor.."<<endl;
   }
   ~IntNum(){
      delete xptr;
      cout<<"Destructing.."<<endl;
   }
   int getInt(){
      return *xptr;
   }
private:
   int *xptr;
};
IntNum getNum(){
   IntNum a;
   return a;
}
int main(){
   cout<<getNum().getInt()<<endl;
   return 0;
}

移动构造

class_name(class_name)
将要返回的局部对象转移到主调函数,省去了构造和删除临时对象的过程

#include <iostream>
#include <vector>
using namespace std;
class IntNum{
public:
   IntNum(int x=0):xptr(new int (x)){
      cout<<"Calling constructor..."<<endl;
   }
   IntNum(const IntNum &n):xptr(new int(*n.xptr)){
      cout<<"Calling copy constructor.."<<endl;
   }
   ~IntNum(){
      delete xptr;
      cout<<"Destructing.."<<endl;
   }
   int getInt(){
      return *xptr;
   }
private:
   int *xptr;
};
IntNum getNum(){
   IntNum a;
   return a;
}
int main(){
   cout<<getNum().getInt()<<endl;
   return 0;
}

注意:
&&是右值引用
函数返回的临时变量是右值

移动构造优于复制构造,但具体还需要之后作题练习,这里一头雾水


评论
  目录