第二部分 C++核心編程一
目錄
1,內存分區模型
C++在程序執行時,將內存大方向劃分爲4個區域
- 代碼區:存放函數體的二進制代碼,由操作系統進行管理的
- 全局區:存放全局變量和靜態變量以及常量
- 棧區:由編譯器自動分配釋放,存放函數的參數值,局部變量等
- 堆區:由程序員分配和釋放,若程序員不釋放,程序結束時由操作系統回收
劃分的意義:不同區域存放的數據,賦予不同的生命週期,給我們更大的靈活編程
1.1程序運行前
未執行該程序前分爲兩部分
代碼區:
存放CPU執行的機器指令
代碼區是共享的,共享的目的是對於頻繁被執行的程序,只需要在內存中有一份代碼即可(.exe文件每次執行都是這一段代碼)
代碼區是只讀的,使其只讀的原因是防止程序意外地修改了他的指令 (.exe文件不能被修改)
全局區:
全局變量和靜態變量存放在此
全局區還包括了常量區,字符串常量和其它變量也存放在此(常量區包括:const修飾的全局常量和字符串常量)
該區域的數據在程序結束後由操作系統釋放(只要是局部定義的都不在全局區,不論是字符串還是字符)
如下一段程序代碼運行結果:
#include<iostream>
#include<string>
using namespace std;
int aa = 10;
int bb = 11;
const int c_a_1 = 10;
const int c_b_1 = 10;
int main()
{
int a = 10;
int b = 11;
static int a_1 = 10;
static int b_1 = 10;
string a_2 = "dasfaf";
string b_2 = "hjkjhh";
const int c_a_2 = 10;
const int c_b_2 = 10;
cout << "局部a=" << (int)&a << endl;
cout << "局部b=" << (int)&b << endl << endl;
cout << "全局aa=" << (int)&aa << endl;
cout << "全局bb=" << (int)&bb << endl << endl;
cout << "靜態a_1=" << (int)&a_1 << endl;
cout << "靜態b_1=" << (int)&b_1 << endl << endl;
cout << "常字符串=" << (int)&"adssasdf" << endl;
cout << "常字符串=" << (int)&"dsaassaa" << endl << endl;
cout << "局部字符串a_2=" << (int)&a_2 << endl;
cout << "局部字符串b_2=" << (int)&b_2 << endl << endl;
cout << "全常c_a_1=" << (int)&c_a_1 << endl;
cout << "全常c_b_1=" << (int)&c_b_1 << endl << endl;
cout << "局常c_a_2=" << (int)&c_a_2 << endl;
cout << "局常c_b_2=" << (int)&c_b_2 << endl << endl;
system("pause");
return 0;
}
1.2程序運行後
棧區:由編譯器自動分配釋放,存放函數的參數值,局部變量等
注:不要返回局部變量的地址,棧區開闢的數據由編譯器自動釋放,所以當自定義函數使用過後,局部變量就會被自動釋放
int *ap(int a)
{
a = 1;
int b = 2; //局部變量,放在棧上,函數執行,棧區的數據就被釋放了
return &a(或者return &b); //返回局部變量地址,只能保留一次該數據
}
此時不論返回a的地址還是b的地址,都會出錯,因爲形參也是局部變量,所以當這個函數運行過後,都會被自動釋放。
#include<iostream>
#include<string>
using namespace std;
int b = 1;
int *ap()
{
int a = 1; //局部變量,放在棧上,函數執行完,棧區的數據就被釋放了
return &a; //返回局部變量地址
}
int *aq()
{
return &b; //返回全局變量地址
}
int main()
{
int *p = ap();
cout << "p=" << *p << endl; //第一次可以打印正確數據,編譯器做了保存
cout << "p=" << *p << endl; //第二次這個數據就不被保存了
int *q = aq();
cout << "q=" << *q << endl; //因爲返回的是全局變量的地址,所以不會被自動釋放,只有程序結束後纔會被釋放
cout << "q=" << *q << endl;
system("pause");
return 0;
}
堆區:由程序員分配釋放,如果程序員不釋放,則程序結束時由操作系統回收
在C++中主要利用new在堆區開闢內存
指針本質上也是局部變量,放在棧上,指針保存的數據是放在堆區
int *ap()
{
int *b = new int; //定義堆區數據
*b = 10;
return b; 堆區的數據不會被釋放
}
1.2new操作符
C++中利用new操作符在堆區開闢數據
堆區開闢的數據,由程序員手動開始,手動釋放,釋放利用操作符delete
語法:new 數據類型
利用new創建的數據,會返回該數據對應的類型的指針
int *p = new int(10); //堆區開闢一個數據
delete p; //釋放該數據int *q = new int[10]; //堆區開闢一個數組
delete[] q; //釋放該數組
2.引用
2.1引用的基本使用
作用:給變量起別名(兩個變量的地址一樣)
語法:數據類型 &別名=原名
int a = 10;
int &b = a;
2.2注意事項
- 引用必須初始化
- 引用在初始化後,不可改變
int a = 10, aa = 20;
int &b = a;
int &c; //錯誤,引用必須初始化
&b = aa;//錯誤,初始化後,不可改變
b = aa;//賦值操作
2.3引用做函數參數
作用:函數傳參時,可以利用引用的技術讓形參修飾實參
優點:可以簡化指針修改實參(可以參照值傳遞和地址傳遞)
void swap(int &a, int &b) //引用傳遞交換兩數的值
{
int x = a;
a = b;
b = x;
}
#include<iostream>
#include<string>
using namespace std;
void swap(int &a, int &b)
{
int x = a;
a = b;
b = x;
}
int main()
{
int a = 10, b = 20;
swap(a, b);
cout << a << " " << b << endl;
system("pause");
return 0;
}
2.4引用做函數返回值
作用:引用是可以作爲函數的返回值存在的
注意:不要返回局部變量的引用(和不要返回局部變量地址類似)
用法:函數調用作爲左值
int &test1()
{
int a = 10; //錯誤,局部變量,系統只保存一次
return a;
}int &test2()
{
static int b = 10; //正確,全局變量
return b;
}
特殊賦值方法,因爲函數返回的是一個引用,所以仍然可以賦值:eg:test2() = 111;
2.5引用的本質
引用的本質在C++內部實現是一個指針常量
int &a = b; //轉換爲 int *const a=&b;
a = 10; //轉換爲 *a=10;
2.6常量引用
作用:常量引用主要用來修飾形參,防止誤操作
在函數形參列表中,可以用const修飾形參,防止形參改變實參
const int &a = 10; //轉換爲int b=10; const int &a=b;
const使用場景,在當作形參時使用,防止數據被誤操作
void show(const int &a)
{
//a = 20; //錯誤,因爲傳遞時爲const所以不能改變
cout << a << endl;
}
#include<iostream>
#include<string>
using namespace std;
void show(const int &a)
{
//a = 20; //錯誤,因爲傳遞時爲const所以不能改變
cout << a << endl;
}
int main()
{
int a=10;
show(a);
system("pause");
return 0;
}
3,函數的提高
3.1函數的默認參數
在C++中,函數的形參列表中的形參可以是有默認值的
語法:返回值類型 函數名(參數=默認值){}
int test(int a = 1, int b = 2, int c = 3)
{
return a + b + c;
}
注1:如果某個位置已經有了默認值,那麼從這個位置往後,從左到右都必須有默認值
int test(int a = 1, int b, int c) //錯誤
注2:如果函數聲明有默認參數,函數實現就不能有默認參數‘(聲明和實現只能有一個默認參數)
int test(int a = 1, int b = 2, int c = 3);
int test(int a = 1, int b = 2, int c = 3) //錯誤,聲明時,函數已經有參數了,所以實現不能有參數
{
return a + b + c;
}
3.2函數佔位參數
C++中函數的形參列表裏可以有佔位參數,用來佔位,調用函數時必須填補該位置
語法:返回值類型 函數名(數據類型){}
void test1(int a, int)
{
cout << "佔位參數" << endl;
}
3.3函數重載
作用:函數名可以相同,提高複用率
滿足條件:
- 同一個作用域下
- 函數名稱相同
- 函數參數不同或者個數或者順序不同
返回值類型不可以作爲函數重載的條件
我的另一篇關於重載的博客鏈接:https://blog.csdn.net/qq_46423166/article/details/106594280
函數重載的注意事項
- 引用作爲重載條件
- 函數重載碰到函數默認參數
void test(int &a)
void test(const int &a) //這兩個可以構成重載
void test(int a)
void test(int a, int b = 10) //因爲包含默認參數,所以不能構成重載