动态内存管理

C语言

malloc

void malloc(size_t size);
size_t就是unsign int无符号正整数

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int* ptr = NULL;
	int num, i;
	printf("请输入待录入整数的个数:");
	scanf_s("%d", &num);

	ptr = (int*)malloc(num * sizeof(int));

	for (i = 0; i < num; i++)
	{
		printf("请录入第%d个整数:", i + 1);
		scanf_s("%d", &ptr[i]);
	}

	printf("The number you scanf is:");
	for (i = 0; i < num; i++)
	{
		printf("%d ", ptr[i]);
	}
	putchar('\n');
	free(ptr);

	return 0;
}
#include <stdio.h>
#include <stdlib.h>

int main()
{
	int* ptr;

	ptr = (int*)malloc(sizeof(int));
	if (ptr == NULL)
	{
		printf("It's wrong!分配内存失败!\n");
		exit(1);
	}

	printf("please input a number:");
	scanf_s("%d", ptr);

	printf("The number is:%d\n", *ptr);

	free(ptr);
	//释放掉地址后变为非法空间,所以不会打印出任何东西。
	printf("The number is:%d\n", *ptr);

	return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	int* ptr1 = NULL;
	int* ptr2 = NULL;

	//第一次申请的内存空间
	ptr1 = (int*)malloc(10 * sizeof(int));

	//第一次申请的内存空间不够用

	//第二次申请内存空间
	ptr2 = (int*)malloc(20 * sizeof(int));
	
	//将ptr1的数据拷贝到ptr2中,memcpy是拷贝,在头文件string.h中
	memcpy(ptr2, ptr1, 10);
	free(ptr1);

	//对ptr2申请的空间做了操作之后

	free(ptr2);
	
	return 0;
}

free

void free(void *ptr)
用来释放ptr参数指向的内存空间,必须是由malloc、calloc或relloc函数申请的。
如果ptr参数是NULL,则不执行任何操作
注意:该函数不会修改ptr参数的值,所以调用后它仍指向原来的地方(变为非法空间)

内存泄漏

1、隐式内存泄漏:用完内存块没有及时用free释放
2、丢失内存块地址

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    //没有释放内存空间导致系统崩溃。(内存泄漏)
    while(1)
    {
        malloc(1024); 
    }
    
    return 0;
}
#include <stdio.h>
#include <stdlib.h>

int main()
{
	int* ptr;
	int num = 123;

	ptr = (int*)malloc(sizeof(int));
	if (ptr == NULL)
	{
		printf("It&#39;s wrong!分配内存失败!\n");
		exit(1);
	}

	printf("please input a number:");
	scanf_s("%d", ptr);

	printf("The number is:%d\n", *ptr);

	//这里把num的地址放到了ptr里面导致前面使用malloc申请的内存块丢失
	ptr = &num;
	printf("The number is:%d\n", *ptr);
	
	//这里相当于直接对局部变量进行了位释放
	free(ptr);

	return 0;
}

calloc

void *calloc(size_t, size_t size);
calloc函数在内存中动态地申请nmemb个长度为size的连续内存空间(空间总尺寸为nmemb * size),而且全部初始化为0,相当于malloc加上memset。
而malloc函数不进行初始化操作,里面的数据是随机的

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define N 10

