文章目录
一、 内存分区
C++程序在执行时,内存大致方向分为4个区域,不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程。代码区和全局区是在程序运行前,栈区和堆区是在代码运行后
- 代码区:存放函数体的二进制码,有操作系统进行管理
- 全局区:存放全局变量和静态变量以及常量
- 栈区:由编译器自动分配释放,存放函数的函数值,局部变量等。
- 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
1.1 代码区
存放CPU执行的机器指令
- 代码区是共享的,只需要在内存中有一份代码即可
- 代码区是只读的,防止程序意外地修改了它的指令
1.2 全局区
全局变量和静态变量存放在此。
全局区还包括了常量区,字符串常量和其他常量也存放在这里。
该区域的数据在程序结束后由操作系统释放。
#include <iostream>
using namespace std;
// 全局变量 ----- 全局区
int g_a = 0;
int g_b = 0;
// 全局常量 ----- 全局区
const int c_g_a = 0;
const int c_g_b = 0;
int main() {
// 以下为全局区
// 全局变量
cout << "全局变量a的地址为" << (int)&g_a << endl;
cout << "全局变量b的地址为" << (int)&g_b << endl;
// 全局常量
cout << "全局常量a的地址为" << (int)&c_g_a << endl;
cout << "全局常量b的地址为" << (int)&c_g_b << endl;
// 静态变量
static int s_a = 0; // static修饰的是静态变量
static int s_b = 0;
cout << "静态变量a的地址为" << (int)&s_a << endl;
cout << "静态变量b的地址为" << (int)&s_b << endl;
// 字符串常量
cout << "字符串常量a的地址为" << (int)&"Hallo world" << endl;
// 以下为非全局区
// 局部变量
int l_a = 0;
int l_b = 0;
cout << "局部变量a的地址为" << (int)&l_a << endl;
cout << "局部变量b的地址为" << (int)&l_b << endl;
// 局部常量
// const修饰的常量
const int c_l_a = 0;
const int c_l_b = 0;
cout << "局部常量a的地址为" << (int)&l_a << endl;
cout << "局部常量b的地址为" << (int)&l_b << endl;
system("pause");
return 0;
}
全局变量a的地址为1109844336
全局变量b的地址为1109844340
全局常量a的地址为1109830576
全局常量b的地址为1109830580
静态变量a的地址为1109844344
静态变量b的地址为1109844348
字符串常量a的地址为1109831072
局部变量a的地址为1807742644
局部变量b的地址为1807742676
局部常量a的地址为1807742644
局部常量b的地址为1807742676
请按任意键继续. . .
可以看到全局变量,全局常量,静态变量,字符串常量是存储在全局区,其他量存储在非全局区。
1.3 栈区
- 由编译器自动分配释放,存放函数的参数值,局部变量等。
- 不要返回局部变量的地址,栈区开辟的数据由编译器自动释放
1.4 堆区
由程序员分配释放,若程序员不释放,程序结束由操作系统回收。
在C++中主要利用new在堆区开辟内存。
#include <iostream>
using namespace std;
int* func() {
int* p = new int(10); // new关键字在堆区域开辟地址
return p;
}
int main() {
cout << *func() << endl;
cout << *func() << endl;
cout << *func() << endl;
system("pause");
return 0;
}
10
10
10
请按任意键继续. . .
new操作符
利用new操作符开辟数据
堆区开辟的数据,有程序员手动开辟,手动释放,释放使用delete
利用new创建的数据,会返回该数据对应的类型的指针
#include <iostream>
using namespace std;
int* func() {
int* p = new int(10); // new关键字在堆区域开辟地址
return p;
}
int main() {
cout << *func() << endl;
int* p = func();
cout << *p << endl;
// 删除操作
delete p;
// cout << *p << endl; 会报错,原因是p已经从堆区域删除
system("pause");
return 0;
}
new在堆区开辟一个数组
#include <iostream>
using namespace std;
int* test() {
int *arr = new int[10]; // new创建数组
for (int i = 0; i < 10; i++) {
arr[i] = i; // 赋值
}
for (int i = 0; i < 10; i++)
{
cout << arr[i] << endl;
}
return arr;
}
int main() {
int *arr = test();
delete[] arr; // 删除数组时要加上[]表示删除的是一个数组
system("pause");
return 0;
}
0
1
2
3
4
5
6
7
8
9
请按任意键继续. . .
二. 引用
2.1 引用的基本使用
作用是给变量起别名,语法: 数据类型 &别名=原名
#include <iostream>
using namespace std;
int main() {
int a = 0;
// 引用
int& b = a;
cout << "a=" << a<<endl;
cout << "b=" << b<<endl;
int* p = &a;
cout << "p的值为:" << *p << endl;
b = 10; // 同样也会修改a的值
cout << "p的值为:" << *p << endl;
cout << "a的值为:" << a << endl;
cout << "a的地址为:" << (int)&a << endl;
cout << "b的地址为:" << (int)&b << endl;
system("pause");
return 0;
}
a=0
b=0
p的值为:0
p的值为:10
a的值为:10
a的地址为:-289408796
b的地址为:-289408796
请按任意键继续. . .
注意事项:
- 引用必须初始化
- 引用在创建之后就不可改变了
int &a; // 错误 未初始化!
int a = 0;
int c = 0;
// 引用
int& b = a;
int& b = c; // 报错,创建之后就不可以更改
2.2 引用做函数参数
函数传参时,可以利用引用的技术让形参修饰实参,简化指针修饰实参
#include <iostream>
using namespace std;
void swap(int &a,int&b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int a = 10;
int b = 20;
cout << "交换前a=" << a << endl;
cout << "交换前b=" << b << endl;
swap(a, b);
cout << "交换后a=" << a << endl;
cout << "交换后b=" << b << endl;
system("pause");
return 0;
}
交换前a=10
交换前b=20
交换后a=20
交换后b=10
请按任意键继续. . .
引用传递和地址传递效果一样,都可以对实参进行修改。
2.3 引用做函数返回值
#include <iostream>
using namespace std;
// 不要返回局部变量的引用
//int &test() {
// int a = 10;
// cout <<"a的地址:"<< (int)& a << endl;
// return a;
//}
int &test() {
static int a = 10; // 静态变量 在全局区
cout <<"a的地址:"<< (int)& a << endl;
return a;
}
// 引用的格式
// int & a = b;
int main() {
int& ref = test();
cout << "ref的地址" << (int) & ref << endl;
cout << "ref的地址为:" << ref << endl;
system("pause");
return 0;
}
a的地址:1526331140
ref的地址1526331140
ref的地址为:-858993460
请按任意键继续. . .
函数的调用可以作为左值:
#include <iostream>
using namespace std;
int& test() {
static int a = 10;// 静态变量
return a;
}
int main() {
int& b = test();
cout << "b的值为:" << b<<endl;
test() = 1000; // 函数调用作为左值
cout << "b的值为:" << b<<endl;
system("pause");
return 0;
}
b的值为:10
b的值为:1000
请按任意键继续. . .
2.4 引用的本质
引用的本质是在C++内部实现一个指针常量,由编译器内部完成
指针常量是指针的指向不可改变,但指针指向的值可以改变
#include <iostream>
using namespace std;
void test(int& x) {
cout << x << endl;
}
int main() {
int a = 0;
int &b = a; // 相当于指针常量 int const *b = &a; 指向对象不能变,指向对象的值能变
b = 10; // 相当于*b =10 解引用
cout << "a的值为:" << a << endl;
cout << "b的值为:" << b << endl;
system("pause");
return 0;
}
a的值为:10
b的值为:10
请按任意键继续. . .
2.5 常量引用
主要用来修饰形参,防止误操作,使用const修饰形参,防止形参修改实参
#include <iostream>
using namespace std;
void printValue(const int& a) {
//a = 100; const修饰后不可再函数内部进行更改,防止误操作
cout << a << endl;
}
int main() {
int a = 10;
printValue(a);
system("pause");
return 0;
}
10
请按任意键继续. . .
在写函数内部具体代码时,应该先判断是否需要使用const修饰,防止后续代码有误操作。
三. 函数提高
3.1 函数的默认参数
默认参数可以不传值,也可以重新传值进行覆盖。
注意事项
:
- 如果某个位置有默认参数,那么从这个默认参数后都应该有默认参数
- 如果函数申明有默认参数,那么函数实现就不能有默认参数,两者只能有其一,防止重定义
#include <iostream>
using namespace std;
// 设置函数默认参数
int func(int a, int b = 20, int c = 30) {
return a + b + c;
}
// 注意事项
// 1. 如果某个位置有默认参数,那么从这个默认参数后都应该有默认参数
// int a, int b = 20, int c; 错误
// 2.如果函数申明有默认参数,那么函数实现就不能有默认参数,两者只能有其一,防止重定义
//int func2(int a = 10, int b = 10);
//int func2(int a = 20, int b = 30) {
// return a + b;
//}
int main() {
cout << func(10,30) << endl;
system("pause");
return 0;
}
70
请按任意键继续. . .
3.2 占位参数
用于占位,调用函数时必须填补该位置,也可以设置默认参数,意义不大,但后续会用到。
语法
: 返回值类型 函数名 (数据类型) {}
#include <iostream>
using namespace std;
void func(int a, int) {
cout << a << endl;
}
int main() {
func(1, 2);
system("pause");
return 0;
}
1
请按任意键继续. . .
四. 函数重载
作用:函数名可以相同,提高复用性
需要满足如下条件:
- 同一个作用域
- 函数名称相同
- 函数参数
类型不同
或者个数不同
或者顺序不同
函数的返回值不可作为函数重载的条件
#include <iostream>
using namespace std;
void func() {
cout << "hallo world" << endl;
}
void func(int a) {
cout << "hallo world" << endl;
}
int main() {
func();
system("pause");
return 0;
}
抓住重载条件即可,函数参数类型不同
或者 个数不同
或者顺序不同
引用作为函数重载条件:
#include <iostream>
using namespace std;
void test1(int& a) { // int&a =10 不合法
cout << "调用的test1 int& a!" << endl;
}
void test1(const int& a) {// const int&a =10 不合法
cout << "调用的test1! const int& a" << endl;
}
int main() {
int a = 10;
test1(a);
test1(10);
system("pause");
return 0;
}
调用的test1 int& a!
调用的test1! const int& a
请按任意键继续. . .
函数重载遇到默认参数情况
#include <iostream>
using namespace std;
void test1(int a,int b=10) {
cout << "调用int a,int b=10" << endl;
}
void test1(int a) {
cout << "调用int a" << endl;
}
int main() {
// test1(10); // 报错
test1(10,20); // 不报错
system("pause");
return 0;
}
调用int a,int b=10
请按任意键继续. . .