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(&#39;\n&#39;);
    free(ptr);

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

int main()
{
    int* ptr;

    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);

    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.
*/