c++进阶

一、内存分区

C++程序在执行时,将内存大方向划分为4个区域

  • 代码区:存放函数体的二进制代码,由操作系统进行管理的

  • 全局区:存放全局变量和静态变量以及全局常量和字符串常量

  • 栈区:由编译器自动分配释放, 存放函数的参数值,局部变量,局部常量

  • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

#include <iostream>
#include <string>

using namespace std;

//全局区

int g_a = 10;  //全局变量
int g_b = 20;

const int c_g_a = 10;	//全局常量
const int c_g_b = 20;

void globel_area() {
	int a = 10;		//局部变量
	int b = 20;
	const int c_a = 10;		//局部常量
	const int c_b = 20;		
	static int s_a = 10;	//局部静态变量
	static int s_b = 20;
	cout << "局部变量a地址为: " << (int)&a << endl;
	cout << "局部变量b地址为: " << (int)&b << endl;
	cout << "全局变量g_a地址为: " << (int)&g_a << endl;
	cout << "全局变量g_b地址为: " << (int)&g_b << endl;
	cout << "静态变量s_a地址为: " << (int)&s_a << endl;
	cout << "静态变量s_b地址为: " << (int)&s_b << endl;
	cout << "字符串常量地址为: " << (int)&"hello world" << endl;
	cout << "字符串常量地址为: " << (int)&"hello world1" << endl;
	cout << "全局常量c_g_a地址为: " << (int)&c_g_a << endl;
	cout << "全局常量c_g_b地址为: " << (int)&c_g_b << endl;
	cout << "局部常量c_a地址为: " << (int)&c_a << endl;
	cout << "局部常量c_b地址为: " << (int)&c_b << endl;
}

由此可见,局部常量并不在全局区。它存放在栈区或常量区。

#include <iostream>
#include <string>

using namespace std;


//栈区和堆区
int* func1() {
	int a=10;
	return &a; //返回内存地址
}
int* func2()
{
	int* b = new int(10); //使用new在C++中分配的内存返回的是内存地址
	return b;
}
void heap_area() {
	int* a = func1();
	cout << *a << endl;  //调动栈区函数,函数结束后系统自动释放
	cout << *a << endl;

	int* b = func2();
	cout << *b << endl;  //调动堆区函数,函数结束后仍然存放在栈区
	cout << *b << endl;
	delete b;            //手动调用关键字delete释放内存
	int* arr = new int[10];	 //new开辟数组
	delete[] arr;  //delete释放数组
}

int main() {

    heap_area();
    system("pause");

    return 0;
}


此代码中两次调用func1和func2函数。栈区的第一次没有问题,但函数 func1 在第一次调用后被系统自动释放了,但是它返回的内存地址仍然保存在变量 a 中。因此,当第二次调用 func1 时,它实际上返回的是第一次调用时分配给变量 a 的内存地址。而堆区的两次由于没有手动释放,所以都打印正常。另外,不应该尝试使用delete释放函数 func1() 返回的地址,因为这块内存已经被释放了。正确的做法是避免使用这个返回的指针,或者确保这个指针指向的内存是可以通过 new 分配的。

注意事项:栈区不要返回局部变量的地址,栈区开辟的数据由编译器自动释放。另外,在C++中,使用newdelete进行动态内存分配和释放时,必须确保分配的内存大小正确,否则可能会导致内存溢出或分配不足的问题。此外,由于堆区的内存不是自动释放的,因此必须小心处理内存泄漏问题。

二、引用

语法: 数据类型 &别名 = 原名

本质:引用的本质在c++内部实现是一个指针常量.

注意事项:

  • 引用必须初始化

  • 引用在初始化后,不可以改变

  • 引用作为函数参数时,形参会修饰实参

  • 不要返回局部变量引用

#include <iostream>
#include <string>

using namespace std;


