指針和數組.
唸叨了這麼多年的指針,咱來看看指針的定義:
指針(K&C): 指針是一種保存變量地址的變量.
ps: 這裏的指針是指的指針類型變量,簡稱指針或指針變量.
ps: 注意!標準中把指針的類型稱爲 "(指向)T的指針"
在ANSI C89中有這樣的描述:
1.指針類型可以由函數類型,對象類型或者不完整類型派生。
2.派生指針類型的類型稱爲引用類型,從引用類型構造指針類型的過程稱之爲指針類型的派生。
ps: 須將引用類型和引用(&)區分開。
-
對於運算符*和&.
-
operator&
取地址符,&的操作數是左值,&操作的結果是指針類型的右值。
-
operator*
解引用運算符,*的操作數是右值,*操作結果返回一個左值.
-
-
void類型的指針
-void*即無類型指針
無類型指針可以保存任何類型對象的地址。 ps: 有此特性可以用於設計各種回調.
-
指針與constraint.
-
類型限定包括const和volatile
限定詞修飾其左詞. 若不存在作詞則修飾第一個右詞. const int var = 100; //const 修飾int int const var = 200; //const 修飾int int const * p = &var;//const 修飾int int * const p = &var;//const 修飾int * Ps:其實就是c++中的頂層const和底層const之別.
-
且看數組是如何定義的:
數組:數組是存儲單一數據類型對象的集合.
數組由數組名(標識符),類型名和數組唯獨組成.
數組的初始化:可以使用初始化列表{,}進行初始化。
關於多維數組:在物理內存上是先行結構的,在邏輯上是多維的.
單獨的數組和指針定義描述完,且看歷史回光,50多年來多少愛恨糾葛(數組與指針的愛恨糾纏從1970的c就開始了吧).
數組與指針.
1.數組名:數組名是指向數組第一個元素(值得推敲)的地址的指針,這個指針是個常量.
2.sizeof, sizeof對數組名做運算時,雖然數組名是指針,但運算得到的結果是數組的長度(length of bytes)
3.&, 對於取址運算,&對數組名做取址運算,產生的結果是"指向一維或多維的數組的指針",而不是指針本身的地址.
4.指針數組,和數組指針.
後綴運算符的優先級高於前綴運算符.
標識符的第一次結合往往能夠判斷類型
---->>
int *p[10]; //元素類型爲int*的指針數組p
int (*p)[10]; //指向int[10]數組類型的指針p
5.數組的引用
ps:引用的本質是指針.
int a[10][4];int (&r)[4] = a[1]; //OK
int (&r)[4] = a[1]+1; //ERROR //對引用的複製必須是左值.
//我沒見過引用的數組, 初始化困難.
動態內存分配.
1.首先淺談下c++的內存結構.
- 內存的管理策略:
- 任意大小分配策略
- 固定大小分配策略
- 垃圾收集分配策略
- 內存管理的分層(一般存在很多層管理(內存池是其中一層的策略實現))
- 內核層提供基礎的分配服務.
- 編譯器有自己的內存分配策略(dsp都可以配置)
- c++的malloc-free和new-delete(建立在操作系統本地服務的基礎上)
- c++對內存的劃分
- 棧(stack)
- 棧存儲自動變量
- 棧變量的分配和釋放由編譯器自動完成(運行時存在)
- 棧是先進後出的存儲結構.
- 棧的大小
- linux默認棧大小是8MB
- windows默認棧大小是2MB(1M)
- 棧的大小是可以配置的.
- 棧也是可以通過alloca函數動態分配的.
- 堆
- 用戶通過malloc-free和new-delete自行分配和釋放.
- 堆是動態分配的,運行時分配
- 可以使用RAII實現簡單gc
- 堆的大小是4G(32位進程地址空間,進程的堆)
- 全局/靜態存儲區
- 全局變量和靜態變量存在的地方
- 還可能細分未初始化全局/靜態的bss區
- 常量存儲區
- 這個區域存儲常量,不允許修改
- 通過內存頁的屬性readonly的嗎?
2.動態內存的分配
- 內存分配和釋放
- 單個對象的分配和釋放.
- int *ptr = new int;
- delete ptr;
- 動態數組的創建
- 一維數組
- int *parr = new int[20];
- delete []parr;
- 多維數組
- int (*parr)[20][50] = new int[30][20][50];
- delete []parr;
- Ps:
- int ***ptr = new int[2][3][4];//ERROR, 類型不同.
- 按照整體的方法分配的多維數組在物理上是連續的。
- 多維數組也可以多次分維度分配,不過這樣就不是連續的
------------------------------------------------
#include<iostream>
#include<memory>
#include<string>
#include <iterator>
int main(int argc, char** argv)
{
//方案一,分維度分配.
int** x = new int*[20];
for (int i = 0; i < 20; i++)
*(x + i) = new int[20];
if (int(x + 1) == int(*x + 20)) {
std::cout << "方案一動態數組在物理上是線性的." << std::endl;
}
//方案二, 整體分配.
int(*ptr)[20][500] = new int[200][20][500];
if (int(ptr + 1) == int(*ptr + 20))
std::cout << "方案二動態數組在物理上是線性的." << std::endl;
delete[]ptr;
return 0;
}
-----------------------------------------------
print:
方案二動態數組在物理上是線性的.
- 懸垂指針(野指針)
- int p = new int; delete p;
- 此時p爲懸垂指針.
- 解決方案, delete釋放對對象後, p = nullptr; //OK.
- 堆變量可以很方便的承擔起線程通信的角色.
- 堆是進程共享的資源, 獨立於線程之外存在,所以可以很方便的實現線程共享.
- 動態聯編和靜態聯編
- 動態聯編:運行時束定,不需要時可以不創建
- 靜態聯編:編譯時束定,不管使用與否,編譯後即存在.
- delete的對稱
- 如果釋放動態數組時,忘記加[]可能是致命的, 也有可能造成內存不釋放完,造成內存泄漏.
- 具體的錯誤和內存管理器相關.
- 初始化動態數組
- int *p = new int[20]();
- //注意這裏是對空的(),不允許提供初始化值; new int[20](0);//ERROR.
- A *pa = new A();
- //對於類類型不允許使用除默認構造外的構造函數進行初始化.
- malloc-free無法滿足對象類型的動態分配.