在C++中,有一種神奇的變量,它不能夠表示一個值,可是能夠表示某個元素的地址,經過地址來訪問這個元素。數組
打個比方:你有一張地圖和一個座標,你就能夠經過訪問這個座標來達到你訪問座標所表示的元素的目的。指針變量就是這個「座標」。函數
下面咱們來具體看看指針變量的應用。大數據
正如上面所說,指針變量不能夠表示一個值,可是能夠指向別的元素的地址,經過這個地址來間接訪問這個元素的值。優化
因爲它的性質,指針變量不能夠直接=一個元素,賦值時要注意。spa
具體操做下面會講到。指針
如何聲明一個指針變量? 有以下表達式:code
數據類型+「*」+指針名blog
一般咱們這樣賦值:內存
int main() { int *p=NULL; return 0; }
這樣咱們就定義了一個指針類型的變量p,NULL是空內存,這個內存裏什麼元素都沒有,你能夠以後再給p賦一個元素的地址。(能夠不用=NULL,可是這是我的習慣,相似於return 0好習慣這種……)io
這個語句的意義是:定義一個int類型的指針p,指向空地址。
那麼怎麼把一個元素的地址賦給一個指針變量呢?
有以下語句:
#include<cstdio> using namespace std; int main() { int a; int *p=NULL; p=&a; return 0; } /*int main() { int a; int *p=&a; return 0; }*/
上面兩個主函數的效果是同樣的。
咱們說說這兩段代碼的意義:
相信你們都用過scanf( ),在咱們輸入變量名前要加一個符號「&」,這就是地址符,表示變量名的地址。
咱們的指針要指向一個地址,固然就是:指針名=&變量名啦!
既然咱們會賦值了,下一步就是調用元素值,可是指針指向的是一個地址,不能直接參與運算,這時候咱們要用到間接運算符「*」。(就是定義的時候那個星號)
若是咱們有一個元素a,須要用指針來輸出它,怎麼操做?
對於這個問題,有以下程序:
#include<cstdio> using namespace std; int main() { int a; scanf("%d",&a); int *p=&a;//定義一個指針變量p指向元素a printf("%d",*p);//間接運算符+指針名錶示指針所指元素 return 0; }
代碼註釋已經很詳盡了,咱們的指針指向一個元素,用「*」+指針名便可訪問指針所指元素
注意:經過指針操做元素值和直接操做元素值是同樣的,好比下面這段代碼:
#include<cstdio> using namespace std; int main() { int a,b,s,t,*pa=NULL,*pb=NULL; pa=&a,pb=&b; a=10,b=20; s=*pa+*pb; t=*pa**pb; printf("%d %d",s,t); return 0; }
程序給出的結果是30 200。
首先咱們給出一個基本的定義:
當咱們定義數組的時候,系統會給出連續的地址,好比a[5],假設a[0]的地址是0,那麼a[1]的地址就是1……以此類推。
此時,咱們直接把地址+1(指針+1),就能夠訪問數組的下一個元素。
#include<cstdio> using namespace std; int main() { int a[5],*p=&a[0]; for(int i=0;i<5;i++) scanf("%d",&a[i]); for(int i=0;i<5;i++) { printf("%d ",*p); p++; } return 0; }
對於p--,同理。這個語句輸出了a[ ]中的全部變量。
指向數組的指針叫數組指針,衆所周知,一個數組的地址是連續的,首地址就是他所佔有的幾個單元的首地址,咱們能夠把數組名賦給指針,也能夠把數組中某個元素的地址賦給它。
有如下語句:
int a[5],*p=a;
則如下三個語句
&a[0],a,*p,均指向同一個單元——數組的首地址。
那麼能夠推導:&a[i]、a+i、p+i,均指向數組a中a[i]的地址。
有以下代碼:
#include<cstdio> using namespace std; int main() { int a[5],*pa=a; for(int i=0;i<5;i++) scanf("%d",&a[i]); for(int i=0;i<5;i++) printf("%d %d %d %d\n",*(pa+i),pa[i],a[i],*(a+i)); return 0; }
咱們輸如5個數:1 2 3 4 5
系統給出了5行:
1 1 1 1
2 2 2 2
3 3 3 3
4 4 4 4
5 5 5 5
這說明上面4個語句:*(pa+i),pa[i],a[i],*(a+i)是等價的。
代碼說明和注意事項:
一、a(數組名)能夠加「*」變爲常量指針,a是開始元素,根據指針的加減原理,a+i是第i個元素的地址。
二、a是常量名,不能直接進行指針的+、-操做(這裏指的是p++、p--這種賦值操做非法,可是a+i這種是合法的),可是pa是指針變量,能夠進行加減操做。
咱們用以下語句能夠申請一個系統空間給指針p:
int *p=new(int);
此時*p的內容不肯定。
這個語句是動態數組的基礎。
一、原理:以前說過,若是咱們一次申請多個空間,系統會發給咱們連續的新地址,能夠當作數組用。
二、具體操做
有以下代碼:
#include<cstdio> using namespace std; int main() { int n,*p; scanf("%d",&n); p=new int[n+1];//申請連續的n+1個空間給指針p for(int i=1;i<=n;i++) scanf("%d",&p[i]); for(int i=1;i<=n;i++) printf("%d ",p[i]); return 0; }
若是咱們輸入:
5
1 2 3 4 5
系統給出
1 2 3 4 5
上面的代碼你能夠理解有一個數組,數組名就是指針名,其他操做和第5個板塊中提到的同樣。(經過數組名+下標訪問)
咱們還能夠改爲這個樣子:
#include<cstdio> using namespace std; int main() { int n,*p; scanf("%d",&n); p=new int[n+1];//申請連續的n+1個空間給指針a for(int i=1;i<=n;i++) scanf("%d",&p[i]); for(int i=1;i<=n;i++) { p++;//因爲p默認指向第0個元素,因此先++ printf("%d ",*p); } return 0; }
這裏使用指針訪問而不使用數組名訪問,和上面的代碼是等價的。固然你也能夠寫成這樣:printf("%d ",*(p+i));在上面提到過,這幾種寫法是等價的。
前面扯了那麼多指針的基本定義和寫法,終於到了今天的正題了——利用指針創建動態數組。
咱們給出一個情景:如今有一個巨大(行列<=10000000)可是稀疏(大部分元素是0)的矩陣,咱們要對這個矩陣進行操做,怎麼辦呢?
顯然,這樣的代碼是絕對行不通的。
#include<cstdio> #define N 10000100 using namespace std; int n[N][N];
若是這麼寫,你的空間複雜度是絕對過不了的。
咱們要進行優化才行。
記得指針能夠申請空間嗎?咱們能夠利用這個特性,避免存儲無效數據(0),咱們爲每一次輸入的有效數據開一個新的內存單元,這樣就不會爆內存啦!
咱們看下面這個例題:
【問題描述】
矩陣能夠認爲是N*M的二維數組。如今有一個巨大但稀疏的矩陣。
N,M的範圍是1<=N,M<=100000,有K個位置有數據,1<=K<=100000。
矩陣輸入的方式是從上到下(第1行到第N行),從左到右(從第1列到第M列)掃描,記錄有數據的座標位置(x,y)和值(v)。這是按照行優先的方式保存數據的。
如今要求按照列優先的數據,即從左到右,從上到下掃描,輸出有數據的座標和位置。
【輸入格式】
第1行:3個整數N,M,K,其中1<=N,M,K<=100000;下面有K行,每行三個整數:a,b,c,表示第a行第b列有數據c。數據在int範圍內,保證是行優先的次序。
【輸出格式】
1行,K個整數,是按照列優先次序輸出的數
【樣例輸入】
4 5 9
1 2 12
1 4 23
2 2 56
2 5 78
3 2 100
3 4 56
4 1 73
4 3 34
4 5 55
【樣例輸出】
73 12 56 100 34 23 56 78 55
【樣例解釋】
0 | 12 | 0 | 23 | 0 |
0 | 56 | 0 | 0 | 78 |
0 | 100 | 0 | 56 | 0 |
73 | 0 | 34 | 0 | 55 |
對於這個矩陣,咱們能夠這樣存:
73 | 12 | 34 | 23 | 78 |
—— | 56 | —— | 56 | 55 |
—— | 100 | —— | —— | —— |
—— | —— | —— | —— | —— |
注:標記「------」的都是沒有使用的內存,這樣咱們就節省了11個內存單元,對於大數據的時候,咱們還能節省更多的內存,保證不會超出空間限制。
這個思路的大致意思就是:忽略x的值,把第y列第一次輸入的數據當作第y列的第一個數據,而後是第二個……
下面來看代碼實現:
#include<cstdio> using namespace std; const int LP=100001; int n,m,k; int x[LP],y[LP],d[LP],c[LP];//記錄數據,記錄第n個數據在x行,y列,c是第y列的數據總數。 int *a[LP];//最多有LP列,因此咱們開LP長度的指針數組 int main() { scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=k;i++) { scanf("%d%d%d",&x[i],&y[i],&d[i]);//輸入x,y,d c[y[i]]++;//第y[i]列的數據個數++ } for(int i=1;i<=m;i++) a[i]=new int[c[i]];//爲每一列申請空間來存數據 for(int i=1;i<=k;i++) { *a[y[i]]=d[i];//收集數據到第y列中 a[y[i]]++;//第y列的指針指向下一個地址,準備下一次收集 } for(int i=1;i<=m;i++)//列優先 { a[i]-=c[i];//由於前面收集數據的時候每一列的指針都指向了該列的最後一個元素,因此要先減去該列的元素數,讓它指向第一個元素 for(int j=1;j<=c[i];j++,a[i]++)//從第1列開始輸出,j用來統計輸出到第i列第幾個元素,若是輸出到最後一個元素,跳出循環 printf("%d ",*a[i]);//指針每次+1,指向下一個元素並輸出它 } return 0; }
a[i]=new int c[[i]];這一句的意思是給a[i]這個指針新申請c[i]個空間,等同於咱們開了LP個一維的指針數組,這些數組每個都有一個專用的指針a[i],每一個數組有c[i]個元素。
到這裏,咱們已經講完了利用指針開動態數組數組的具體作法,這樣能夠頗有效率的優化你的程序,趕忙用起來吧!!!