c++中,动态数组的动态扩容

c++中,动态数组的动态扩容

  • 方法说明
  • 代码
  • 程序运行截图

在程序开发时,我们有些时候不可能预先知道一个数组的最大储存量是多少,所以需要在程序运行时,代码自动的从堆(heap)中申请分配一段内存,堆是一个很大的内存块,用以在程序运行时分配一些小的储存空间。本文通过以下两个例子来阐述c++动态数组的动态扩容机制:

  1. 从给定范围内(start,end)找寻素数并储存在一个一维数组中——函数GetPrimerNumber();
  2. 读取文本,将所有的单词储存到一个二维数组中——函数GetWordsFromTxt();

方法说明

标准c中,动态内存分配函数包括malloc()、calloc()、realloc()和free()。c++不是采用库函数调用的方法,而是采用更加高级的方法——被集成进c++中的动态存储分配,使用关键字new和delete
c++通过new关键字向堆(heap)申请内存,使用完毕后,需要用delete关键字显式的告诉堆管理器回收内存。
注意:

  1. 任何时候申请内存都有可能失败(一般来是申请特别大的内存时),例如存储单元用完了,c++ 内存申请失败的时候会抛出bad_alloc异常,可以用try-catch来处理;
  2. 任何由new分配的内存块必须用delete来释放,如果忘记了使用delete,这个内存块就不能用了,这被称为内存泄漏(memory leak),泄漏到一定程度,内存就耗尽了;
  3. 对于二维数组或者多维数组,申请内存时是由外而内,释放内存时是由内而外,具体见代码;
  4. 动态数组不能作为形参传给函数,因为通过new和delete操作,主函数传递给子函数的动态数组地址与子函数实际运行结果的动态数组地址不一样,我们可以用一小段代码来测试:`
int main()
{
	int *result = new int[1];
	//初始化,赋值为0
	result[0] = 0;
	cout << "主函数动态数组地址(函数未调用前):" << result << endl;
	test(result);
	cout << "主函数动态数组地址(函数调用后):" << result << endl;		
	return 0;
}
void test(int *arr)
{
	delete arr;
	arr = new int[2];
	arr[0] = arr[1] = 2;
	cout << "子函数动态数组地址:"<<arr << endl;
}

其运行结果为:
在这里插入图片描述
可见,不能用动态数组作为子函数形参。

代码

说明:

  1. 代码中判断新单词出现的依据是:前一个字符不是字母且当前字符为字母,所以当出现“un-friendly”、"I’m"等这种中间有非字母连接的单词时,将其认为是两个单词,读者就这一点可以进行优化;
  2. 本代码读取的文本内容为:“For a start,leaving lockdown is a process, not an event.Even when the worst is over, cases ebb slowly. A month after Italy’s deaths peaked at about 900 a day, the toll is still over 300. With the virus still present, some social distancing is bound to stay.”
#include 
#include 
#include 
using namespace std;
#define MAXLEN 30 //单词最长为30个字符
int * GetPrimerNumber(int start, int end, int *PrimerNumCounts);
char ** GetWordsFromTxt(string file_name, int *word_counts);
int main()
{
	int start, end;//范围 (start,end)
	int WordCounts = 0;
	int PrimerNumCounts = 0;
	int *t = &PrimerNumCounts;
	int *p = &WordCounts;
	string filename = "test.txt";
	//输入起点,终点
	cout << "请输入起点(大于等于2):";
	cin >> start;
	cout << "请输入终点:";
	cin >> end;
	//定义二维数组并赋值,获取单词总数
	char **allWords = GetWordsFromTxt(filename,p);
	//定义一维数组并赋值,获取素数总数
	int *PrimerNum = GetPrimerNumber(start,end,t);
	//输出各个素数
	cout << "各个素数为:" ;
	for (int i = 0; i < PrimerNumCounts; i++)
	{
		cout << PrimerNum[i] << "  ";
	}
	cout << endl;
	//输出各个单词
	cout << "读取文本获得单词依次为:" << endl;
	for (int i = 0; i < WordCounts; i++)
	{
		for (int j = 0; j < MAXLEN; j++)
			if (allWords[i][j] != '\0')
				cout << allWords[i][j];
		cout << " ";
	}		
	return 0;
}
int * GetPrimerNumber(int start, int end, int *PrimerNumCounts)
{
	/*
@param
	start 范围-起始
	end 范围-终止
	PrimerNumCounts 素数个数
@return
	result[] 素数一维数组
@explain
	本函数根据范围[start,end],获取中间所有的素数,并将所有素数存放在一维数组中
	*/
	int NumCounts = 0;//素数个数
	bool IsPrimerNum = true;
	//新建一个一维数组,包含一个数
	int *result = new int[1];
	//初始化,赋值为0
	result[0] = 0;
	for (int i = start; i <= end; i++)
	{
		IsPrimerNum = true;
		for (int j = 2; j < i; j++)
			if (i%j == 0)//如果不是素数
			{
				IsPrimerNum = false;
				break;
			}		
		//如果是素数
		if (IsPrimerNum == true)
		{
			NumCounts++;//素数个数增加
			if (NumCounts == 1) result[0] = i;//如果是第一个,不需要扩容
			else
			{
			//动态数组扩容
			  //1.备份已有的数组
				//1.1引入中间数组
				int *arr = new int[NumCounts];
				//1.2中间数组赋初值
				for (int i = 0; i < NumCounts; i++)
					arr[i] = 0;
				//1.3备份
				for (int i = 0; i < NumCounts - 1; i++)
					arr[i] = result[i];

				//2.对目标数组扩容
					//2.1回收原来的result内存 
				delete []result;
				//2.2分配新的内存
				result = new int[NumCounts];
				//2.3新的数组赋初值
				for (int i = 0; i < NumCounts; i++)
					result[i] = 0;
				//3.复制原来的内容到扩容后的数组
					//3.1挨个复制
				for (int i = 0; i < NumCounts - 1; i++)
					result[i] = arr[i];
					//3.2回收中间数组内存 由内而外释放内存
				delete[]arr;
					//储存新素数
				result[NumCounts - 1] = i;
			}						
		}		
	}		
	(*PrimerNumCounts) = NumCounts;
	return result;
}
char ** GetWordsFromTxt(string file_name, int *word_counts)
{
	/*
	@param 
		filename 文件名 
		word_counts 单词总数
	@return
	    result[][] 单词二维数组
	@explain
	    本函数依次读取文件的每个字符,获取单词,并将所有单词存放在二维数组中,二维数组的行数为单词数,列数为MAXLEN
		*/
	bool IF_new_word = false;//连续两个字符中,有一个不为字符,则认为出现了新的单词
	char c;//读文件时获得的字符
	int WordCounts = 1;//所有单词的数量,也是新单词的下标+1
	int WordLen = 0;//当前单词的长度-1,也是新单词的新字符的下标
	//定义并分配二维数组为一个单词
	char **result = new char *[1];
	for (int i = 0; i < 1; i++)
		result[i] = new char[MAXLEN];
	//给二维数组赋初值
	for (int i = 0; i < 1; i++)
		for (int j = 0; j < MAXLEN; j++)
			result[i][j] = '\0';
	fstream inFile;
	inFile.open(file_name);
	if (!inFile.is_open()) {
		cout << "Could not find the file\n";
		cout << "Program terminating\n";
		exit(EXIT_FAILURE);
	}
	inFile.unsetf(ios::skipws);//取消 inFile输入跳过空白的默认设置
	while (!inFile.eof()) {
		if (inFile.good()) {//判断当前流是否正常
			inFile >> c;//读取字符
			//cout << c << "  ";
			if (((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) && IF_new_word == false)//如果当前为字母且前一个字符为字母,此时说明本单词还未结束
			{
				result[WordCounts - 1][WordLen++] = c;//储存第WordCounts-1(WordCounts从1开始)个单词的第WordLen个字符
			}
			else if (((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) && IF_new_word != false)//如果当前为字母且前一个字符不为字母,此时说明出现了新单词
			{
				WordCounts++;//单词数加一
				WordLen = 0;//长度重新赋值为0
			//1.备份已有的数组
				//1.1引入中间数组
				//由外而内申请内存
				char **cur_arr = new char *[WordCounts];
				for (int i = 0; i < WordCounts; i++)
				{
					cur_arr[i] = new char[MAXLEN];
				}
				//1.2中间数组赋初值
				for (int i = 0; i < WordCounts; i++)
					for (int j = 0; j < MAXLEN; j++)
						cur_arr[i][j] = '\0';
				//1.3备份
				for (int i = 0; i < WordCounts - 1; i++)
					for (int j = 0; j < MAXLEN; j++)
						if (result[i][j] != '\0')
							cur_arr[i][j] = result[i][j];
				//2.对目标数组扩容
					//2.1回收原来的result内存 由内而外释放内存
				for (int i = 0; i < WordCounts - 1; i++)
					delete[] result[i];
				delete[] result;
				//2.2分配新的内存
				result = new char *[WordCounts];
				for (int i = 0; i < WordCounts; i++)
					result[i] = new char[MAXLEN];
				//2.3新的数组赋初值
				for (int i = 0; i < WordCounts; i++)
					for (int j = 0; j < MAXLEN; j++)
						result[i][j] = '\0';
				//3.复制原来的内容到扩容后的数组
					//3.1挨个复制
				for (int i = 0; i < WordCounts - 1; i++)
					for (int j = 0; j < MAXLEN; j++)
						if (cur_arr[i][j] != '\0')
							result[i][j] = cur_arr[i][j];
				//3.2回收中间数组内存 由内而外释放内存
				for (int i = 0; i < WordCounts; i++)
					delete[] cur_arr[i];
				//储存新单词的第一个字符
				result[WordCounts - 1][WordLen++] = c;//储存第WordCounts(0开始)个单词的第WordLen个字符
			}

			if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
				IF_new_word = true;
			else IF_new_word = false;
		}
	}
	(*word_counts) = WordCounts;//获得单词数
	return result;
}

程序运行截图

c++中,动态数组的动态扩容_第1张图片

你可能感兴趣的:(c++学习)