第十六天:C++数组指针与字符串

数组

数组是具有一定顺序关系的若干相同类型变量的集合体,组成数组的变量。

范围for循环
#include <iostream>
using namespace std;
int main(void)
{
	int a[10], b[10];
	for (int i = 0; i < 10; i++)
	{
		a[i] = i * 2 - 1;
		b[10 - i - 1] = a[i];
	}
	//遍历容器十分方便,遍历的另一种形式,从a中依次取出元素,进行操作
	//auto 自动类型,根据a的类型确定e的类型
	for (const auto& e : a)  //范围for循环,输出a中每个元素
		cout << e << " ";
	cout << endl;
	for (int i = 0; i < 10; i++) //下标迭代循环,输出b中每个元素
	{
		cout << b[i] << " ";
	}
	cout << endl;

	return 0;
}

数组初始化
  • 如果不作任何初始化,局部作用域的非静态数组中会存在垃圾数据,static数组中的数组默认初始化为0
  • 如果只对部分元素初始化,剩下的未显式初始化的元素,将自动被初始化为零
对象数组
  • 定义对象数组
    • 类名 数组名[元素个数]
  • 访问对象数组元素
    • 通过下标访问
    • 数组名[下标].成员名
  • 对象数组初始化
    • 数组中每一个元素对象被创建时,系统都会调用类构造函数初始化该对象。
    • 通过初始化列表赋值
    • Point a[2]={Point(1,2),Point(3,4)}
    • 如果没有为数组元素指定显式初始值,数组元素便使用默认值初始化(调用默认构造函数)
  • 对象数组的析构
    • 当数组中每一个对象被删除时,系统都要调用一次析构函数
#include <iostream>
#include "Pont.h"
using namespace std;
Point::Point() :x(0), y(0)
{
	cout << "Default Constructor called." << endl;
}
Point::Point(int x, int y) :x(x), y(y)
{
	cout << "Constructor called." << endl;
}
Point::~Point()
{
	cout << "Destructor called" << endl;
}
void Point::move(int newX, int newY)
{
	cout << "Moving the point to(" << newX << "," << newY << ")" << endl;
	x = newX;
	y = newY;
}
int main(void)
{
	cout << "Entering main..." << endl;
	Point a[2];
	for (int i = 0; i < 2; i++)
	{
		a[i].move(i + 10, i + 20);
	}
	cout << "Exiting main..." << endl;
	return 0;
}
/*
* Entering main...
* Default Constructor called.
* Default Constructor called.
* Moving the point to(10,20)
* Moving the point to(11,21)
* Exiting main...
* Destructor called
* Destructor called
*/

指针

  • 指针:内存地址,用于间接访问内存单元
  • 指针变量:用于存放地址的变量
    指针变量
  • 可以赋给指针,表示空指针
  • 向指针变量赋值的值必须是地址常量,不能是普通整数
  • 允许定义或声明指向void类型的指针,该指针可以被赋予任何类型对象的地址
void *general;
  • 以往用0或者NULL去表达空指针的问题
    • C/C++的NULL宏是个有很多潜在BUG的宏。因为有的库把其定义为整数0,有的定义成(void*)0.
    • C++11使用nullptr关键字,是表达更准确,类型安全的空指针
指向常量的指针

不能通过指向常量的指针改变所指对象的值,但指针本身可以改变,可以指向另外的对象

int a;
const int *p1 = &a; //p1是指向常量的指针
int b;
p1 = &b; //正确,p1本身的值可以改变
*p1 = 1;//编译时出错,不能通过p1改变所指的对象
b=6;
指针类型的常量

若声明指针常量,则指针本身的值不能被改变,指针本身是常量

int a;
int * const p2 = &a;
*p2 = 3;  //可以
int b;
p2 = &b;  //错误,p2是指针常量,值不能被改变
指针数组
  • 数组的元素是指针型
Point *pa[2];
// 由pa[0],pa[1]两个指针组成

利用数组与二维数组对比比

#include <iostream>
using namespace std;

int main(void)
{
	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;
	}

	return 0;
}
/*
Matrix test:
1 0 0
0 1 0
0 0 1
*/
以指针作为函数参数
  • 需要数据双向传递时(引用也可以达到响应效果)

    用指针作为函数的参数,可以使被调函数通过形参指针存取主调函数中实参指针指向的数组,实现数据的双向 传递

  • 小传递一组数据,只传首地址运行效率比较高

    实参是数组名时形参可以是指针

