在完成对C语言的学习后,我最近开始了对C++和Java的学习,目前跟着视频学习了一些语法,也跟着敲了一些代码,有了一定的掌握程度。现在将跟着视频做的笔记进行整理。本篇博客是整理C++知识点的第九篇博客。
本篇博客介绍了C++的内存区域和引用。
本系列博客所有C++代码都在Visual Studio 2022环境下编译运行。程序为64位。
目录
内存区域
内存分区
C++程序执行时,内存大方向划分为4个区域:
代码区:存放函数体的二进制代码,由操作系统进行管理。
全局区:存放全局变量和静态变量,以及常量。
栈区:由编译器自动分配释放,存放函数的参数,局部变量等。
堆区:由程序员分配和释放,如果程序员不释放,程序结束时由操作系统回收。
不同区域存放不同数据,生命周期不同,编程时更灵活。
在程序编译后,生成后缀名为exe的可执行的程序,未执行该程序前分为两个区域:代码区和全局区。
代码区
代码区存放CPU执行的机器指令。
代码区是共享的,对于频繁被执行的程序,只需要一份空间就足够。
代码区是只读的,防止程序意外修改指令。
全局区
全局区存放全局变量和静态变量,还包含常量区,存放字符串常量和const修饰的全局常量。
该区域的数据在程序结束后由操作系统释放。
普通变量前加static就属于静态变量。
全局变量定义在函数外。
#include<iostream>
using namespace std;
int a = 5;
int b = 10;
const int c = 10;
const int d = 10;
int main(void)
{
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
cout << &d << endl;
int a = 5;
int b = 10;
const int c = 10;
const int d = 10;
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
cout << &d << endl;
static int e = 10;
static int f = 15;
cout << &e << endl;
cout << &f << endl;
return 0;
}
程序的一个输出示例是:
00007FF7F5DED000
00007FF7F5DED004
00007FF7F5DEABB0
00007FF7F5DEABB4
000000605072FA14
000000605072FA34
000000605072FA54
000000605072FA74
00007FF7F5DED008
00007FF7F5DED00C
可见全局变量a,b,c,d和静态变量e,f的地址差异较小,位于全局区。而main函数内的变量a,b,const修饰的局部变量c,d的地址差异较小,位于栈区。
不同区域的地址值差异较大。
栈区
栈区由编译器分配释放,存放函数的参数和局部变量。栈区数据由编译器自动释放。
堆区
堆区由程序员分配释放,如果程序员不释放,程序结束时由操作系统回收。
C++中,new在堆区开辟内存。new的用法是:
new 数据类型(初始值)
后面数据类型是要开辟的数据类型,初始值可以为空,这时为默认值(如整数为0)。
new如果开辟成功,会返回此数据类型的一个指针,指向这块空间。
new开辟的数据需要释放,用关键字delete,格式是:
delete 动态开辟的指针
#include<iostream>
using namespace std;
int* func(void);
int main(void)
{
int* p;
p = func();
cout << *p << endl;
delete p;
return 0;
}
int* func(void)
{
int* p = new int(10);
cout << *p << endl;
return p;
}
程序的输出是:
10
10
程序的func函数中用new动态开辟了一块空间,存放一个整数,并初始化为10。随后输出值,并将指针返回main函数,main函数再度输出后释放空间。
#include<iostream>
using namespace std;
int* func(void);
int main(void)
{
int* p;
p = func();
int i;
for (i = 0; i < 10; i += 1) {
cout << *(p + i) << endl;
}
delete[] p;
return 0;
}
int* func(void)
{
int* p = new int[10];
int i;
for (i = 0; i < 10; i += 1) {
*(p + i) = i;
}
return p;
}
这段代码中,func函数创建了一块空间,存放有10个整数元素的数组,并依次存放0-9,随后将首元素地址返回main函数,main函数遍历输出后释放空间。
程序依次输出0-9。
引用
引用的基本使用
引用的作用是给变量起别名。引用的语法是:
数据类型 &别名 = 原名;
#include<iostream>
using namespace std;
int main(void)
{
int a = 10;
int& b = a;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
b = 15;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
return 0;
}
程序的输出是:
a = 10
b = 10
a = 15
b = 15
为变量a起了别名b,随后通过b修改了a的值。
引用的注意事项
引用必须初始化,初始化后不可指向其他变量。
#include<iostream>
using namespace std;
int main(void)
{
int a = 10;
int& b = a;
int c = 15;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
b = c;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
return 0;
}
程序的输出是:
a = 10
b = 10
a = 15
b = 15
b = c是将c的值赋给b。
引用作为函数参数
函数传参时,可以利用引用让形参修饰实参,这样也可以修改实参。
#include<iostream>
using namespace std;
void swap(int& m, int& n);
int main(void)
{
int a = 10;
int b = 15;
swap(a, b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
return 0;
}
void swap(int& m, int& n)
{
int temp;
temp = m;
m = n;
n = temp;
}
程序的输出是:
a = 15
b = 10
swap函数用引用接收了a和b的值,并进行交换。swap函数结束后main函数的a,b值也发生了交换。
引用作为函数返回值
引用也可以是函数返回值。不要返回局部变量的引用。
在一个函数中使用静态变量时,静态变量没有随着函数的结束而消失,而是保持着值,下一次调用该函数时,静态变量的值依然不变。静态变量在程序结束后被回收。
#include<iostream>
using namespace std;
int& test() {
static int num = 5;
return num;
}
int main(void)
{
int &p = test();
cout << p << endl;
test() = 10;
cout << p << endl;
return 0;
}
程序的输出是:
5
10
引用的本质
引用的本质是一个指针常量。
常量引用
常量引用可以修饰形参,防止误操作。
#include<iostream>
using namespace std;
void show(const int& num);
int main(void)
{
int n = 15;
show(n);
return 0;
}
void show(const int& num)
{
cout << num << endl;
}
程序的输出是15
用const修饰num,防止被修改。如果在show函数中试图修改num的值,会报错。