int main()
{
	int* ptr = NULL;
	int i;

	/*
	int* ptr = (int*)calloc(8, sizeof(int));
	==
	int* ptr = (int*)malloc(8 * sizeof(int));
	memset(ptr, 0, 8 * sizeof(int));
	*/

	ptr = (int*)malloc(N * sizeof(int));
	if (ptr == NULL)
	{
		exit(1);
	}

	//memset 函数使用常量字节c填充s指向的前n个字节,在string头文件里面
	//void* memset(void* s, int c, size_t n);
	//这里作用貌似就是用0填充一个用malloc创造出空间的ptr数组(数组内一共有十个全是0的元素)

	memset(ptr, 0, N * sizeof(int));
	for (i = 0; i < N; i++)
	{
		printf("%d ", ptr[i]);
	}
	putchar(&#39;\n&#39;);
	// 运行结果是0 0 0 0 0 0 0 0 0 0

	free(ptr);

	return 0;
}

realloc

void *realloc(void *ptr, size_t size);
realloc函数修改ptr指向的内存空间大小为size字节
如果新分配的内存空间比原来的打,则就内存块的数据不会发生改变;但是如果新的内存空间大小小于旧的内存空间,可能会导致数据丢失,慎用!
该函数将移动内存空间的数据并返回新的指针

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int i, num;
	int count = 0;
	int* ptr = NULL; // 注意这里必须初始化为NULL

	do
	{
		printf("please scanf a int(输入-1表示结束):");
		scanf("%d", &num);
		count++;

		/*
		如果ptr参数是NULL,调用该函数相当于调用malloc(size)
		如果size参数为0,并且ptr参数不为NULL,那么调用该函数相当于调用free(ptr)
		除非ptr参数为NULL,否则ptr的值必须由先前调用malloc、calloc、realloc函数返回
		*/

		ptr = (int*)realloc(ptr, count * sizeof(int));
		if (ptr == NULL)
		{
			exit(1);
		}

		ptr[count - 1] = num;
	} while (num != -1);

	printf("The number you input is :");
	for (i = 0; i < count; i++)
	{
		printf("%d", ptr[i]);
	}
	putchar(&#39;\n&#39;);

	free(ptr);
	
	return 0;
}

C语言的内存布局

#include <stdio.h>
#include <stdlib.h>

int global_uninit_var;
int global_init_var1 = 520;
int global_init_var2 = 880;

void func(void);

void func(void)
{
	;
}

int main()
{
	int local_var1;
	int local_var2;

	static int static_uninit_var;
	static int static_init_var = 456;

	char* str1 = "I love Fishc.com!";
	char* str2 = "You are right!";

	int* malloc_var = (int*)malloc(sizeof(int));

	printf("addr of func -> %p\n", &func);
	printf("addr of str1 -> %p\n", &str1);
	printf("addr of str2 -> %p\n", &str2);
	printf("addr of global_init_var1 -> %p\n", &global_init_var1);
	printf("addr of global_init_var2 -> %p\n", &global_init_var2);
	printf("addr of static_init_var -> %p\n", &static_init_var);
	printf("addr of static_uninit_var -> %p\n", &static_uninit_var);
	printf("addr of global_uninit_var -> %p\n", &global_uninit_var);
	printf("addr of malloc_var -> %p\n", &malloc_var);
	printf("addr of local_var1 -> %p\n", &local_var1);
	printf("addr of local_var2 -> %p\n", &local_var2);
	
	/*
	addr of func -> 006313A7
	addr of str1 -> 007DFB10
	addr of str2 -> 007DFB04
	addr of global_init_var1 -> 0063A034
	addr of global_init_var2 -> 0063A038
	addr of static_init_var -> 0063A03C
	addr of static_uninit_var -> 0063A13C
	addr of global_uninit_var -> 0063A46C
	addr of malloc_var -> 007DFAF8
	addr of local_var1 -> 007DFB28
	addr of local_var2 -> 007DFB1C
	*/

	return 0;
}

小甲鱼的图


C++

动态申请内存操作符new

new 类型名T(初始化参数列表)

  • 功能:
    在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以初值。
  • 结果值:
    成功:T类型的指针,指向新分配的内存; 失败:抛出异常。

释放内存操作符delete

delete 指针p

  • 功能:释放指针p所指向的内存。
    p必须是new操作的返回值
#include <iostream>
using namespace std;

class Point {
public:
	Point() :x(0), y(0) {
		cout << "Default Constructor called." << endl;
	}
	Point(int x, int y) :x(x), y(y) {
		cout << "Constructor 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()
{
	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 Constructor called.
Destructor called.
Step two:
Constructor called.
Destructor called.
*/

分配和释放动态数组

  • 分配:new类型名T{数组长度}
    数组长度可以是任何整数类型表达式,在运行时计算
  • 释放:delete[] 数组名p
    释放指针p所指向的数组。
    p必须是用new分配得到的数组首地址。
#include <iostream>
using namespace std;

class Point {
public:
	Point() :x(0), y(0) {
		cout << "Default Constructor called." << endl;
	}
	Point(int x, int y) :x(x), y(y) {
		cout << "Constructor 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()
{
	Point* ptr = new Point[2]; // 创建对象数组
	ptr[0].move(5, 10); // 通过指针访问数组元素的成员
	ptr[1].move(15, 20); // 通过指针访问数组元素的成员
	cout << "Deleting..." << endl;
	delete[] ptr; // 删除整个对象数组(用两个方括号,用两个能释放两个内存空间,只用一个只释放一个内存空间,导致内存泄漏)
	return 0;
}
/*
Default Constructor called.
Default Constructor called.
Deleting...
Destructor called.
Destructor called.
*/

动态创建多维数组

new 类型名T[第1维长度][第2维长度]…;

  • 如果内存申请成功,new运算返回一个指向新分配内存首地址的指针。
  • 例如:
    char(*fp)[3]; fp = new char[2][3];
#include <iostream>
using namespace std;

int main()
{
	// cp是指向数组(二维数组)的指针,多维数组去掉第一维的下标个数
	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;
}
#include <iostream>
#include <cassert>
using namespace std;

class Point {
public:
    Point() :x(0), y(0) {
        cout << "Default Constructor called." << endl;
    }
    Point(int x, int y) :x(x), y(y) {
        cout << "Constructor 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 << "Deleteing..." << endl;
        delete[] points;
    }
    Point& element(int index) {
        assert(index >= 0 && index < size);
        return points[index];
    }
private:
    Point* points; // 指向动态数组首地址
    int size;      // 数组大小
};

int main()
{
    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: 2
Default Constructor called.
Default Constructor called.
Deleteing...
Destructor called.
Destructor called.
*/

#vector

为什么需要vector?

  • 封装任何类型的动态数组,自动创建和删除。
  • 数组下标越界检查。

vector对象的定义
vector<元素类型> 数组对象名(数组长度);
例:vector<int> arr(5) 建立大小为5的int数组

#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];
	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 int i = 0; i < n; i++)
		cin >> arr[i];
	cout << "Average = " << average(arr) << endl;
	return 0;
}
#include <iostream>
#include <vector>
using namespace std;

int main()
{
	std::vector<int>v = { 1, 2, 3 };
	for (auto i = v.begin(); i != v.end; ++i)
		std::cout << *i << std::endl;

	for (auto e : v)
		std::cout << e << std::endl;

	return 0;
}

浅层复制

  • 实现对象间数据元素的一一对应复制。
    不能复制指针(当这个指针指向的空间是用动态内存分配的空间)

深层复制

  • 当被复制的对象数据成员是指针类型时,不是复制该指针成员本身,而是将指针所指对象进行复制。(需要开辟新的动态内存空间)
#include <iostream>
#include <cassert>
using namespace std;

class Point {
public:
    Point() :x(0), y(0) {
        cout << "Default Constructor called." << endl;
    }
    Point(int x, int y) :x(x), y(y) {
        cout << "Constructor 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(const ArrayOfPoints& pointsArray);
    ArrayOfPoints(int size) : size(size) {
        points = new Point[size];
    }
    ~ArrayOfPoints() {
        cout << "Deleteing..." << endl;
        delete[] points;
    }
    Point& element(int index) {
        assert(index >= 0 && index < size);
        return points[index];
    }
private:
    Point* points; // 指向动态数组首地址
    int size;      // 数组大小
};

ArrayOfPoints::ArrayOfPoints(const ArrayOfPoints& v)
{
    size = v.size; // size的值可以直接复过来
    points = new Point[size]; // 指针的不能直接,要开辟新的空间
    for (int i = 0; i < size; i++)
        points[i] = v.points[i];
}

int main()
{
    int count;
    cout << "Please enter the count of points: ";
    cin >> count;
    ArrayOfPoints pointsArray1(count); // 创建对象数组
    pointsArray1.element(0).move(5, 10);
    pointsArray1.element(1).move(15, 20);

    ArrayOfPoints pointsArray2(pointsArray1); // 创建副本,使用的是默认的复制构造函数,只能复制地址,所以在析构的时候使用了两次delete导致出错

    cout << "Copy of pointArray1:" << endl;
    cout << "Point_0 of array2: " << pointsArray2.element(0).getX() << ", "
        << pointsArray2.element(0).getY() << endl;
    cout << "Point_1 of array2: " << pointsArray2.element(1).getX() << ", "
        << pointsArray2.element(1).getY() << endl;

    pointsArray1.element(0).move(25, 30);
    pointsArray2.element(1).move(35, 40);

    cout << "After the moving of pointsArray1:" << endl;

    cout << "Point_0 of array2: " << pointsArray2.element(0).getX() << ", "
        << pointsArray2.element(0).getY() << endl;
    cout << "Point_1 of array2: " << pointsArray2.element(1).getX() << ", "
        << pointsArray2.element(1).getY() << endl;

    return 0;
}
/*
Please enter the count of points: 2
Default Constructor called.
Default Constructor called.
Default Constructor called.
Default Constructor called.
Copy of pointArray1:
Point_0 of array2: 5, 10
Point_1 of array2: 15, 20
After the moving of pointsArray1:
Point_0 of array2: 5, 10
Point_1 of array2: 35, 40
Deleteing...
Destructor called.
Destructor called.
Deleteing...
Destructor called.
Destructor called.
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值