一、取地址運算符&(內存地址)
C++編譯的程序佔用的內存分爲以下幾個部分:
1.棧區:由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似於數據結構中的棧。與其它分區不同,變量的地址是按照申請順序降序排列的。
2.堆區: 由new指令申請,由delete指令釋放。申請後不釋放可能會造成內存資源的浪費。需要指出,對於用指針申請的對內存空間,指針本身存入棧,指針指向的空間則存儲在堆中。
3.全局(靜態)變量區:全局變量和靜態(由static修飾)變量的存儲是放在一塊的。從程序開始運行時寫入變量值,運行結束前釋放變量。
4.程序代碼區:用於存放函數體的二進制代碼。
另外,還有專門用於存放常量的區域。
下面編寫程序進行測試,證明以上幾種變量分區不同。
#include
<iostream> using namespace std; int x,y,z; void f1(){}; void f2(){}; void main() { int a,b,c; cout<< "局部變量的地址:" <<endl; cout<<&a<< "
" <<&b<< "
" <<&c<<endl; //地址降序 int *m
= new int [3],
*n = new int ,
*l = new int ,
*k = new int ; cout<< "指針所指向的內存的地址:" <<endl; cout
<< &(*n) << "
" <<
&(*l) << "
" <<
&(*k) << endl; //指針本身存入棧,地址降序 cout<< "指針指向空間的地址:" <<endl; cout
<<&(m[0])<< "
" <<&(m[1])<<
"
" <<&(m[2])<<
endl; static int w; cout<< "全局(局部)變量的地址:" <<endl; cout<<&x<< "
" <<&y<< "
" <<&z<< /*"
"<<&w<<*/ endl; cout<< "函數代碼的地址:" <<endl; cout<<&main<< "
" <<&f1<< "
" <<&f2<<endl; } |
程序運行結果:
由程序運行結果可以看出:
1. 四種變量的地址相互相差很大,而本身相差很小,說明被分配在了不同的內存空間。
2. 指針和變量的地址是降序排列的,說明被分配在了棧區。
3. 全局變量和局部變量的地址相鄰,說明二者被安排在了同一分區。
注意:不要把取地址運算符&和應用運算符&搞混,前者放在變量定義中的變量的前面,而後者放在函數原型定義中的類型名後面。
二、指針變量
我們要想更好的利用上面提到的地址,就必須設定一個變量來保存地址。像用變量來保存int、float等類型的值一樣,地址也可以用類似的方法保存。
定義:用來保存地址的變量叫做指針變量(pointer variable)或者簡稱指針。
如:int* ptr;星號表示指向誰,這個聲明定義了一個指向整型數據的指針變量。這個變量中保存整型數據的地址:ptr=&variable;
1) 訪問指針指向的變量。
*ptr就表示了ptr中的地址上存儲的值。變量名前面的星號表示間接引用運算符(dereference operator)或者叫做取內容運算符(contents operator)。
2)void指針
指針保存的變量必須和指針的類型保持一致,如不能將一個float類型的地址賦給一個指向int的指針。
float flovar=98.6; int *
ptr=&flovar; //錯誤:不能將一個float類型的地址賦給一個指向int的指針 |
但是有一個通用的指針可指向任何數據類型,這類指針成爲void:如void* ptr;
三、指針和數組
首先來看看數組表示法和指針表示法訪問同一數組元素:
#include
<iostream> using namespace std; int main() { int intarray[5]={31,54,36,47,38}; for ( int j=0;j<5;j++) { cout<<intarray[j]<<endl
//數組表示法 <<*(intarray+j)<<endl; //指針表示法 } return 0; } |
指針表示法:數組名就是數組的首地址,在這裏+j就是數組第j+1個元素的地址。再加上取內容運算符*就得到了地址上儲存的數據了。
考慮:爲什麼系統能知道+j就跳到j+1個元素的地址呢,在這裏地址明明四個字節四個字節的增長的?
答:還記得前面討論void指針的時候說的嗎,在指針說明的時候必須包含指針所指向的數據類型,因爲編譯器要知道一個指針是int類型的還是float類型的,這樣它才能在訪問數組元素的時候執行真確的算術運算。
1) 指針常量和指針變量
表達式intarray是系統分給數組空間的首地址,數組將一直保持着這個地址知道程序結束。intarray是一個指針常量,不能進行++或者--運算,就和不能說7++一樣。只有變量纔可以進行這樣的運算。
四、指針和函數
1)首先,同樣進行引用傳遞參數和指針傳遞參數的對比:
#include
<iostream> using namespace std; void funcref( double &); void funcptr( double *); int main() { double var=10.0; cout<< "前var=" <<var<<endl; funcref(var); cout<< "引用var=" <<var<<endl; funcptr(&var); cout<< "指針var=" <<var<<endl; return 0; } void funcref( double &
v) { v
*= 3.14; } void funcptr( double *
ptr) { *ptr*=2; } |
在這裏,指針作爲參數和應用一樣,他傳遞的不是變量本身,而是變量的地址。(引用是原始變量的一個別名,指針是原始變量的地址)
因爲ptr中包含的是var的地址,所以任何對*ptr的操作實際上都是對var的操作。
2)排序數組元素
冒泡法排序:
#include<iostream> using namespace std; int main() { void order( int *, int ); const int N=10; int array[N]={1,3,234,23,54,656,76,878,87,57}; cout<< "比較前:" ; for ( int j=0;j<N;j++) cout<<*(array+j)<< "
" ; order(array,N); cout<< "\n比較後:" ; for ( int j=0;j<N;j++) cout<<*(array+j)<< "
" ; cout<<endl; return 0; } void order( int *
ptr, int n) { void bijiao( int *, int *); for ( int j=0;j<n-1;j++) { for ( int i=j+1;i<n;i++) { bijiao(ptr+j,ptr+i); } } } void bijiao( int *
m, int *
n) { int tem; if (*m>*n) { tem=*m; *m=*n; *n=tem; } } |
五、指針和C類型字符串
1)字符串常量指針:char str1[]=”Defined as an array”;(c類型字符串)
char* str2=”Defined as an array”;(指針類型字符串)
2)看個程序:
#include<iostream> using namespace std; int main() { void copystr( char *, const char *); char *
str1= "hah
is hahah!" ; char str2[80]; copystr(str2,str1); cout<<str2<<endl; return 0; } void copystr( char *
dest, const char *
src) { while (*src) { *dest++=*src++; } *dest= '\0' ; } |
※注意※=====const的使用================================================
(http://bbs.chinaunix.net/viewthread.php?tid=683333&extra=&page=1)
關鍵問題點:const 屬於修飾符 ,關鍵是看const 修飾的位置在那裏
1、const int *a
這裏const 修飾的是int,而int定義的是一個整值
因此*a 所指向的對象 值 不能通過 *a 來修改,但是 可以重新給 a 來賦值,使其指向不同的對象
eg:
const int *a = 0;
const int b = 1;
int c = 1;
a = &b //ok! 額外:注意不能通過a 來修改 b值
a = &c //ok! 額外:雖然c本身不是一個常量
*a = 2 //erro! 爲題就在這裏,不能修改通過 *a 所指向的對象值,最後賦值得對象是c,因此不能通過*a 來修改c值。
2、int *const a
這裏const修飾的是 a ,a代表的是一個指針地址
因此不能賦給a其他的地址值,但可以修改a指向的值
這有點和cont int *a相反的意味,例子就不說了
3、至於int const *a 和 const int *a 的意義是相同的 他們兩個的作用等價
補充:
4、const int * const a
這個代表a所指向的對象的值以及它的地址本身都不能被改變
5.const int a = 10 和 int const a = 10有什麼區別?
這應該沒區別,指針的話有區別
對指針來說,可以指定指針本身爲const,也可以指定指針所指的數據爲const,或二者同時指定爲const。
關於const的點滴補充:
1、const 對象的地址只能賦值給指向const 對象的指針
2、指向const 對象的指針可以 被賦 以 一個非const 對象的地址
3、指向const 的指針常被用作函數的形式參數,保證被傳遞給函數的實際對象在函數得實際對象在函數中不會被修改
4、常量在定義後就不能被修改,所以它必須被初始化。未初始化的常量定義將導致編譯錯誤(上面都是在說明const得問題,所以沒有賦值,實際語句中要賦值的)
3)指針數組和數組指針
==================指針數組,數組指針==================================
對於理解指針數組和數組指針也比較有用,如:
--------------指針數組(首先是個數組)--------------------
int *p[10];//指針數組,含有10個指針元素
也就是說每一個元素都是指針。先是解析[]表示它是一個數組,然後*表示指針,int表示爲int型指針,即表示定義一個指針數組,含有10個int類型指針元素。
--------------數組指針(首先是個指針)--------------------
int (*p)[10];//數組指針,這個指針能夠用來指向含有10個元素的整數數組。
先是解析(),括號裏表示這個是個指針,然後[]表示數組,即表示定義了一個指向數組的指針。數組指針。