C++从0到1入门编程

1、变量

1个字节 2个字节 4个字节 8个字节
char short

int

long(linux64位)
float long long
long(win/linux32位) double

switch(只能整形或字符){}

生成随机数0~99:rand()%100;

//随机数种子生成随机数每次不同:
#include <ctime>
srand((unsigned int)time(NULL));
  • 水仙花数:1个3位数每位3次方之和等于它本身

  • 跳转语句

    • break:终止整个循环

    • continue:跳过之下语句执行下一次循环

    • goto(后纯大写):无条件跳

2、数组

2.1、一维数组

数组的末尾元素下标:sizeof(arr)/sizeof(arr[0]-1)

扫描二维码关注公众号,回复: 15605125 查看本文章
//冒泡
int arr[9] = {5,8,2,3,4,1,6,2,7};
    
    //总共排列的轮数
    for(int i = 0 ; i < 9 - 1 ; i++)
    {
        //总共排列的次数
        for(int j = 0 ; j < 9 - i - 1 ; j++)
        {
            if(arr[j]<arr[j+1])
            {
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
            
        }
    }
    for(int i = 0 ; i < 9 ; i++)
    {
        cout<<arr[i]<<" ";
    }
    cout<<endl;

2.2、二维数组

二维数组4种定义方式:

1、数据类型 数组名[行数][列数];

2、(推荐用)数据类型 数组名[行数][列数] =

{

        //直观看出有2行2列

        { 数据1,数据2},

        { 数据3,数据4}

};

3、数据类型 数组名[行数][列数] = { 数据1,数据2,数据3,数据4};//根据列数自动划分行数

4、数据类型 数组名[ ][列数] = { 数据1,数据2,数据3,数据4};//少行数,也是根据列数自动划分行数

//计算二维数组的行数
sizeof(arr)/sizeof(arr[0])
//计算二维数组的列数
sizeof(arr[0])/sizeof(arr[0][0])

访问首地址:arr

访问第一行首地址:arr[0]

访问第二行首地址:arr[1]

访问第一个元素首地址:&arr[0][0]        // 要有取地址符

//二维数组案例-考试成绩统计
#include <iostream>
using namespace std;
#include <string>

int main()
{
    //1、列出成绩    
    int scores[3][3] =
    {
        {100,100,100},
        {90,80,70},
        {60,50,40}
    };
    
    string names[3] = {"张三","李四","狗五"};
    
    //2、统计每人的总分
    //控制行数
    for(int i=0 ; i<3 ; i++)
    {
        int sum = 0;//统计分数总和变量
        //控制列数
        for(int j=0 ; j<3 ; j++)
        {
            sum += scores[i][j];
        }
        cout<<names[i]<<"的总分为:"<<sum<<endl;
    }
    
    return 0;
}

3、函数

3.1、函数基本形式:无参无反、无参有反、有参无反、有参有反

语法:
返回值类型 函数名 (参数列表)
{
    声明和语句;
    return x;
}
//若返回值类型为:void ,则最后写成 return;

3.2、函数声明可以写多次,定义只能写一次。

3.3、函数的分文件编写

//1、在“头文件”新建.h后缀名的头文件
//2、在“源文件”新建.cpp后缀名的源文件

//3、在头文件中写函数的声明
#include <iostream>
using namespace std;
//写函数声明

//4、在源文件中写函数的定义
#include "函数名.h"
//写函数定义

4、指针

4.1、指针的定义

int a = 10;

//定义指针:数据类型 * 指针变量名 或者 数据类型 * 指针变量名 = &地址变量名
int * p;

//引用:&放在一个变量声明或者是函数的形参声明前


//取地址:&放在一个已经定义的变量前
p = &a;

//两者打印结果一样
cout<<"a的地址为:"<<&a<<endl;
cout<<"指针p为:"<<p<<endl;

//指针前加 * 代表解引用,找到指针指向内存中的数据,即 a 的值
*p = 1000;

//两者打印结果一样
cout<<"a="<<a<<endl;
cout<<"*p="<<*p<<endl;

4.2、空指针(指针变量指向内存中编号为0的空间)

//用途:初始化指针变量
int * p = NULL;

//注意:空指针指向的内存是不可以访问的
//0~255之间的内存编号是系统占用的,不可访问
*p = 100;//运行报错

4.3、野指针(指针变量指向非法的内存空间)

//在程序中,尽量避免出现野指针
int * p = (int *)0x1100;//未声明就访问
cout<<*p<<endl;//报错 

空指针和野指针都不是我们申请的空间,因此不要访问

4.4、const修饰指针或者常量(原理:const在谁前谁不可变)

int a=10;
int b=10;

//1、const 修饰指针:常量指针(把const读成常量,把*读成指针)
const int * p = &a;

//指针指向的值不可以改,指针的指向可以改
*p = 20;//报错
p = &b;//正确

//2、const 修饰常量:指针常量
int * const p2 = &a;
*p2 = 100;//正确
p2 = &b;//报错

//3、const 修饰指针和常量
const int * const p3 = &a;
*p3 = 100;//报错
p3 = &b;//报错

4.5、指针访问数组

int arr[3] = {1,2,3};

int * p = arr;//指向数组的指针,arr就是数组首地址

cout<<"指针访问第一个元素:"<<*p<<endl;

//利用指针遍历数组
for(int i=0 ; i<3 ; i++)
{
    cout<<*p<<endl;
    p++;//让指针向后偏移4个字节
}

4.6、地址传递(值传递不改实参,地址传递改变实参)

void swap(int * p1,int * p2)
{
    //函数体
}

int main()
{
    int a = 10;
    int b = 20;
    swap(&a,&b);//地址传递会改变实参

    return 0;
}

4.7、指针数组函数实现冒泡

//指针数组函数实现冒泡排序

//冒泡排序函数
void bubbleSort(int * arr, int len)
{
    for(int i=0 ; i<len-1 ; i++)
    {
        for(int j=0 ; j<len-i-1 ; j++)
        {
            if(arr[j]>arr[j+1])
            {
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
         }
    }
}

void printArray(int * arr , int len)
{
    for(int i=0 ; i<len ; i++)
    {
        cout<<arr[i]<<endl;
    }
    
}

int main()
{
   //1、创建数组
   int arr[10] = {3,2,9,8,3,9,5,7,4,5};
   
   //数组长度(关键:可灵活处理数组长度)
   int len = sizeof(arr)/sizeof(arr[0]);// 40/4=10
   
   //2、创建函数,实现冒泡排序
   bubbleSort(arr,len);
   
   //3、打印排序后的数组
   printArray(arr,len);
   
    return 0;
}

5、结构体

5.1、概念:属于用户自定义的数据类型,允许用户存储不同的数据类型

/*
语法:(定义结构体时的关键字struct不可省)
struct 结构体名
{ 
	数据类型 变量名1;
	数据类型 变量名2;
};
*/

struct Student
{ 
	string name;
	int age;
};

//3种通过结构体创建变量的方式

//1、struct 结构体名 变量名
struct Student s1;//(创建结构体变量时的关键字struct可省)
//可直接写成:Student s1;

s1.name = "张三";//用操作符 . 访问成员
s1.age = 10;

//2、struct 结构体名 变量名 = { 成员1值 , 成员2值...}
struct Student s2 = {"李四",19};//(此struct可省略)

//3、定义结构体时顺便创建变量(不建议用)
struct Student
{ 
	string name;
	int age;
}s3;//尾括号加变量名就顺便创建结构体变量

s3.name = "张三";
s3.age = 10;

5.2、结构体数组

作用:将自定义的结构体放入到数组中方便维护

//语法:struct 结构体名 数组名[元素个数] = {
   
   {},{},{}}; 
struct Student
{
    string name;
    int age;
};

int main()
{
    struct Student arr[2] = 
    {
        {"张三",10},
        {"李四",20}    
    };

    //访问数组中的元素
    cout<<arr[0].name<<endl;
}

5.3、结构体指针

作用:通过指针访问结构体中的成员

struct Student
{ 
	string name;
	int age;
};

//1、创建学生结构体变量
struct Student s = {"小明",19};

//2、通过指针指向结构体变量
struct Student * p = &s;

//3、利用操作符 -> 访问结构体中的成员
cout<<p->name<<endl;

5.4、结构体嵌套

#include <iostream>
#include <string>
using namespace std;

struct Student 
{
    string name;
    int age;
};

struct Teacher
{
    string name;
    int id;
    struct Student stu;//结构体中再嵌套结构体
};


int main()
{
   //创建结构体
   struct Teacher t1;
   t1.name = "老师";
   t1.id = 10;
   t1.stu.name = "学生";//对结构体中的结构体赋值操作
   t1.stu.age = 18;
   
   cout<<t1.name<<endl;
   cout<<t1.id<<endl;
   cout<<t1.stu.name<<endl;
   cout<<t1.stu.age<<endl;
   
    return 0;
}

5.5、结构体做函数参数(值传递不改主函数中的数据,地址传递会改)

#include <iostream>
#include <string>
using namespace std;

struct Student 
{
    int age;
};

//代码检测时把函数printStudent1或者printStudent2注释其中之一
//值传递
void printStudent1(struct Student s)
{
    s.age = 19;
    cout<<"值传递值:"<<s.age<<endl;//值传递输出为19,主函数输出仍为18,不改主函数的值
}

void printStudent2(struct Student * p)
{
    p->age = 20;
    cout<<"地址传递值:"<<p->age<<endl;//地址传递输出为20,主函数输出也为20,改变主函数的值
}

int main()
{
   //创建结构体
   struct Student s;
   s.age = 18;
   printStudent1(s);
   //printStudent2(&s);
   cout<<"主函数值:"<<s.age<<endl;
   
    return 0;
}

5.6、结构体const的使用

作用:用const来防止误操作

#include <iostream>
using namespace std;

struct Student 
{
    int age;
};


//将函数中的形参改为指针,可以减少内存空间,而且不会复制新的副本出来
void printStudent1(const struct Student *s)//形参改为指针
{
    //s->age = 19;//报错
    cout<<s->age<<endl;
    
}

int main()
{
   //创建结构体
   struct Student s;
   s.age = 18;
   printStudent1(&s);//加个取地址符
 
    return 0;
}

5.7、结构体案例

/*
案例描述1 :学校正在做毕设项目,每名老师带领5个学生,总共有3名老师,需求如下。
设计学生和老师的结构体,其中在老师的结构体中,有老师姓名和一个存放5名学生的数组作为成员。
学生的成员有姓名、考试分数。
创建数组存放3名老师,通过函数给每个老师及所带的学生赋值。
最终打印出老师数据以及老师所带的学生数据。
*/

#include <iostream>
using namespace std;
#include <string>
#include <ctime>

//1、创建老师和学生的结构体
//学生的结构体
struct Student 
{
    //学生姓名
    string sName;
    
    //学生分数
    int score;
};

//老师的结构体
struct Teacher
{
    //老师姓名
    string tName;
    
    //每名老师所带学生的数组
    struct Student sArray[5];
};

//tips:赋值函数和打印函数有许多相同的地方
//3、给老师和学生赋值的函数
void allocateSpace(struct Teacher tArray[],int len)
{
    string nameSeed = "ABCDE";
    
    //循环给老师赋值
    for(int i=0 ; i<len ; i++)
    {
        tArray[i].tName = "Teacher_";
        tArray[i].tName += nameSeed[i];
        
        //循环给每名老师所带的学生赋值
        for(int j=0 ; j<5 ; j++)
        {
            tArray[i].sArray[j].sName = "Student_";
            tArray[i].sArray[j].sName += nameSeed[j];
            
            int random = rand()% 61+40; //40~100
            tArray[i].sArray[j].score = random;
        }
        
    }
    
}

//4、打印所有老师及所带学生信息
void printInof(struct Teacher tArray[],int len)
{
    
    //循环打印老师的信息
    for(int i=0 ; i<len ; i++)
    {
        cout<<"老师姓名:"<<tArray[i].tName<<endl;
        
        //循环打印每名老师所带的学生信息
        for(int j=0 ; j<5 ; j++)
        {
            cout<<"\t学生姓名:"<<tArray[i].sArray[j].sName
                <<" 考试分数:"<<tArray[i].sArray[j].score
                <<endl;
        }
    }
}

int main()
{
   
   //随机数种子
   srand((unsigned int)time(NULL));
   
   //2、创建3名老师的数组
   struct Teacher tArray[3];
  
   //tips:赋值函数和打印函数有许多相同的地方
 
   //3、通过函数给3名老师的信息赋值,并给老师带的学生信息赋值
   int len = sizeof(tArray)/sizeof(tArray[0]);
   allocateSpace(tArray,len);
   
   //4、打印所有老师及所带学生信息
   printInof(tArray,len);
 
    return 0;
}
/*
案例描述2 :设计一个英雄的结构体,包括成员姓名,年龄,性别;
创建结构体数组,数组中存放5名英雄。
通过冒泡排序的算法,将数组中的英雄按照年龄进行升序排序,
最终打印排序后的结果。
*/

#include <iostream>
using namespace std;
#include <string>

//1、设计英雄结构体
struct Hero 
{
    //姓名
    string name;
    
    //年龄
    int age;
    
    //性别
    string sex;
};

//冒泡排序:实现年龄升序排序
void bubbleSort(struct Hero heroArray[],int len)
{
    for(int i=0 ; i<len-1 ; i++)
    {
        for(int j=0 ; j<len-i-1 ; j++)
        {
            
            //此处是关键
            if(heroArray[j].age>heroArray[j+1].age)
            {
                struct Hero temp = heroArray[j];
                heroArray[j] = heroArray[j+1];
                heroArray[j+1] = temp;
            }
        }
    }
}

//打印排序后数组中的信息
void printHero(struct Hero heroArray[],int len)
{
    for(int i=0 ; i<len ; i++)
    {
        cout<<"姓名:"<<heroArray[i].name
            <<"年龄:"<<heroArray[i].age
            <<"性别:"<<heroArray[i].sex
            <<endl;
    }
}

int main()
{
   
   //2、创建数组存放5名英雄
   struct Hero heroArray[5] = 
   {
       {"刘备",23,"男"},
       {"关羽",22,"男"},
       {"张飞",20,"男"},
       {"赵云",21,"男"},
       {"貂蝉",19,"女"}
   };
   
   int len = sizeof(heroArray)/sizeof(heroArray[0]);
   
   cout<<"排序前的打印:"<<endl;
   
   //可作为测试代码
   for(int i=0 ; i<len ; i++)
   {
       cout<<"姓名:"<<heroArray[i].name
           <<"年龄:"<<heroArray[i].age
           <<"性别:"<<heroArray[i].sex
           <<endl;
   }
   
   
   //3、对数组进行排序,按照年龄进行升序排序
   bubbleSort(heroArray,len);
   cout<<"排序后的打印:"<<endl;
   
   //4、将排序后结果打印输出
   printHero(heroArray,len);
 
    return 0;
}

6、小项目:通讯录管理系统

//
// Created by mac on 2021/2/8.
//


//封装函数显示该界面 如 void showMenu()
//在 main 函数中调用封装好的函数
#include <iostream>
using namespace std;
#include <string>
#define MAX 1000
#include <unistd.h>

//3、设计联系人和通讯录的结构体
//设计联系人的结构体
struct Person
{
    //姓名
    string m_Name;

    //性别:男1 女2
    int m_Sex;

    //年龄
    int m_Age;

    //电话
    string m_Phone;

    //住址
    string m_Addr;
};

//通讯录的结构体
struct Addressbook
{
    //通讯录中保存的联系人数组
    struct Person personArray[MAX];

    //通讯录中当前记录联系人个数
    int m_Size;
};

//1、菜单界面
void showMenu()
{
    cout<<"***********************"<<endl;
    cout<<"***** 1、添加联系人 *****"<<endl;
    cout<<"***** 2、显示联系人 *****"<<endl;
    cout<<"***** 3、删除联系人 *****"<<endl;
    cout<<"***** 4、查找联系人 *****"<<endl;
    cout<<"***** 5、修改联系人 *****"<<endl;
    cout<<"***** 6、清空联系人 *****"<<endl;
    cout<<"***** 0、退出通讯录 *****"<<endl;
    cout<<"***********************"<<endl;
}

//5、利用地址传递,可以修饰实参
void addPerson(struct Addressbook * abs)
{
    //判断通讯录是否已满,如果满了就不再添加
    if(abs->m_Size == MAX)
    {
        cout<<"通讯录已满,无法添加!"<<endl;
        return;
    }
    else
    {
        //添加具体联系人

        //添加姓名
        string name;
        cout<<"请输入姓名:"<<endl;
        cin>>name;
        abs->personArray[abs->m_Size].m_Name = name;

        //添加性别
        cout<<"请输入性别:"<<endl;
        cout<<"1--男;2--女"<<endl;
        int sex = 0;
        while(true)
        {
            cin>>sex;
            //输入1或者2正确退出循环,输入其他则重新输入
            if (sex == 1 || sex == 2)
            {
                abs->personArray[abs->m_Size].m_Sex = sex;
                break;
            }
            cout<<"输入有误,请从新输入!"<<endl;
        }

        //添加年龄
        cout<<"请输入年龄:"<<endl;
        int age = 0;
        cin>>age;
        abs->personArray[abs->m_Size].m_Age = age;

        //添加电话
        cout<<"请输入联系电话:"<<endl;
        string phone;
        cin>>phone;
        abs->personArray[abs->m_Size].m_Phone = phone;

        //添加住址
        cout<<"请输入家庭住址:"<<endl;
        string addr;
        cin>>addr;
        abs->personArray[abs->m_Size].m_Addr = addr;

        //更新通讯录人数(关键)
        abs->m_Size++;

        cout<<"添加成功"<<endl;

        //pause();//请按任意键继续   mac下似乎不能用这两个函数,此问题暂时还为解决
        //system("clear");//清屏操作
    }
}

//6、显示所有的联系人
void showPerson(struct Addressbook * abs)
{
    //判断通讯录中人数是否为0,如果为0,提示记录为空
    //如果不为0,显示记录的联系人信息
    if(abs->m_Size == 0)
    {
        cout<<"当前联系人为空"<<endl;
    }
    else
    {
        for(int i=0 ; i<abs->m_Size ; i++)
        {
            cout<<"姓名:"<<abs->personArray[i].m_Name<<"\t";
            cout<<"性别:"<<(abs->personArray[i].m_Sex ==1 ? "男":"女")<<"\t";//关键
            cout<<"年龄:"<<abs->personArray[i].m_Age<<"\t";
            cout<<"电话:"<<abs->personArray[i].m_Phone<<"\t";
            cout<<"住址:"<<abs->personArray[i].m_Addr<<endl;
        }
    }

    //pause();//按任意键继续
    //system("clear");//请屏
}

//7、检测联系人是否存在,如果存在,返回联系人所在数组中的具体位置,不存在返回-1
//参数1 通讯录 参数2 对比姓名
int isExist(struct Addressbook * abs , string name)
{
    for(int i=0 ; i<abs->m_Size ; i++)
    {
        //找到用户输入的姓名了
        if(abs->personArray[i].m_Name == name)
        {
            return i;//找到了,返回这个人在数组中的下标编号
        }
         return -1;//如果遍历结束都没有找到,返回-1
    }
}

//8、删除指定联系人
void deletePerson(struct Addressbook *abs)
{
    cout<<"请输入您要删除的联系人"<<endl;

    string name;
    cin>>name;

    //ret == -1 未查到
    //ret != -1 查到了
    int ret = isExist(abs,name);

    if(ret != -1)
    {
        for(int i=0 ; i<abs->m_Size ; i++)
        {
            //数据前移
            abs->personArray[i] = abs->personArray[i+1];//只能按顺序删除,把后一个数据覆盖调前一个数据,此代码有缺陷
        }
        abs->m_Size--;//更新通讯录中的人员数
        cout<<"删除成功"<<endl;
    }
    else
    {
        cout<<"查无此人"<<endl;
    }

    //pause();
    //system("clear");

}

//9、查找指定联系人:此代码有缺陷,必须先创建一个联系人信息才能执行完整代码
void findPerson(struct Addressbook * abs)
{
    cout<<"请输入您要查找的联系人"<<endl;

    string name;
    cin>>name;

    //判断指定的联系人是否存在通讯录
    int ret = isExist(abs,name);

    if(ret != -1)//找到联系人
    {
        cout<<"姓名:"<<abs->personArray[ret].m_Name<<"\t";
        cout<<"性别:"<<abs->personArray[ret].m_Sex<<"\t";
        cout<<"年龄:"<<abs->personArray[ret].m_Age<<"\t";
        cout<<"电话:"<<abs->personArray[ret].m_Phone<<"\t";
        cout<<"住址:"<<abs->personArray[ret].m_Addr<<endl;
    }
    else
    {
        cout<<"查无此人"<<endl;
    }

    //pause();
    //system("clear");
}

//10、修改指定联系人的信息
void modifyPerson(struct Addressbook * abs)
{
    cout<<"请输入您要修改的联系人"<<endl;
    string name;
    cin>>name;

    int ret = isExist(abs,name);
    if(ret != 1) {
        //修改姓名
        cout << "请输入姓名:" << endl;
        string name;
        cin >> name;
        abs->personArray[ret].m_Name = name;

        //修改性别
        cout << "请输入性别:" << endl;
        cout << "1--男" << endl;
        cout << "2--女" << endl;
        int sex = 0;

        while (true)
        {
            cin >> sex;
            if (sex == 1 || sex == 2)
            {
                abs->personArray[ret].m_Sex = sex;
                break;
            }
            cout<<"输入有误,请重新输入"<<endl;
        }

        //修改年龄
        cout<<"请输入年龄:"<<endl;
        int age = 0;
        cin>>age;
        abs->personArray[ret].m_Age = age;

        //修改电话
        cout<<"请输入联系电话:"<<endl;
        string phone;
        cin>>phone;
        abs->personArray[ret].m_Phone = phone;

        //修改住址
        cout<<"请输入家庭住址:"<<endl;
        string addr;
        cin>>addr;
        abs->personArray[ret].m_Addr = addr;
    }
    else
    {
        cout<<"查无此人"<<endl;
    }
}

//11、清空所有的联系人
void cleanPerson(struct Addressbook * abs)
{
    cout<<"是否确定清空?"<<endl;
    cout<<"1 -- 是"<<endl;
    cout<<"2 -- 否"<<endl;

    int num = 0;

    while(true)
    {
        cin >> num;
        if (num == 1)
        {
            abs->m_Size = 0;//将当前记录联系人的数量置为0,做逻辑清空操作
            cout << "通讯录已清空" << endl;
            break;
        }
        else if (num == 2)
        {
            cout << "停止清空" << endl;
            break;
        }
        cout<<"输入有误,请重新输入"<<endl;
    }
    //pause();
    //system("clear");
}

 int main()
 {

    //4、创建通讯录结构体变量
    struct Addressbook abs;

    //初始化通讯录中当前人员个数
    abs.m_Size = 0;

    int select = 0;//2、创建用户选择输入的变量

    while(true)
    {

        //1、菜单调用
        showMenu();

        //2、创建用户选择输入的变量
        cin>>select;

        //只有选择0的时候才退出,其他选择则继续循环
        switch (select) {
            case 1: //1、添加联系人
                addPerson(&abs);//5、增加联系人。利用地址传递,可以修饰实参
                break;

            case 2://2、显示联系人
                showPerson(&abs);//6、显示所有的联系人
                break;

            case 3://3、删除联系人

            /*
             * 测试代码
            {
                cout << "请输入需要删除的联系人姓名:" << endl;
                string name;
                cin >> name;
                if (isExist(&abs, name) == -1) {
                    cout << "查无此人" << endl;
                } else {
                    cout << "找到此人" << endl;
                }

            }
            */
                deletePerson(&abs);//7和8、删除指定联系人
                break;

            case 4://4、查找联系人
                findPerson(&abs);//9、查找指定联系人
                break;
            case 5://5、修改联系人
                modifyPerson(&abs);//10、修改指定联系人的信息
                break;
            case 6://6、清空联系人
                cleanPerson(&abs);//11、清空所有的联系人
                break;
            case 0://0、退出通讯录
                cout << "欢迎下次使用" << endl;
                pause();
                return 0;
            default:
                break;
        }

    }

}

7、程序的内存模型

7.1、内存四区

7.1.1、概念

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

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

- 全局区(存放量):存放全局变量、静态变量和常量(常量区存放const修饰的全局变量和字符串常量)

- 栈区(函参、局部):由编译器自动分配释放, 存放函数的参数值,局部变量等

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

7.1.2、意义(区域不同寿命不同)

不同区域存放的数据,赋予不同的生命周期, 给我们更大的灵活编程

7.2、程序运行前

在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域

​ 7.2.1、代码区

​ 存放 CPU 执行的机器指令

​ 代码区是共享的,频繁被执行的程序只需在内存中有一份代码即可

​ 代码区是只读的,防止程序意外地修改了它的指令

​ 7.2.2、全局区

​该区数据在程序结束后由操作系统释放

#include <iostream>

using namespace std;

//全局变量(凡函数体外的变量称为全局变量)
int g_a = 10;

//const修饰的全局变量称为全局常量
const int c_g_a = 10;

int main()
{
    
    //全局区:全局变量、静态变量、常量
    
    //创建普通局部变量(在函数体内的变量称为局部变量)
    int a = 10;
    cout<<"局部变量a的地址为:"<<&a<<endl;
    cout<<"全局变量g_a的地址为:"<<&g_a<<endl;
    
    //静态变量(在普通变量前加static,属于静态变量)
    static int s_a = 10;
    cout<<"静态变量s_a的地址为:"<<&s_a<<endl;
    
    //常量
    //字符串常量
    cout<<"字符串常量的地址为:"<<&"字符串常量地址"<<endl;
    
    //const修饰的变量
    //const修饰的全局变量
    cout<<"全局常量 c_g_a的地址为:"<<&c_g_a<<endl;
    
    //const修饰的局部变量称为局部常量
    const int c_l_a = 10;//c代表const l代表local g代表global
    cout<<"局部常量 c_l_a的地址为:"<<&c_l_a<<endl;
    
    return 0;
}

7.3、程序运行后

7.3.1、栈区

//由编译器自动分配释放, 存放函数的参数值,局部变量等
​//注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放

#include <iostream>
using namespace std;

int* func(int b)//形参数据也会放在栈区
{
    b = 100;
    int a = 10;//局部变量,存放在栈区,栈区的数据在函数执行完成后自动释放
    return &a;//不能返回局部变量的地址
}

int main()
{
    int * p = func(1);
    cout<<*p<<endl;//第一次可以打印正确的数字是因为编译器做了保留
    cout<<*p<<endl;//第二次这个数据就不再保留了

    return 0;
}

7.3.2、堆区

//由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
//在C++中主要利用new在堆区开辟内存

#include <iostream>
using namespace std;

int * func()//形参数据也会放在栈区
{
    //利用new关键字可以将数据开辟到堆区
    //指针,本质也是局部变量,放在栈上;指针保存的数据是放在堆区
    int * p = new int(10);
    return p;
}

void test01()
{
    int * p = func();
    cout<<*p<<endl;
    cout<<*p<<endl;
    cout<<*p<<endl;
    cout<<"---------"<<endl;
    //1、堆区开辟的数据,由程序员手动开辟,手动释放,释放利用关键字 ==delete==
    delete p;
    
    cout<<*p<<endl;//内存已经被释放,再次访问就是非法操作,会报错
}

//2、在堆区利用new开辟数组
void test02()
{
    //创建10整形数据的数组,在堆区
    int * arr = new int[10];
    
    for(int i=0 ; i<10 ; i++)
    {
        arr[i] = i+100;//给10个元素赋值 100~109
    }
    
    for(int i=0 ; i<10 ; i++)
    {
        cout<<arr[i]<<endl;
    }
    
    //释放堆区数组
    //释放数组的时候要加[]才可以
    delete[] arr;
}

int main()
{
    //在堆区开辟数据
    //int * p = func();
    test01();
    
    test02();

    //
    
    return 0;
}

8、引用

作用: 给变量起别名

8.1、引用的基本语法与注意事项

#include <iostream>
using namespace std;

int main()
{
    
    //语法: 数据类型 &别名 = 原名
    int a = 10;
    //创建引用
    int &b = a;
    //int &b;//1、错误,必须初始化
    
    //2、引用在初始化后不可以改变
    int c = 30;

    b = c;//赋值操作,而不是更改引用
    
    cout<<"原名:"<<a<<endl;
    cout<<"别名:"<<b<<endl;
    cout<<"c的值:"<<c<<endl;
    cout<<"--------"<<endl;
    
    b = 20;
    cout<<"原名:"<<a<<endl;
    cout<<"别名:"<<b<<endl;
    
    return 0;
}

8.2、引用做函数参数

作用:函数传参时,可以利用引用的技术让形参修饰实参

优点:可以简化指针修改实参

#include <iostream>
using namespace std;

void Swap(int &a,int &b)//其实 &a 是 a 的别名,&b 是 b 的别名
{
    int temp = a;
    a = b;
    b = temp;
}

int main()
{
    int a = 1;
    int b = 2;
    
    Swap(a,b);//引用传递和地址传递一样,都可以形参修饰实参
    
    cout<<"a = "<<a<<endl;
    cout<<"b = "<<b<<endl;

    return 0;
}

8.3、引用做函数的返回值

作用:引用是可以作为函数的返回值存在的

用法:函数调用作为左值

#include <iostream>
using namespace std;

//引用做函数的返回值
//1、注意:不要返回局部变量的引用
int& test01()
{
    int a = 10;//局部变量存放在四区中的 栈区
    return a;
}

//2、用法:函数的调用可以作为左值
int& test02()
{
    static int a = 10;//静态变量,存放在全局区,全局区上的数据在程序结束后系统释放
    return a;
}

int main()
{
    int &ref1 = test01();
    
    cout<<"ref1 = "<<ref1<<endl;//第一次结果正确,是因为编译器做了保留
    cout<<"ref1 = "<<ref1<<endl;//第二次结果错误,是因为a的内存已经释放
    cout<<"------"<<endl;

    int &ref2 = test02();
    
    cout<<"ref2 = "<<ref2<<endl;
    cout<<"ref2 = "<<ref2<<endl;
    
    test02() = 1000;//如果函数的返回值是引用,这个函数调用可以作为左值(左边的值)
    
    cout<<"-------"<<endl;
    cout<<"ref2 = "<<ref2<<endl;
    cout<<"ref2 = "<<ref2<<endl;
    
    
    return 0;
}

8.4、引用的本质

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

结论:C++推荐用引用技术,因为语法方便,引用本质是指针常量,但是所有的指针操作编译器都帮我们做了

8.5、常量引用

#include <iostream>
using namespace std;

void showValue(const int &val)//const修饰形参,防止误操作
{
    //val = 1000;//加了const防止形参改变实参 assignment:分配 ,reference:引用
    cout<<"val="<<val<<endl;
}

int main()
{
    
    //作用:常量引用主要用来修饰形参,防止误操作
    int a = 10;
    //int &ref = 10;//10是在常量区,引用必须引一块合法的内存空间
    
    //加上const之后编译器将代码修改 int temp = 10;const int & ref = temp;
    //const int &ref = 10;
    //ref = 20;//加入const之后变为只读,不可以修改
    
    showValue(a);
    cout<<"a = "<<a<<endl;
    
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42535298/article/details/122796753