指針和數組

指針和數組.

唸叨了這麼多年的指針,咱來看看指針的定義:

指針(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無法滿足對象類型的動態分配.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章