#C++ Day20 October 30 2025

//12 – 1 code area

#include <iostream>

using namespace std;

//four space

//代码区、全局区、栈区、堆区

void printMessage() {

cout << “Hello Vito” << endl;

//1.运行起来之后,代码区里面的数据动不了

//Can’t modified the data of the code space after running

}

//2.共享 运行代码时会生成exe可执行文件 然后多次运行时 代码区也只存储一份

// sharing:when running the code,it will generated the exe execution file,after ran it serveral times,it would only store one sapce of data

//

int main() {

printMessage();

//运行起来之后,代码区里面的数据动不了

while(1){}

return 0;

}

//12 – 2 global area

//全局区

#include <iostream>

using namespace std;

//全局区

//存放 全局变量、全局常量(const修饰的)、静态变量、字符串常量(注意:局部变量、局部常量不在全局区)

int g_a = 1; //全局变量1

int g_b = 2; //全局变量2

//全局变量存储在全局区

const int c_g_a = 3;

const int c_g_b = 3;

int main() {

cout << “全局变量g_a的地址:” << &g_a << endl;

cout << “全局变量g_b的地址:” << &g_b << endl;

int c = 3; //局部变量1

int d = 4; //局部变量2

static int e = 5;//静态变量1

static int f = 6;//静态变量2

cout << “局部变量c的地址:” << &c << endl;

cout << “局部变量d的地址:” << &d << endl;

//局部变量存储在栈区 它跟全局变量内存地址差很多

//静态变量即使紧挨着 局部变量创建 但是地址还是跟全局变量接近

//全局变量跟静态变量存在一起

cout << “静态变量e的地址:” << &e << endl;

cout << “静态变量f的地址:” << &f << endl;

cout << “字符串常量的地址” << &”Vito 算法” << endl;

//字符串常量也跟全局变量、静态变量存在一起

const int g = 7;

const int h = 8;

cout << “局部常量g的地址:” << &g << endl;

cout << “局部常量h的地址:” << &h << endl;

//局部常量跟局部变量在一起

cout << “全局常量c_g_a的地址:” << &c_g_a << endl;

cout << “全局常量c_g_b的地址:” << &c_g_b << endl;

//全局常量跟字符串常量、全局变量、静态变量存在一起

return 0;

}

//12 – 3 stack area

//栈区

#include <iostream>

using namespace std;

//栈区和堆区的内存都是程序运行过程中申请和释放的

//栈区内存是由操作系统控制生命周期的

//堆区内存是由程序员控制生命周期的

//函数变量 局部变量 都是栈区的内存,这里的堆和栈与数据结构里面的堆和栈是两个概念

char* func() {//返回字符串的首地址

char c[20] = “Vito算法联盟”;//这里是字符串常量 按理说应该存放在全局区 但是把字符串常量赋值给变量就不一样了

return c;//字符串的首地址

}

void test(int a, int b) {

int c, d;

cout << “形式参数a的地址:” << &a << endl;

cout << “形式参数b的地址:” << &b << endl;

cout << “局部变量c的地址:” << &c << endl;

cout << “局部变量d的地址:” << &d << endl;

//形式参数(形参)和局部变量的地址都挨得很近,都在栈上存储

}

//函数的参数存储在栈区

int main() {

cout << func() << endl;//会输出”烫烫烫,,,,,,,”,这里是一块坏掉的内存,野指针

//原因是c这个数组是一个局部变量 存储在栈上面 返回时字符串常量已经被销毁了 

//记住不要返回局部变量的地址,因为这块内存已经坏掉了

test(5,6);//说明形式参数(形参)和局部变量的地址都挨得很近,都在栈上存储

return 0;

}

//12-4 heap area

//堆区

#include <iostream>

using namespace std;

//堆区的内存由程序员来申请和释放

//malloc free (c语言堆区申请和释放语句)(这两个是函数)

//new delete (c++堆区申请和释放语句)(这两个是运算符)

int* getV(int v) {

int* a = new int(v);//在堆区里面申请了一块内存, 并且值赋成v 

//int *a 是一个栈上的变量

//*a 也就是 a 解引用以后得到的值,是存储在堆上面的

cout << a << endl;//打印a的地址

return a; //函数返回时,虽然栈上面的变量a被操作系统释放了,但是a指向的内存仍然存在

}

int main() {

int* p = getV(1314);

cout << *p << endl;//dereference 解引用

//函数返回时,虽然栈上面的变量a被操作系统释放了,但是a指向的内存仍然存在

//所以这里还是可以解引用得到堆上的数据

cout << p << endl;//打印p的地址

//p和a地址都一样在堆区上面

return 0;

}

//12 – 5 – 1 memory allocation & deallocation

//内存的申请和释放

//c++中内存申请和释放

//主要用到 new 和 delete两个操作符

#include <iostream>

using namespace std;

int main() {

int *ptr = new int; //去找一块整数的空间,并且把内存分配出来,返回一个指向这块内存的指针

//我们可以定一个ptr指向它,这时候代码执行完后。系统会在堆上找到一个足够大的内存存储一个整数,并且将内存的地址赋值给ptr

//这样写的话,这个内存的值是未定的,是一个随机值

cout << *ptr << endl;

//1.加上括号和值就初始化

int* ptr2 = new int(1314); //加上括号和值就初始化了

cout << *ptr2 << endl;

//2.解引用初始化

//int* ptr3 = new int(1314); 

int* ptr3 = new int;

*ptr3 = 520; //解引用来初始化

cout << *ptr3 << endl;

delete ptr; //释放完后ptr实际变成野指针

ptr = NULL;//需要把ptr置为空,这样其他人在判断时可以先看它是否为空

//为空就不访问它,不为空就解引用它

//if (ptr != NULL) *ptr = 67;

/* *ptr =67*/ ; //得在debug版本才能报错,write access violation,不能用的指针还要去用它不行

//cout << *ptr << endl;

cout << *ptr2 << endl;

cout << *ptr3 << endl;

//申请了内存后要释放 不然如果while(1)会一直在这里

while (1) {

}

return 0;

}

