指針數組的使用

指針數組的使用

來自專欄 Sun Flowers C World

指針變數可以用來存儲一個內存地址,數組可以用來存儲一組相同類型的數據,那麼,如果我們需要存儲多個相同類型的變數的地址,應該用什麼呢?答案就是指針數組。

顧名思義,指針數組就是每個數據元素都是一個指針的數組,每個數據元素中存儲一個指針,他們的基類型必須相同。比如說,一個int *p可以指向一個整數(如int a=5;p=&a;),也可以指向一個一維整型數組中的元素(如 int a[5]={1,2,3,4,5}; p=a+1;),那麼由n個int *元素組成的數組int *a[5]就可以指向五個整型變數或者一維整型數組的內存空間,實現類似一個五行若干列的二維整型數組的數據存儲,但是每行的元素個數需要存儲在一個數組中以便於對數組的正確訪問。當類型為char *[]的字元型指針數組在這種應用情景中就比較簡單了,由於字元串自帶字元表示字元串的結束,因此一個一維字元指針數組可以實現二維字元數組的數據存儲。

利用指針數組可以有兩個目的:(1)可以實現各維度上長度可變的類似多維數組的數據存儲,靈活多變,利用數據的實際需求來申請內存;(2)數據共享,防止數據的多個副本。

一、類多維數組的數據存儲

假設我們需要存儲某學校計算機專業4個班級同學的年齡,假設1-4班同學人數分別為4,6,3,2,當然我們可以用二維數組來進行實現,但是此時就需要用4個班級同學人數的最大值來定義二維數組的行數,會造成內存浪費。此時,我們可以為每一個班級根據其學生人數定義一個一維數組,然後將這四個班級對應數組的地址存入一個指針數組中,就可以實現這種需求了。 如下圖示意圖所示,我們定義了一個一維整型指針數組a,那麼可以用a[0]元素存儲一個由四個元素組成的一維整型數組的首地址,用a[1]元素存儲一個有6個元素組成的數組的首地址,用a[2]元素存儲一個有3個元素組成的數組的首地址,用a[3]元素存儲一個有2個元素組成的數組的首地址,這樣一來可以用一個一維的指針數組實現類似二維數組的數據存儲,但是這個二維數組要更靈活,每行的元素個數可變的情形。

這種使用方式更多的應用於多個字元串數據的存儲,在存儲多個長度不一的字元串時,可以先定義一個臨時字元數組來接收用戶的輸入,然後根據輸入字元串的長度申請動態內存以存儲數據,新申請內存的地址存入一個字元指針數組中,最後把臨時字元數組中的字元串拷貝至申請的內存中。這樣,就可以用一個一維字元指針數組結合動態申請內存完成多個字元串信息的存儲。

也就是說,利用一維指針數組可以實現類似二維數組的數據存儲,同樣的,利用一維的指針的指針(**)數組可以實現類似三維數組的數據存儲,利用一維的多重指針數組可以實現類似多維數據的更靈活的數據存儲。

為了分析方便,假設上面1-4班我們用0-3班來表示,各班級同學也用0-n-1這樣的類似數組下標的表示方法。那麼, 在上面的數組a中,a的值為a[0]元素的首地址(2000),a[0]元素的值(*(a+0))為0班同學年齡數組的首地址(3000), 那麼*(a[0])為0班第0名同學的年齡(18),注意,由於a[0]可以表示為(*(a+0),那麼*(a[0])也可以表示為:*(*(a+0)+0),是不是也可以表示為a[0][0]呢?? 答案是肯定的!!!可以!!! 第1名同學年齡的地址為a[0]+1(即3004),其年齡(19)可以用*(a[0]+1)來進行訪問,也可以用a[0][1]來訪問。類似的,1班第0名同學的年齡(17)可以用*(a[1]+0)或者a]1][0]和*(*(a+1)+0)來進行訪問。推廣開來,i班級第j名同學的數據就可以用a[i][j]來進行訪問,也就是說訪問各元素可以用與二維數組中一樣的方式來完成。但是,列標的範圍對於每行來說可能都不一樣,使用的時候要注意。

其代碼實現有兩種方式:

(1)可以全部用數組實現:

int num[4]={4,6,3,2}; int * a[4],a1[4],a2[6],a3[3],a4[2]; int i,j; a[0]=a1; a[1]=a2; a[2]=a3; a[3]=a4;2)各班級同學的數據也可以用動態分配內存來進行存儲: int num[4]={4,6,3,2}; int * a[4]; int i; for(i=0;i<4;i++) { a[i]=(int *)malloc(num[i]*sizeof(int)); if(a[i]==NULL) exit(1); }

(3)具體實現:

以下以動態內存實現該數據的輸入與輸出。

#include <stdio.h>#include <stdlib.h>#include <string.h>int main(){ int num[4]= {4,6,3,2}; int * a[4]; int i,j; for(i=0; i<4; i++) { a[i]=(int *)malloc(num[i]*sizeof(int)); if(a[i]==NULL) exit(1); } for(i=0; i<4; i++) { printf("請輸入%d班同學的年齡:
",i+1); for(j=0; j<num[i]; j++) { scanf("%d",&a[i][j]); } } for(i=0; i<4; i++) { for(j=0; j<num[i]; j++) { printf("%d ",a[i][j]); } printf("
"); } for(i=0; i<4; i++) for(j=0; j<num[i]; j++) { free(a[i]); a[i]=NULL; } return 0;}

二、數據共享

