內存與指針雜談
1、指針
1、數組指針
int(*ptr)[n]
()的優先級高,(*ptr)表示ptr是一個指針,指向一個int類型的一維數組,這個數組的長度為n,也可以說ptr的步長就是n。也就是說執行ptr+1時,ptr要跨過n個int的長度。
2、指針數組
int* p[n]
[]的優先級高,p和[]先結合表示一個數組,int*表示數組中元素的數據類型為int*。
3、復雜一點的指針
int* (*ptr)[n]
ptr是一個指向數組的指針,數組中的每個元素都是指向int的指針。
4、指針運算
指針運算:一個指針ptrold加(減)一個整數n后,結果是一個新的指針ptrnew,ptrnew的類型和ptrold的類型相同,ptrnew所指向的類型和ptrold所指向的類型也相同。ptrnew的值將比ptrold的值加或減了n乘sizeof(ptrold所指向的類型)個字節。
5、指針變量和指針所指向的內存空間是兩個不同的概念
6、指針做函數參數,是指針存在的最大意義
*p間接賦值成立的3個條件:
- 2個變量(通常一個實參,一個形參)。
- 建立關系,實參取地址賦給形參。
- *p形參去間接修改實參的值。
引申:函數調用時,用n級指針(形參)修改n-1級指針(實參)的值。
7、一個指針的內容
-
指針的類型。
-
指針所指向的類型。
-
指針的值或者叫指針所指向的內存區或地址
- 指針的值都是指針本身存儲的數值,這個數值將被編譯器當做一個地址,而不是一般的數值,在32位程序里所有類型的指針的值都是一個32位整數,因為32位程序里內存地址都是32位長。
- 指針所指向的內存區就是從指針的值所代表的那個內存地址開始,長度為sizeof(指針所指向的類型)的一片內存區。
-
指針本身所占據的內存區。在32位平台里,指針本身占據了4個字節的長度。
2、內存
1、內存四區:
-
棧區(stack):由編譯器自動分配釋放,存放函數參數值,局部變量的值等。其操作方式類似於數據結構中的棧。
-
堆區(heap):一般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收。注意它與數據結構中的堆是兩個概念。分配方式倒是類似於鏈表。
-
數據區:主要包括靜態全局區和常量區,如果站在匯編角度細分的話還可以有很多小的區。
- 全局區(靜態區)(static):全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域,,未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域,程序結束后由系統釋放。
- 常量區:常量字符串就是放在這里。程序結束后由系統釋放。
-
代碼區:存在函數體的二進制代碼。
2、變量
變量的本質:一段連續內存空間的別名。
數據類型的本質:固定大小內存空間的別名。
1、修改變量的方法
- 直接修改,int a = 10;
- 間接修改,內存有地址編號,拿到地址編號也可以修改內存,外掛的原理就是通過變量內存地址來修改變量值:&a = 1245024; ((int)(1245024)) = 10。
- C++中引用。
2、聲明變量的意義
- 建立變量符號表。
- 變量的數據類型指示了系統分配多少內存空間。
- 變量的數據類型指示了系統如何解釋內存空間中的值。
- 變量的數據類型確定了變量的取值范圍。
- 不同的數據類型有不同的操作。
3、函數內存模型
- 主調函數可以把堆區、棧區、全局數據內存地址傳遞給被掉函數。
- 被掉函數只能返回堆區、全局數據。
4、避免產生野指針:
- 定義指針時,把指針變量賦值為NULL。
- 釋放內存時,先判斷指針變量是否為NULL。
- 釋放完內存后,把指針變量重新賦值成NULL
3、void與void*
- 如果函數沒有返回值,那么應聲明為void類型。
- 如果函數無參數,那么應聲明其參數為void。
- 小心使用void指針類型,ANSI標准,不能對void指針進行算法操作。
- 如果函數的參數可以是任何無類型指針,那么應聲明其參數為void*。
例:典型的如內存操作函數memcpy,memset的函數原型:
void * memcpy(void *dest, const void *src, size_t len);
void * memset(void *buffer, int c, size_t num);
這體現了內存操作函數的意思,因為它操作的對象僅僅是一片內存,而不論這片內存是什么類型。
- void不能代表一個真實的變量。