//1. 值传递
void mySwap01(int a, int b) {
	int temp = a;
	a = b;
	b = temp;
}
//2. 地址传递
void mySwap02(int* a, int* b) {
	int temp = *a;
	*a = *b;
	*b = temp;
}
//3. 引用传递
void mySwap03(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}
//引用做函数返回值
int& test01() {
	int a = 10; //局部变量
	return a;
}
//返回静态变量引用
//静态变量存储与全局区,存放于栈区函数释放不会释放静态变量,会在整个程序执行完释放全局区数据   
int& test02() {
	static int a = 20;
	return a;
}
//引用
void quote() {
	int a = 10;
	int b = 20;
	mySwap01(a, b);    //值传递不会改变实参
	cout << "a:" << a << " b:" << b << endl;
	mySwap02(&a, &b);  //地址传递对应变量内存地址,会改变实参
	cout << "a:" << a << " b:" << b << endl;
	mySwap03(a, b);    //引用传递本质是指针常量也会改变实参
	cout << "a:" << a << " b:" << b << endl;


	//不能返回局部变量的引用。因为test01函数是存于栈区的,一次调用后,系统自动释放
	int& ref1 = test01();
	cout << "ref1 = " << ref1 << endl;
	cout << "ref1 = " << ref1 << endl;

	//如果函数做左值(具有有效内存地址的实体),那么必须返回引用
	//可以返回static静态变量的引用
	int& ref2 = test02();
	cout << "ref2 = " << ref2 << endl;
	cout << "ref2 = " << ref2 << endl;
	test02() = 1000;
	cout << "ref2 = " << ref2 << endl;
	cout << "ref2 = " << ref2 << endl;

	//int& ref = 10;  引用本身需要一个合法的内存空间,因此这行错误
	int c = 10;
	int& quote_a = c;//是正确的
	//加入const就可以了,编译器优化代码,int temp = 10; const int& ref = temp;相当于常量指针常量
	const int& ref = 10;
	//ref = 100;  //加入const后不可以修改变量
	cout << "c = " <<quote_a<< endl;
	cout << "ref = "<<ref << endl;

	//函数中利用常量引用防止误操作修改实参

}


int main() {

    quote();
    system("pause");

    return 0;
}

三、函数

1.函数定义

  • 1、返回值类型
  • 2、函数名
  • 3、参数表列
  • 4、函数体语句
  • 5、return 表达式
返回值类型 函数名 (参数列表)
{

       函数体语句

       return表达式

}

2.函数的调用和值传递

void swap(int num1, int num2)
{
        cout << "交换前:" << endl;
        cout << "num1 = " << num1 << endl;
        cout << "num2 = " << num2 << endl;

        int temp = num1;
        num1 = num2;
        num2 = temp;

        cout << "交换后:" << endl;
        cout << "num1 = " << num1 << endl;
        cout << "num2 = " << num2 << endl;

        //return ; 当函数声明时候,不需要返回值,可以不写return
}

int main() {

        int a = 10;
        int b = 20;

        swap(a, b);//函数调用

        cout << "mian中的 a = " << a << endl;
        cout << "mian中的 b = " << b << endl;

        system("pause");

        return 0;
}

注意:值传递时,如果形参发生,并不会影响实参

3.函数声明

  • 函数的声明可以多次,但是函数的定义只能有一次

4.函数默认参数和占位参数

  • 在C++中,函数的形参列表中的形参是可以有默认值的。

        语法:返回值类型 函数名 (参数= 默认值){}

  • C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置

        语法: 返回值类型 函数名 (数据类型){}

int func(int a, int b = 10, int c = 10) {
        return a + b + c;
}

//1. 如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值
//2. 如果函数声明有默认值,函数实现的时候就不能有默认参数
int func2(int a = 10, int b = 10);
int func2(int a, int b) {
        return a + b;
}

//函数占位参数 ,占位参数也可以有默认参数
void func3(int a, int) {
        cout << "this is func" << endl;
}

int main() {

        cout << "ret = " << func(20, 20) << endl;
        cout << "ret = " << func(100) << endl;
        cout << func3(10,10);<< endl;//占位参数必须填补
        system("pause");

        return 0;
}

5.函数重载

**作用:**函数名可以相同,提高复用性

函数重载满足条件:

  • 同一个作用域下

  • 函数名称相同

  • 函数参数类型不同 或者 个数不同 或者 顺序不同

注意: 函数的返回值不可以作为函数重载的条件

//函数重载需要函数都在同一个作用域下
void func()
{
        cout << "func 的调用!" << endl;
}
void func(int a)
{
        cout << "func (int a) 的调用!" << endl;
}
void func(double a)
{
        cout << "func (double a)的调用!" << endl;
}
void func(int a ,double b)
{
        cout << "func (int a ,double b) 的调用!" << endl;
}
void func(double a ,int b)
{
        cout << "func (double a ,int b)的调用!" << endl;
}