/*
project:从浮点数中取整数和浮点数
注:浮点数在机器内部近似存储
*/
#include <iostream>
using namespace std;
void splitFloat(float x, int* intpart, float* fracPart)
{
	*intpart = static_cast<int>(x); //取x的整数部分
	*fracPart = x - *intpart; //取x的小数部分
}
int main(void)
{
	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 << " Fraction Part = " << f << endl;
	}
	return 0;
}
/*
Enter 3 float point numbers:
3.14
integer Part=3 Fraction Part = 0.14
9.7
integer Part=9 Fraction Part = 0.7
5
integer Part=5 Fraction Part = 0
*/
指针常量
/* 
Project:有关常量的几个问题
*/
#include <iostream>
using namespace std;
const int N = 6;   //N值不可修改
int const M = 9;
const int* p; //*p不可修改,p可以更改指针
int const *q = 0; //可通过赋值指针修改
const int* const A = &N; //只可初始化,不可膝盖
int main(void)
{
	//N = 7;//错误
	//M = 5; //此值也不允许修改
	int a = 7;
	p = &a;  //地址不能改变,也不能*p赋值
	//*p = 9; //左值也不可修改
	p = &N;
	//*q = 9; //左值不可修改
	q = &a;
	cout << *q << endl;
	return 0;
}

/*
project:使用const进行权限管理
*/
#include <iostream>
using namespace std;
const int N = 6;
void print(const int* p, int n) //加const常量,其只有读的权限,不能修改
{
	cout << "{" << *p;
	for (int i = 1; i < n; i++)
	{
		cout << "," << *(p + i);
	}
	cout << "}" << endl;
}
int main(void)
{
	int array[N];
	for (int i = 0; i < N; i++)
	{
		cin >> array[i];
	}
	print(array, N);
	return 0;
}
/*1 2 3 4 5 6
{1,2,3,4,5,6}
*/
指针类型的函数
存储类型 数据类型 *函数名()
{
	//函数体语句
}

注意:

  • 不要将非静态局部地址用作函数的返回值
    • 子函数中定义局部变量后将其地址返回给主函数,就是非法地址
  • 返回的指针要确保在主调函数中是有效,合法的地址
    • 主函数中定义的数组,在子函数中对该数组元素进行某种操作后,返回其中一个元素的地址
    • 在子函数中通过动态内存分配new操作取得的内存地址返回给主函数是合法有效的,但是内存分配和释放不在同一级别,要注意不能忘记释放,避免内存泄露。
#include <iostream>
int* newintvar()
{
	int* p = new int();  
	return p; //返回的地址指向的是动态分配的空间
	//函数运行结束时,p中的地址仍有效
}
int main(void)
{
	int* newintvar();
	int* intptr = newintvar();
	*intptr = 5; //访问的是合法有效的地址
	delete intptr;  //如果忘记在这里释放,会造成内存泄露
	return 0;
}

内存泄露:程序中已分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

函数指针的定义:函数指针指向的是程序代码存储区。
存储类型 数据类型 (*函数指针名);
函数指针的典型用途
  • 通过函数指针调用的函数
    • 将函数的指针作为参数传递给一个函数,使得在处理相似事件的时候可以灵活的使用不同的方法
  • 调用者不关心谁是被调用者
    • 需直到存在一个具有特定原型和限定条件的被调用函数
#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(void)
{
	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;
}
对象指针
  • 对象指针定义形式
    • 类名 * 对象指针名
Point a(5,10);
Point *ptr;
ptr = &a;
  • 通过指针访问对象成员
    • 对象指针名->成员名
ptr->getx() 相当于(*ptr).getx();

#include <iostream>
using namespace std;
class Point
{
public:
	Point(int x = 0, int y = 0) :x(x), y(y)
	{
	}
	int getX() const 
	{ 
		return x; 
	}
	int getY() const
	{
		return y;
	}
private:
	int x, y;
};
int main(void)
{
	Point a(4, 5);
	Point* p1 = &a;  //定义对象指针,用a的地址初始化
	cout << p1->getX() << endl; //用指针访问对象成员
	cout << a.getY() << endl;	//用对象名访问对象成员
	return 0;
}
this 指针
  • 隐含于类的每一个非静态成员函数中
  • 指出成员函数所操作的对象
    • 当通过一个对象调用成员函数时,系统先将该对象的地址赋给this指针,然后调用成员函数,成员函数对对象的数据成员进行操作时,就隐含使用了this指针
    • Point类的getX函数中return x相当于return this->x;
