【相关题目】

代码语言:javascript

AI代码解释

int globalVar = 1;

static int staticGlobalVar = 1;

void Test()

{

	static int staticVar = 1;

	int localVar = 1;

	

	int num1[10] = {1, 2, 3, 4};

	char char2[] = "abcd";

	const char* pChar3 = "abcd";

	int* ptr1 = (int*)malloc(sizeof (int)*4);

	int* ptr2 = (int*)calloc(4, sizeof(int));

	int* ptr3 = (int*)realloc(ptr2, sizeof(int)*4);

	free (ptr1);

	free (ptr3);

}

1. 选择题:

选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)

globalVar在哪里? C staticGlobalVar在哪里?__C__ staticVar在哪里?__C__ localVar在哪里?__A__ num1 在哪里?__A__ 【分析】:

  • globalVar全局变量在数据段
  • staticGlobalVar静态全局变量在静态区
  • staticVar静态局部变量在静态区
  • localVar局部变量在栈区
  • num1局部变量在栈区

char2在哪里?__A__ *char2在哪里?__A__

pChar3在哪里?__A__ *pChar3在哪里?__D__

ptr1在哪里?__A__ *ptr1在哪里?__B__

【分析】:

  • char2局部变量在栈区
  • char2是一个数组,把后面常量串拷贝过来到数组中,数组在栈上,所以*char2在栈上
  • pChar3局部变量在栈区
  • *pChar3得到的是字符串常量字符在代码段
  • ptr1局部变量在栈区
  • *ptr1得到的是动态申请空间的数据在堆区

2. 填空题:

sizeof(num1) = __40__; //数组大小,10个整形数据一共40字节 sizeof(char2) = __5__; //包括\0的空间 strlen(char2) = __4__; //不包括\0的长度 sizeof(pChar3) = __4__; //pChar3为指针 strlen(pChar3) = __4__; //字符串“abcd”的长度,不包括\0的长度 sizeof(ptr1) = __4__; //ptr1是指针

二、栈与堆对比

栈和堆是日常开发中最常接触的内存区域,二者的差异直接决定了使用场景的选择,以下是全方位对比:

特性

栈区(Stack)

堆区(Heap)

管理方式

编译器自动分配释放

程序员手动通过new/delete等接口管理

空间大小

较小(几 MB)

较大(受系统内存限制)

访问速度

极快(栈指针操作)

较慢(需内存寻址)

生命周期

随作用域自动销毁

显式释放前持续存在

内存碎片

频繁分配释放易产生碎片

增长方向

从高地址到低地址

从低地址到高地址

典型场景

局部变量、函数参数

大对象、跨作用域数据

风险点

栈溢出(过大局部变量)

内存泄漏(未释放)、野指针

三、动态内存管理

C 和 C++ 都支持堆内存的动态管理,但提供了不同的接口,使用时需注意语法规范和匹配原则,避免混用导致内存问题。

1. C语言:malloc/calloc/reallocfree

  • malloc(size_t size):分配指定字节数的堆内存,返回 void * 指针,需手动类型转换。
  • calloc(size_t num, size_t size):分配 num 个大小为 size 的连续内存,初始化为 0。
  • realloc(void* ptr, size_t size):调整已分配内存的大小,可能会移动内存块。
  • free(void* ptr):释放通过上述函数分配的堆内存,ptr 必须指向堆内存起始地址。

【示例代码】:

代码语言:javascript

AI代码解释

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

int main() 
{
    // 1. malloc:分配内存(不初始化)
    int* m = (int*)malloc(2 * sizeof(int));
    m[0] = 1; m[1] = 2;
    printf("malloc: %d, %d\n", m[0], m[1]);


    // 2. calloc:分配并初始化为0
    int* c = (int*)calloc(2, sizeof(int));
    printf("calloc: %d, %d\n", c[0], c[1]);


    // 3. realloc:扩容malloc的内存
    m = (int*)realloc(m, 3 * sizeof(int));
    m[2] = 3;
    printf("realloc后: %d, %d, %d\n", m[0], m[1], m[2]);


    // 4. free:释放内存
    free(m);
    free(c);
    return 0;
}
2. C++:new/deletenew[]/delete[]

C++ 在 C 的基础上提供了更安全的动态内存接口,支持对象的构造和析构:

  • new T**:**分配单个 T 类型对象的内存,自动调用构造函数初始化。
  • new T[n]**:**分配 n 个 T 类型对象的数组内存,自动调用每个对象的构造函数。
  • delete ptr**:**自动调用析构函数,释放单个对象内存。
  • delete[] ptr**:**自动调用每个对象的析构函数,释放数组内存。

【注意】:

  • 接口必须成对使用,new对应deletenew[]对应delete[],混用会导致内存泄漏或程序崩溃。
  • 无需手动类型转换,new返回对应类型指针,且分配失败时抛出bad_alloc异常(而非返回 NULL)。

【示例代码】:

代码语言:javascript

AI代码解释

int main()
{
    //内置类型
    int* a2 = new int;//不初始化
    delete a2;

    int* a3 = new int(1);//初始化
    delete a3;

    int* a4 = new int[4];//数组~不初始化
    delete[] a4;

    int* a5 = new int[4] {1, 2, 3, 4};//数组~初始化,不完全初始化的话,未初始化的都会被初始化为0
    delete[] a5;


    //自定义类型~用法和内置类型同理
    // new/delete 和 malloc/free最大区别是:
    // new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数


    return 0;
 
}
【面试题】:malloc/calloc/realloc的区别?

malloc 、 calloc 、 realloc 的区别主要在三点:

  • 参数不同: malloc 是单参数(总字节数), calloc 是双参数(元素个数+单个大小), realloc 是双参数(原地址+新字节数)。
  • 初始化不同: malloc 分配的内存未初始化, calloc 会自动初始化为0, realloc 的新增部分未初始化。
  • 功能不同: malloc 和 calloc 是首次分配内存, realloc 是调整已分配内存的大小。

【问题】:这里需要free(p2)吗?

代码语言:javascript

AI代码解释

void Test ()
{ 
    int* p2 = (int*)calloc(4, sizeof (int));
    int* p3 = (int*)realloc(p2, sizeof(int)*10);
    free(p3 );
    
}

不需要free(p2),无论是异地扩容还是原地扩容,最终都不需要专门释放p2,如果是原地扩容,p1和p2指向同一块空间,如果是异地的话,将原内容拷贝后会自动释放原来p2指向的空间

四、operator new/operator delete函数

new和delete 是用户进行 动态内存申请和释放的操作符operator new 和operator delete 是系统提供的 全局函数new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间

但是,追踪到底还是会调用malloc和free,因为毕竟c++兼容C语言,有现成开空间的函数直接调用不就行了呗

下面是operator new函数源码,malloc失败返回空,new失败抛异常(后面会学到)

【new底层汇编】

从图中可以看到是先调用operator new,再调用相对应的构造函数

【delete底层汇编】

delete的底层是先调用析构函数,然后再调用operator delete

【模拟栈对象如何在堆上申请/释放空间】

先为栈对象开空间,然后调用该对象的构造函数为_array开辟空间,最终让栈上的p1指针指向堆上的对象(栈对象和栈内存区域是两个不同领域的概念)

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