//12 – 5 – 2 array allocation & deallocation

//数组的申请和释放

#include <iostream>

using namespace std;

int* getGapList(int *arr,int size) {//写一个返回数组的函数,arr退化成指针*arr

//传进去这么多size的地址,首地址是*arr

//传出来一个数组,需要申请一个内存,并且传出来的是list 并且要比size少一个圆

int *p = new int[size – 1];//相当于在堆上分配了一个数组空间

//你告诉操作系统的size,它不需要返回给你

//int a[size-1];  return a;//这样不可以,因为a是栈上的内存,不能作为返回值返回

for (int i = 0; i < size – 1; ++i) {

p[i]=arr[i + 1] – arr[i]; //相邻两个值的差把它赋值给p[i]

}

return p;

}

int main() {

int arr[] = { 1,5,6,4,4,3,3,2,1,9 };

int* p = getGapList(arr, 10 );

for (int i = 0; i < 9; ++i) {

cout << p[i] << ” “;

}

cout << endl;

delete[] p;//加中括号 p,不加中括号的话只会删除数组的第一个位置,首地址

//加了中括号会把数组后面所有的删掉

p = NULL;

return 0;

}

AI:

这段代码主要展示了 C++ 中动态数组的申请(new)与释放(delete[],以及 “数组作为函数参数时的退化” 和 “相邻元素差值计算” 的逻辑。我们可以从功能、内存管理、关键细节三个层面逐步解释:

一、代码整体功能

这段代码的核心是:计算一个已知数组中 “相邻元素的差值”,并将这些差值存储在一个新的动态数组中返回,最后正确释放动态数组的内存,避免泄漏。

具体流程:

  • 定义一个初始数组arr(包含 10 个元素);
  • 调用getGapList函数,传入初始数组和其长度(10),获取 “相邻元素差值数组”;
  • 输出差值数组的所有元素;
  • 释放差值数组占用的动态内存,避免内存泄漏。

二、关键函数与逻辑详解

1. getGapList函数:生成相邻元素差值数组

函数原型:int* getGapList(int *arr, int size)

作用:接收一个数组(指针形式)和其长度,返回一个存储 “相邻元素差值” 的新数组。

  • 参数说明
    • int *arr:传入的原数组(注意:数组作为函数参数时会退化为指针,无法通过sizeof获取原数组长度,因此必须显式传入size);
    • int size:原数组的元素个数(这里是 10)。
  • 动态数组申请(new)
    代码中用int *p = new int[size – 1];在堆上申请了一个新数组。
    为什么是size – 1?
    因为 “相邻元素的差值数量” = 原数组元素数 – 1。例如:10 个元素的数组,有 9 对相邻元素,因此差值数组长度为 9。
  • 差值计算逻辑
    循环i从 0 到size-2(共size-1次),计算arr[i+1] – arr[i](后一个元素减前一个元素),并存储到新数组p[i]中。
    以原数组{1,5,6,4,4,3,3,2,1,9}为例:
    • p[0] = 5 – 1 = 4
    • p[1] = 6 – 5 = 1
    • p[2] = 4 – 6 = -2
    • … 以此类推,共 9 个差值。
  • 返回值:返回动态数组p的首地址(因为堆内存的生命周期不受函数作用域限制,函数结束后仍可访问)。

2. 为什么不用栈上数组?

代码注释提到://int a[size-1]; return a;//这样不可以

原因:栈上的数组(如a)的生命周期仅限于函数内部,函数执行结束后,栈内存会被自动释放。此时返回其地址会导致 “悬空指针”(指向已释放的内存),后续访问该指针会触发未定义行为(如程序崩溃、数据错乱)。

而new在堆上申请的内存,生命周期由程序员手动控制(需用delete[]释放),因此可以安全返回。

3. main函数:调用与内存释放

  • 调用getGapList
    传入原数组arr(自动退化为指针)和长度 10,得到差值数组的指针p。
  • 输出差值数组
    循环 9 次(因为差值数组长度为10-1=9),打印p[0]到p[8],即上述计算的 9 个差值。
  • 释放动态内存(delete[])
    用delete[] p;释放堆上的数组。
    为什么必须加[]?
    • delete[]用于释放动态数组,会逐个释放数组中的所有元素,避免内存泄漏;
    • 若用delete p;(不加[]),只会释放数组的首元素,其余元素的内存不会被释放,导致内存泄漏。
  • 避免野指针
    释放后将p置为NULL(空指针),避免后续误操作该指针(访问空指针会触发明确的错误,便于调试,而野指针的行为是未定义的)。

三、输出结果

根据原数组{1,5,6,4,4,3,3,2,1,9},相邻差值计算结果为:

4 1 -2 0 -1 0 -1 -1 8

总结

这段代码的核心价值在于展示:

  • 动态内存管理的基本操作(new[]申请数组、delete[]释放数组);
  • 数组作为函数参数时的 “退化” 特性(必须显式传递长度);
  • 逻辑上的 “差值数组长度” 与 “原数组长度” 的关系(差值数组长度 = 原数组长度 – 1)。

通过正确的动态内存申请与释放,避免了内存泄漏和悬空指针问题,是 C++ 中处理动态数组的典型示例。