前向引用声明
class Fred; //前向引用生命
class Barney
{
	Fred *x;  //不使用指针的话,由于编译器不知道fred细节,没法分配空间等操作,将报错,使用对象指针达到效果
}
class Fred
{
	Barney y;
}
动态内存分配
  • new 类型名T(初始化参数)
  • 功能:
    • 在程序执行期间,申请用于存放T类型对象的存储空间,并依次初始化参数进行初始化。
    • 基本类型初始化:如果有初始化参数,依初始化参数进行初始化,如果没有括号和初始化参数,不进行初始化,新分配的内存中内容不确定,如果有括号但初始化参数为空,初始化为0
    • 对象类型:如果有初始化参数,以初始化参数中的值为参数调用构造函数进行初始化,如果没有括号和初始化参数或者有括号但初始化参数为空,用默认构造函数初始化
  • 结果值:成功:T类型的指针,指向新分配的内存,失败:抛出异常

动态申请动态数组
  • new 类型名T[表达式][常量表达式]…()
  • 功能:
    • 在程序执行期间,申请用于存档T类型对象数组的内存空间,可以有"()"但初始化列表必须为空
    • 如果有"()",对每个元素的初始化与执行"new T()"所做进行初始化的方式相同
    • 如果没有"()",对每个元素的初始化与执行"new T"所做进行初始化的方式相同
  • 结果值
    • 如果内存申请成功,返回一个指向新分配内存首地址的指针
    • 如果失败,抛出异常
double* array = new double[n]();
char(*fp)[3];  //一维数组
fp = new char[n][3];
释放内存操作符delete
  • delete 指针p
  • 功能:释放指针p所指向的内存,p必须是new操作的返回值
  • delete[] 指针p
  • 功能:释放指针p所指向的数组,p必须是用new分配得到的数组首地址

#include <iostream>
using namespace std;
class Point
{
public:
	Point(int x, int y)
	{
		cout << "Destructor called." << endl;
	};
	Point() :x(x), y(y)
	{
		cout << "Default Destructor called." << endl;
	};
	~Point()
	{
		cout << "Destructor called" << endl;
	}
	int getX() const { return x; } //常函数
	int getY() const { return y; }
	void move(int newX, int newY)
	{
		x = newX;
		y = newY;
	}
private:
	int x, y;
};

int main(void)
{
	cout << "step one:" << endl;
	Point* ptr1 = new Point;//调用默认构造函数
	delete ptr1;  //删除对象,自动调用析构函数

	cout << "Step two" << endl;
	ptr1 = new Point(1, 2);
	delete ptr1;
	return 0;
}
/*
step one:
Default Destructor called.
Destructor called
Step two
Destructor called.
Destructor called
*/

#include <iostream>
using namespace std;
class Point
{
public:
	Point(int x, int y)
	{
		cout << "Destructor called." << endl;
	};
	Point() :x(x), y(y)
	{
		cout << "Default Destructor called." << endl;
	};
	~Point()
	{
		cout << "Destructor called" << endl;
	}
	int getX() const { return x; } //常函数
	int getY() const { return y; }
	void move(int newX, int newY)
	{
		x = newX;
		y = newY;
	}
private:
	int x, y;
};