假如需要接收n名(0<n<100)同學的姓名,每名同學的姓名長度不超過30,先對其按照ASCII值從小到大的順序進行排序後輸出,然後按照輸入順序進行輸出。

簡單的分析一下,n名同學的姓名可以用一個二維字元數據來進行存儲,如果不需要保留輸入順序,那麼就可以直接對二維字元數組中的n個字元串進行排序即可實現。但是,本題目中要求保留輸入順序,如果直接對二維字元數組進行排序,將丟失輸入順序。此時有兩個方案可以實現這一功能,一是將n個同學的姓名備份至另一個二維數組中進行排序輸出;二是另外定義一個一維字元指針數組,利用其中的n個指針指向n個同學的姓名,然後根據其指向的字元串的ASCII值的大小,對指針數組進行排序,輸出時先根據指針數組輸出排序後的值,然後根據存放學生姓名的二維數組進行按照輸入順序的輸出。相比較而言,第二種方案不僅佔用的內存少,還由於在進行排序時僅僅交換字元指針數組中的元素的值,避免了字元串拷貝,因此運行效率較高。以下用分別用兩種方案來進行實現。

1.數據備份方式實現:

(1)不適用函數的方式實現:

#include <stdio.h>#include <stdlib.h>#include <string.h>int main(){ char a[100][30],b[100][30],tmp[30]; int n,i,j; scanf("%d",&n); for(i=0;i<n;i++) { scanf("%s",a[i]); strcpy(b[i],a[i]); } for(i=0;i<n-1;i++) { for(j=0;j<n-i-1;j++) { if(strcmp(b[j],b[j+1])>0) { strcpy(tmp,b[j]); strcpy(b[j],b[j+1]); strcpy(b[j+1],tmp); } } } printf("排序後的順序為:
"); for(i=0;i<n;i++) { printf("%s%c",b[i],(i==n-1)?
: ); } printf("輸入的順序為:
"); for(i=0;i<n;i++) { printf("%s%c",a[i],(i==n-1)?
: ); } return 0;}

(2)使用函數對以上代碼進行優化:

#include <stdio.h>#include <stdlib.h>#include <string.h>void input(char a[][30],int n){ int i; for(i=0;i<n;i++) scanf("%s",a[i]);}void arraycopy(char to[][30],char from[][30],int n){ int i; for(i=0;i<n;i++) strcpy(b[i],a[i])}void sort(char a[][30],int n){ int i,j; char tmp[30]; for(i=0;i<n-1;i++) { for(j=0;j<n-i-1;j++) { if(strcmp(b[j],b[j+1])>0) { strcpy(tmp,b[j]); strcpy(b[j],b[j+1]); strcpy(b[j+1],tmp); } } }}void output(char a[][30],int n){ int i; for(i=0;i<n;i++) { printf("%s%c",a[i],(i==n-1)?
: ); }}int main(){ char a[100][30],b[100][30]; int n,i,j; scanf("%d",&n); input(a,n); arraycopy(b,a,n); sort(b,n); printf("排序後的順序為:
"); output(b,n); printf("輸入的順序為:
"); output(a,n); return 0;}

2.指針數組實現

(1)不使用函數

#include <stdio.h>#include <stdlib.h>#include <string.h>int main(){ char a[100][30],*b[30],*tmp; int n,i,j; scanf("%d",&n); for(i=0; i<n; i++) scanf("%s",a[i]); for(i=0; i<n; i++) b[i]=a[i]; for(i=0; i<n-1; i++) { for(j=0; j<n-i-1; j++) { if(strcmp(b[j],b[j+1])>0) { tmp=b[j]; b[j]=b[j+1]; b[j+1]=tmp; } } } printf("排序後的順序為:
"); for(i=0;i<n;i++) { printf("%s%c",b[i],(i==n-1)?
: ); } printf("輸入的順序為:
"); for(i=0;i<n;i++) { printf("%s%c",a[i],(i==n-1)?
: ); } return 0;}

(2)使用函數對以上代碼進行優化

#include <stdio.h>#include <stdlib.h>#include <string.h>void input(char a[][30],int n){ int i; for(i=0;i<n;i++) scanf("%s",a[i]);}void chararray(char *to[],char from[][30],int n){ int i; for(i=0;i<n;i++) to[i]=from[i];}void sort(char *b[],int n){ int i,j; char *tmp; for(i=0;i<n-1;i++) { for(j=0;j<n-i-1;j++) { if(strcmp(b[j],b[j+1])>0) { tmp=b[j]; b[j]=b[j+1]; b[j+1]=tmp; } } }}void output(char a[][30],int n){ int i; for(i=0;i<n;i++) { printf("%s%c",a[i],(i==n-1)?
: ); }}void output2(char *a[],int n){ int i; for(i=0;i<n;i++) { printf("%s%c",a[i],(i==n-1)?
: ); }}int main(){ char a[100][30],*b[30],*tmp; int n,i,j; scanf("%d",&n); input(a,n); chararray(b,a,n); sort(b,n); printf("排序後的順序為:
"); output2(b,n); printf("輸入的順序為:
"); output(a,n); return 0;}

推薦閱讀:

進口車的質量和其出口到其他國家或者在本國銷售的質量有差別嗎?
三個碎片側觀:中國到底為什麼缺乏科技創新
那是一隻望穿深空的天眼
微軟計劃向物聯網投資50億美元
如果讓你進行無人機巡線中的任務管理,你怎麼做?

TAG:編程 | 科技 | 編程語言 |