//函数返回值不可以作为函数重载条件
//int func(double a, int b)
//{
//        cout << "func (double a ,int b)的调用!" << endl;
//}


int main() {

        func();
        func(10);
        func(3.14);
        func(10,3.14);
        func(3.14 , 10);
        
        system("pause");

        return 0;
}

函数重载注意事项

  • 引用作为重载条件

  • 函数重载碰到函数默认参数

//函数重载注意事项
//1、引用作为重载条件

void func(int &a)
{
        cout << "func (int &a) 调用 " << endl;
}

void func(const int &a)
{
        cout << "func (const int &a) 调用 " << endl;
}


//2、函数重载碰到函数默认参数

void func2(int a, int b = 10)
{
        cout << "func2(int a, int b = 10) 调用" << endl;
}

void func2(int a)
{
        cout << "func2(int a) 调用" << endl;
}

int main() {
        
        int a = 10;
        func(a); //调用无const
        func(10);//调用有const


        //func2(10); //碰到默认参数产生歧义,需要避免

        system("pause");

        return 0;
}

四、文件操作

程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放

通过文件可以将数据持久化

C++中对文件操作需要包含头文件 ==< fstream >==

文件类型分为两种:

  1. 文本文件 - 文件以文本的ASCII码形式存储在计算机中

  2. 二进制文件 - 文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们

操作文件的三大类:

  1. ofstream:写操作

  2. ifstream: 读操作

  3. fstream : 读写操作

文件打开方式:

打开方式

解释

ios::in

为读文件而打开文件

ios::out

为写文件而打开文件

ios::ate

初始位置:文件尾

ios::app

追加方式写文件

ios::trunc

如果文件存在先删除,再创建

ios::binary

二进制方式

1.文本文件

#include <fstream>
#include <iostream>
#include <string>

void test01()
{
        ofstream ofs;
        ofs.open("test.txt", ios::out);

        ofs << "姓名:张三" << endl;
        ofs << "性别:男" << endl;
        ofs << "年龄:18" << endl;

        ofs.close();
}

void test02()
{
        ifstream ifs;
        ifs.open("test.txt", ios::in);

        if (!ifs.is_open())
        {
                cout << "文件打开失败" << endl;
                return;
        }

        //第一种方式
        //char buf[1024] = { 0 };
        //while (ifs >> buf)
        //{
        //        cout << buf << endl;
        //}

        //第二种
        //char buf[1024] = { 0 };
        //while (ifs.getline(buf,sizeof(buf)))
        //{
        //        cout << buf << endl;
        //}

        //第三种
        //string buf;
        //while (getline(ifs, buf))
        //{
        //        cout << buf << endl;
        //}

        char c;
        while ((c = ifs.get()) != EOF)
        {
                cout << c;
        }

        ifs.close();


}


int main() {

        test01();
        test02();

        system("pause");

        return 0;
}

2.二进制文件

  • 二进制方式写文件主要利用流对象调用成员函数write

        函数原型 :ostream& write(const char * buffer,int len);

  • 二进制方式读文件主要利用流对象调用成员函数read

        函数原型:istream& read(char *buffer,int len);

参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数

#include <fstream>
#include <iostream>
#include <string>

class Person
{
public:
        char m_Name[64];
        int m_Age;
};

//二进制文件  写文件
void test01()
{
        //1、包含头文件

        //2、创建输出流对象
        ofstream ofs("person.txt", ios::out | ios::binary);
        
        //3、打开文件
        //ofs.open("person.txt", ios::out | ios::binary);

        Person p = {"张三"  , 18};

        //4、写文件
        ofs.write((const char *)&p, sizeof(p));

        //5、关闭文件
        ofs.close();
}

void test02()
{
        ifstream ifs("person.txt", ios::in | ios::binary);
        if (!ifs.is_open())
        {
                cout << "文件打开失败" << endl;
        }

        Person p;
        ifs.read((char *)&p, sizeof(p));

        cout << "姓名: " << p.m_Name << " 年龄: " << p.m_Age << endl;
}

int main() {

        test01();
        test02();

        system("pause");

        return 0;
}

  • 27
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值