int main(void)
{
	Point* ptr = new Point[2];//创建对象数组
	ptr[0].move(5, 10); //通过指针访问数组元素的成员
	ptr[1].move(15, 20); 
	cout << "Deleting..." << endl;
	delete[] ptr; //删除整个对象数组
	return 0;
}
/*
Default Destructor called.
Default Destructor called.
Deleting...
Destructor called
Destructor called
*/
创建动态数组
#include <iostream>
using namespace std;
int main(void)
{
	int n;
	cin >> n;
	//创建动态内存分配孔家
	int(*cp)[9][8] = new int[n][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; //释放整个动态多维数组空间

}
将动态数组封装成类
  • 更加简介,便于管理
    • 建立和删除数组的过程比较繁琐
    • 封装成类后更加简洁,便于管理
  • 可以在访问数组元素前检查下标是否越界
    • 用assert来检查,assert只在调试时生效
#include <iostream>
#include <cassert>
using namespace std;
class Point
{
public:
	Point(int x, int y)
	{
		cout << "Destructor called." << endl;
	};
	Point() :x(x), y(y)
	{
		cout << "Default Destructor called." << endl;
	};
	~Point()
	{
		cout << "Destructor called" << endl;
	}
	int getX() const { return x; } //常函数
	int getY() const { return y; }
	void move(int newX, int newY)
	{
		x = newX;
		y = newY;
	}
private:
	int x, y;
};
class ArrayOfPoints  //创建动态数组类
{
public:
	ArrayOfPoints(int size) :size(size)
	{
		points = new Point[size];//建立数组,就要想起在何处释放
	}
	~ArrayOfPoints()
	{
		cout << "Deleting ..." << endl;  //释放数组 
		delete[] points;
	}
	Point& element(int index) //返回引用
	{
		assert(index >= 0 && index < size);
		return points[index];  //返回
	}
private:
	Point* points;//指向动态数组首地址
	int size; //数组大小
};


int main(void)
{
	int count;
	cout << "Please enter the count of points:";
	cin >> count;
	ArrayOfPoints points(count); //创建数组对象
	points.element(0).move(5, 0); //访问数组元素的成员
	points.element(1).move(15, 20); //访问数组元素的成员
	return 0;
}
/*
Please enter the count of points:3
Default Destructor called.
Default Destructor called.
Default Destructor called.
Deleting ...
Destructor called
Destructor called
Destructor called
*/
为什么element函数返回对象的引用
  • 返回"引用"可以用来操作封装数组对象内部的数据元素,如果返回"值"则只是返回了一个副本,通过副本是无法操作原来数组中的元素的。
vector容器
  • 封装任何类型的动态数组
  • 数组下标越界检查
vector容器定义:
  • vector<元素类型>数组对象名(数组长度)
  • vectorarr(5) //建立大小为5的int数组
#include <iostream>
#include <vector>
using namespace std;
//计算数组arr中元素的平均值
double average(const vector<double>& arr)
{
	double sum = 0;
	for (unsigned i = 0; i < arr.size(); i++)
	{
		sum += arr[i];
	}
	return sum / arr.size();
}
int main(void)
{
	unsigned n;
	cout << "n = ";
	cin >> n;
	vector<double>arr(n); //创建数组对象
	cout << "Please input " << n << " real number:" << endl;
	for (unsigned i = 0; i < n; i++)
	{
		cin >> arr[i];
	}
	cout << "Average = " << average(arr) << endl;
	return 0;
}
基于for循环配合auto举例
/*
* 基于范围的for循环配合auto举例
*/
#include <vector>
#include <iostream>
using namespace std;
int main(void)
{
	vector<int>v = { 1,2,3 };
	/*v.begin(),指向首元素的迭代器,可以认为是指向首元素的“泛型”指针*/
	for (auto i = v.begin(); i != v.end(); ++i) //auto是i的类型根据后面变
	{
		cout << *i << endl;
	}
	for (auto e : v)
	{
		cout << e << endl;
	}

}

		sum += arr[i];
	}
	return sum / arr.size();
}
int main(void)
{
	unsigned n;
	cout << "n = ";
	cin >> n;
	vector<double>arr(n); //创建数组对象
	cout << "Please input " << n << " real number:" << endl;
	for (unsigned i = 0; i < n; i++)
	{
		cin >> arr[i];
	}
	cout << "Average = " << average(arr) << endl;
	return 0;
}
基于for循环配合auto举例
/*
* 基于范围的for循环配合auto举例
*/
#include <vector>
#include <iostream>
using namespace std;
int main(void)
{
	vector<int>v = { 1,2,3 };
	/*v.begin(),指向首元素的迭代器,可以认为是指向首元素的“泛型”指针*/
	for (auto i = v.begin(); i != v.end(); ++i) //auto是i的类型根据后面变
	{
		cout << *i << endl;
	}
	for (auto e : v)
	{
		cout << e << endl;
	}

}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值