int (*a)(int),重點介紹指針數組與數組指針- c/c++ - 台部落
文章推薦指數: 80 %
int a[3][4]; int (*p)[4]; p=a; //也可以爲p=&a[0];. 其中* ( *(p+i)+j)表示任意一個i行j列的元素。
多說一句,其實訪問 ...
請輸入正確的登錄賬號或密碼
註冊
忘記密碼
首頁
c/c++
正文
C/C++語言中的inta;int*a;int**a;int(*a)[];int(*a)(int),重點介紹指針數組與數組指針
原創
edward_zcl
2019-04-0909:57
這一部分往往很少被總結,但是卻經常用到,在使用的時候要注意區分。
先來一個基本的來入門吧!
參考自:
https://blog.csdn.net/qq_36744540/article/details/79832728
數組指針
首先,我們先介紹一下二維數組和二維數組的指針,二維數組相當於一個特殊的一維數組裏面每個元素又是一個一維數組,例如:inta[3][3],可以看成一個3行的一個列數組,每一列的元素又是一個長度爲3的數組,數組名的一個特殊一維數組的首地址,如下:
a(第0行的首地址,也是可以是&a[0])指向a[0],對0行首地址元素的引用就有a=a[0];
a+1(第一行的首地址)指向a[1],有(a+1)=a[1];
a+2(第二行的首地址)指向a[2],有*(a+2)=a[2];
a+i(第i行的首地址)指向一個[i]中,有*(a+1)=a[i]中。
而a[0],a[1],a[2]又是裏面一維數組的數組名,因此a[i]可表示i行的首地址:
a[0](第0行第0個元素的地址)指向一個[0][0],有*(a[0]+0)=a[0][0];
a[0]+1(第0行第1個元素的地址)指向a[0][1],有*(a[0]+1)=a[0][1];
a[0]+2(第0行第2個元素的地址)指向a[0][2],有*(a[0]+2)=a[0][2]
總結:a[i]+j(第i行第j列個元素的地址)指向a[i][j],有*(a[i]+j)=a[i][j],
即*(*(a+i)+j)=a[i][j]。
二維數組的指針變量
對於指向二維數組的指針變量p,可以有兩種:
一種是指向數組元素的列指針,一種是指向行的行指針,這時,p不是指向一個具體的數組元素,而是指向一個包含m個元素的一維數組。
這裏對行指針特殊說明一下;
行指針的定義形式爲:類型標識符(*指針變量名)[長度];
例如:inta[3][4],(*p)[4]=a;//這裏也可以寫爲inta[3][4],(*p)[4]=&a[0];
其中p,p+1,p+2和a,a+1,a+2都是第i行的首地址。
使用指針變量訪問二維數組的任意一個元素的方法
**(1)使用列指針:**定義一個列指針p,讓它指向二維數組的第0個元素
inta[3][4];
int*p;
p=&a[0][0];//因爲a[0]是第0行的數組名,所以p=&a[0][0]相當於p=a[0],因爲a[i][j]前面共有i*4+j個元素,該二維數組的任意i行j列元素可表示爲*(p+i*4+j)
(2)使用行指針:定義一個行指針p,讓它指向二維數組的第0行
inta[3][4];
int(*p)[4];
p=a;//也可以爲p=&a[0];
其中*(*(p+i)+j)表示任意一個i行j列的元素。
多說一句,其實訪問方式還可以很靈活的,跟直接使用數組名訪問一樣也可以,後面還會詳細討論。
參考自:https://www.cnblogs.com/zou107/p/4909847.html
一、關於二維數組和二維數組區別
(1)一維數組在內存中是連續分佈存儲的,同樣,二維數組也是在內存連續存儲的。
所以從內存的角度來分析,一維數組和二維數組其實沒有本質區別。
(2)二維數組可以使用一維數組來代替。
但是在實際的應用中,有時候使用二維數組會更加直觀,方便程序的編程。
(3)兩者在內存使用效率上是一模一樣的。
二、二維數組的第一維和第二維概念
(1)例如inta[2][5]中,前面的2表示第一維;後面的5表示第二維
(2)二維數組的第一維表示最外部的那一層,第一維本身也是一個數組,裏面存放了2個元素,這兩個元素就分別是第二維的數組。
第二維數組本身也是一個數組,裏面存放的元素是普通的int型變量。
三、二維數組的下標訪問
例1:
1inta[2][5]={{1,2,3,4,5},{6,7,8,9,10}};
2int(*p)[5];//定義一個數組指針
3p=a;
4
5printf("a[1][2]=%d.\n",a[1][2]);//a[1][2]=8
6printf("(*(p+1)+1)=%d.\n",*(*(p+1)+2));//a[1][2]
運行結果:
四、關於二維數據必須要明白的幾個符號
例2:理解a、&a、a[0]、&a[0]、a[0][0]、&a[0][0]
/*
二維數組的幾個符號的測試
1、a等同於&a[0]
2、a[0]等同於&a[0][0]
3、在數值上a、&a、a[0]、&a[0]、&a[0][0]是相等的,但是在類型上面是有區別的。
*/
inta[2][5]={{1,2,3,4,5},{6,7,8,9,10}};
printf("a=%p.\n",a);//a類型是int(*)[5]
printf("&a=%p.\n",&a);//&a類型是int(*)[2][5]
printf("a[0]=%p.\n",a[0]);//a[0]類型是int*
printf("&a[0]=%p.\n",&a[0]);//&a[0]類型是int(*)[5]
printf("a[0][0]=%d.\n",a[0][0]);//a[0][0]類型是int
printf("&a[0][0]=%p.\n",&a[0][0]);//&a[0][0]類型是int*
運行結果:
例3:第一維和第二維的數組指針的使用
//二維數組與指針的結合使用
inta[2][5]={{1,2,3,4,5},{6,7,8,9,10}};
int(*p1)[5];//數組指針
int*p2;//一般指針
p1=a;//等同p1=&a[0];//指向二維數組的數組名
p2=a[0];//等同p2=&a[0][0];//指向二維數組的第一維數組
printf("a[0][2]=%d.\n",*(*(p1+0)+2));//a[0][2]=3
printf("a[1][2]=%d.\n",*(*(p1+1)+2));//a[1][2]=8
printf("a[0][2]=%d.\n",*(p2+2));//a[0][2]=3
printf("a[0][4]=%d.\n",*(p2+4));//a[0][4]=5
運行結果:
五、總結
(1)需要理解二維數組的實質和幾個符號的含義。
(2)二維數組和數組指針兩者是有緊密的關係的。
學會使用數組指針來操作二維數組,重在實踐操作使用就會加深理解。
**注:學習筆記部分是在朱有鵬老師物聯網視頻教程中摘取,特此聲明。
**http://www.zhulaoshi.org/
a)inta;表示一個內存空間,這個空間用來存放一個整數(int);
b)int*a;表示一個內存空間,這個空間用來存放一個指針,這個指針指向一個存放整數的空間,即a)中提到的空間;
c)int**a;表示一個內存空間,這個空間用來存放一個指針,這個指針指向一個存放指針的空間,並且指向的這個空間中的指針,指向一個整數。
也簡單的說,指向了一個b)中提到的空間;
d)int(a)[4];表示一個內存空間,這個空間用來存放一個指針,這個指針指向一個長度爲4、類型爲int的數組;和int*a的區別在於,++、+=1之後的結果不一樣,其他用法基本相同。
以上四種類型見上圖表示。
e)int(*a)(int);表示一個內存空間,這個空間用來存放一個指針,這個指針指向一個函數,這個函數有一個類型爲int的參數,並且函數的返回類型也是int。
多說一句,都是指針,有時候可能值相同,但是意義卻不同,使用方式或者說數據格式也不同。
重點:
int*p[]和int(*p)[]
前者是指針數組,後者是指向數組的指針。
更詳細地說。
前:指針數組;是一個元素全爲指針的數組.
後:數組指針;可以直接理解是指針,只是這個指針類型不是int也不是char而是int[4]類型的數組.(可以結合函數指針一併看看…)
int*p[4]------p是一個指針數組,每一個指向一個int型的
int(*q)[4]---------q是一個指針,指向int[4]的數組。
定義涉及兩個運算符:“”(間接引用)、“[]”(下標),“[]”的優先級別大於“”的優先級別。
小括號或者說圓括號優先級最大。
首先看int*p[4],“[]”的優先級別高,所以它首先是個大小爲4的數組,即p[4];剩下的“int*”作爲補充說明,即說明該數組的每一個元素爲指向一個整型類型的指針。
int*p[4]的存儲結構如下:(存儲方格橫向排列或豎向排列沒區別,只要按內存地址順序排列就行,此處只是爲畫圖方便)
再看int(q)[4]。
它首先是個指針,即q,剩下的“int[4]”作爲補充說明,即說明指針q指向一個長度爲4的數組。
int(*q)[4]的存儲結構如下:
請看以下定義:
inta[2][4]={{2,5,6,8},{22,55,66,88}};
intc[4]={5,8,9,4};
intd[3]={23,12,443};
int*p[4],(*q)[4];
q=a;
*p=c;
*(p+1)=d;
則int*p[4]和int(*q)[4]的存儲數據爲:
驗證:
#include
也就是說執行p+1時,p要跨過n個整型數據的長度。
如要將二維數組賦給一指針,應這樣賦值:
inta[3][4];
int(*p)[4];//該語句是定義一個數組指針,指向含4個元素的一維數組。
p=a;//將該二維數組的首地址賦給p,也就是a[0]或&a[0][0]
p++;//該語句執行過後,也就是p=p+1;p跨過行a[0][]指向了行a[1][]
所以數組指針也稱指向一維數組的指針,亦稱行指針。
指針數組
定義intp[n];
[]優先級高,先與p結合成爲一個數組,再由int說明這是一個整型指針數組,它有n個指針類型的數組元素。
這裏執行p+1時,則p指向下一個數組元素,這樣賦值是錯誤的:p=a;因爲p是個不可知的表示,只存在p[0]、p[1]、p[2]…p[n-1],而且它們分別是指針變量可以用來存放變量地址。
但可以這樣p=a;這裏p表示指針數組第一個元素的值,a的首地址的值。
如要將二維數組賦給一指針數組:
int*p[3];
inta[3][4];
p++;//該語句表示p數組指向下一個數組元素。
注:此數組每一個元素都是一個指針
for(i=0;i<3;i++)
p[i]=a[i]
這裏int*p[3]表示一個一維數組內存放着三個指針變量,分別是p[0]、p[1]、p[2]
所以要分別賦值。
這樣兩者的區別就豁然開朗了,數組指針只是一個指針變量,似乎是C語言裏專門用來指向二維數組的,它佔有內存中一個指針的存儲空間。
指針數組是多個指針變量,以數組形式存在內存當中,佔有多個指針的存儲空間。
還需要說明的一點就是,同時用來指向二維數組時,其引用和用數組名引用都是一樣的。
比如要表示數組中i行j列一個元素:
*(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j]
優先級:()>[]>*
出處:http://www.cnblogs.com/hongcha717/archive/2010/10/24/1859780.html
=========================================================================
一、指針數組和數組指針的內存佈局
初學者總是分不出指針數組與數組指針的區別。
其實很好理解:
指針數組:首先它是一個數組,數組的元素都是指針,數組佔多少個字節由數組本身的大小決定,每一個元素都是一個指針,在32位系統下任何類型的指針永遠是佔4個字節。
它是“儲存指針的數組”的簡稱。
數組指針:首先它是一個指針,它指向一個數組。
在32位系統下任何類型的指針永遠是佔4個字節,至於它指向的數組佔多少字節,不知道,具體要看數組大小。
它是“指向數組的指針”的簡稱。
下面到底哪個是數組指針,哪個是指針數組呢:
A)
int*p1[10];
B)
int(*p2)[10];
每次上課問這個問題,總有弄不清楚的。
這裏需要明白一個符號之間的優先級問題。
“[]”的優先級比“*”要高。
p1先與“[]”結合,構成一個數組的定義,數組名爲p1,int修飾的是數組的內容,即數組的每個元素。
那現在我們清楚,這是一個數組,其包含10個指向int類型數據的指針,即指針數組。
至於p2就更好理解了,在這裏“()”的優先級比“[]”高,“”號和p2構成一個指針的定義,指針變量名爲p2,int修飾的是數組的內容,即數組的每個元素。
數組在這裏並沒有名字,是個匿名數組。
那現在我們清楚p2是一個指針,它指向一個包含10個int類型數據的數組,即數組指針。
我們可以藉助下面的圖加深理解:
二、int(*)[10]p2-----也許應該這麼定義數組指針
這裏有個有意思的話題值得探討一下:平時我們定義指針不都是在數據類型後面加上指針變量名麼?這個指針p2的定義怎麼不是按照這個語法來定義的呢?也許我們應該這樣來定義
p2:
int(*)[10]p2;
int(*)[10]是指針類型,p2是指針變量。
這樣看起來的確不錯,不過就是樣子有些彆扭。
其實數組指針的原型確實就是這樣子的,只不過爲了方便與好看把指針變量p2前移了而已。
你私下完全可以這麼理解這點。
雖然編譯器不這麼想。
_
三、再論a和&a之間的區別
既然這樣,那問題就來了。
前面我們講過a和&a之間的區別,現在再來看看下面的代碼:
intmain()
{
chara[5]={'A','B','C','D'};
char(*p3)[5]=&a;
char(*p4)[5]=a;
return0;
}
上面對p3和p4的使用,哪個正確呢?p3+1的值會是什麼?p4+1的值又會是什麼?毫無疑問,p3和p4都是數組指針,指向的是整個數組。
&a是整個數組的首地址,a是數組首元素的首地址,其值相同但意義不同。
在C語言裏,賦值符號“=”號兩邊的數據類型必須是相同的,如果不同需要顯示或隱式的類型轉換。
p3這個定義的“=”號兩邊的數據類型完全一致,而p4這個定義的“=”號兩邊的數據類型就不一致了。
左邊的類型是指向整個數組的指針,右邊的數據類型是指向單個字符的指針。
在VisualC++6.0上給出如下警告:
warningC4047:‘initializing’:‘char(*)[5]’differsinlevelsofindirectionfrom‘char*’。
還好,這裏雖然給出了警告,但由於&a和a的值一樣,而變量作爲右值時編譯器只是取變量的值,所以運行並沒有什麼問題。
不過我仍然警告你別這麼用。
既然現在清楚了p3和p4都是指向整個數組的,那p3+1和p4+1的值就很好理解了。
但是如果修改一下代碼,把數組大小改小點,會有什麼問題?p3+1和p4+1的值又是多少呢?
intmain()
{
chara[5]={'A','B','C','D'};
char(*p3)[3]=&a;
char(*p4)[3]=a;
return0;
}
甚至還可以把代碼再修改,把數組大小改大點:
intmain()
{
chara[5]={'A','B','C','D'};
char(*p3)[10]=&a;
char(*p4)[10]=a;
return0;
}
這個時候又會有什麼樣的問題?p3+1和p4+1的值又是多少?
上述幾個問題,希望讀者能仔細考慮考慮,並且上機測試看看結果。
測試結果:
(1).char(p2)[5]=a;必須使用強制轉換,如:char(p2)[5]=(char()[5])a;
(2).把數組大小改變,都會編譯不通過,提示:
errorC2440:‘initializing’:cannotconvertfrom'char()[5]’to‘char()[3]’
errorC2440:‘initializing’:cannotconvertfrom'char()[5]’to'char(*)[10]'
(3).把以上程序測試代碼如下:
intmain()
{
chara[5]={'a','b','c','d'};
char(*p1)[5]=&a;
char(*p2)[5]=(char(*)[5])a;
printf("a=%d\n",a);
printf("a=%c\n",a[0]);
printf("p1=%c\n",**p1);
printf("p2=%c\n",**p2);
printf("p1+1=%c\n",**(p1+1));
printf("p2+1=%c\n",**(p2+1));
return0;
}
輸出:
a=1638208
a=a
p1=a
p2=a
p1+1=?
p2+1=?
Pressanykeytocontinue
結論:
根據指針類型及所指對象,表示指針大小,每次加1,表示增加指針類型大小的字節.----後面還會有解釋說明.
四、地址的強制轉換
先看下面這個例子:
structTest
{
intNum;
char*pcName;
shortsDate;
charcha[2];
shortsBa[4];
}*p;
假設p的值爲0x100000。
如下表表達式的值分別爲多少?
p+0x1=0x___?
(unsignedlong)p+0x1=0x___?
(unsignedint*)p+0x1=0x___?
我相信會有很多人一開始沒看明白這個問題是什麼意思。
其實我們再仔細看看,這個知識點似曾相識。
一個指針變量與一個整數相加減,到底該怎麼解析呢?
還記得前面我們的表達式“a+1”與“&a+1”之間的區別嗎?其實這裏也一樣。
指針變量與一個整數相加減並不是用指針變量裏的地址直接加減這個整數。
這個整數的單位不是byte而是元素的個數。
所以:p+0x1的值爲0x100000+sizof(Test)*0x1。
至於此結構體的大小爲20byte,前面的章節已經詳細講解過。
所以p+0x1的值爲:0x100014。
(unsignedlong)p+0x1的值呢?這裏涉及到強制轉換,將指針變量p保存的值強制轉換成無符號的長整型數。
任何數值一旦被強制轉換,其類型就改變了。
所以這個表達式其實就是一個無符號的長整型數加上另一個整數。
所以其值爲:0x100001。
(unsignedint*)p+0x1的值呢?這裏的p被強制轉換成一個指向無符號整型的指針。
所以其值爲:0x100000+sizof(unsignedint)*0x1,等於0x100004。
上面這個問題似乎還沒啥技術含量,下面就來個有技術含量的:在x86系統下,其值爲多少?
intmain()
{
inta[4]={1,2,3,4};
int*ptr1=(int*)(&a+1);//指向a數組後面的內存單元,&a+1表示向後移16個存儲單元
int*ptr2=(int*)((int)a+1);//表示a的存儲單元的地址增加一個字節
printf("%x,%x",ptr1[-1],*ptr2);//ptr1[-1]其實指向的是a數組的最後一個單元,*ptr1則表示a數組的地址後移一個字節之後的4個連續存儲單元所存儲的值
return0;
}
這是我講課時一個學生問我的題,他在網上看到的,據說難倒了n個人。
我看題之後告訴他,這些人肯定不懂彙編,一個懂彙編的人,這種題實在是小case。
下面就來分析分析這個問題:
根據上面的講解,&a+1與a+1的區別已經清楚。
ptr1:將&a+1的值強制轉換成int類型,賦值給int類型的變量ptr,ptr1肯定指到數組a的下一個int類型數據了。
ptr1[-1]被解析成*(ptr1-1),即ptr1往後退4個byte。
所以其值爲0x4。
ptr2:按照上面的講解,(int)a+1的值是元素a[0]的第二個字節的地址。
然後把這個地址強制轉換成int類型的值賦給ptr2,也就是說ptr2的值應該爲元素a[0]的第二個字節開始的連續4個byte的內容。
其內存佈局如下圖:
好,問題就來了,這連續4個byte裏到底存了什麼東西呢?也就是說元素a[0],a[1]裏面的值到底怎麼存儲的。
這就涉及到系統的大小端模式了,如果懂彙編的話,這根本就不是問題。
既然不知道當前系統是什麼模式,那就得想辦法測試。
大小端模式與測試的方法在第一章講解union關鍵字時已經詳細討論過了,請翻到彼處參看,這裏就不再詳述。
我們可以用下面這個函數來測試當前系統的模式。
intcheckSystem()
{
unioncheck
{
inti;
charch;
}c;
c.i=1;
return(c.ch==1);//如果當前系統爲大端模式這個函數返回0;如果爲小端模式,函數返回1。
}
如果當前系統爲大端模式這個函數返回0;如果爲小端模式,函數返回1。
也就是說如果此函數的返回值爲1的話,*ptr2的值爲0x2000000。
如果此函數的返回值爲0的話,*ptr2的值爲0x100。
出處:http://see.xidian.edu.cn/cpp/html/476.html
更多內容,可以參考:
https://blog.csdn.net/wtzdedaima/article/details/78300169
https://blog.csdn.net/qq_33573235/article/details/79530792
這篇博客可以說非常全面了。
。
https://www.cnblogs.com/haore147/p/3647262.html
發表評論
登录
所有評論
還沒有人評論,想成為第一個評論的人麼?請在上方評論欄輸入並且點擊發布.
相關文章
CMake::CMakeLists.txt基本語法及常用
CMakeLists.txt基本語法及常用
常用變量:
變量名
含義
PROJECT_NAME
project命令中寫的項目名
CMAKE_VERSION
當前使用CMake的版本
CMAKE_SOURCE_DIR
工程頂層目錄,即入口C
dieju8330
2020-07-0810:35:13
while(cin)的理解
Jery最早的意圖是支持一個iostreamclassobject的純量測試,像這樣:
if(cin)...
爲了讓cin能夠求得一個真假值,Jerry首先爲它定義一個conversion運算符,即operatorint()。
xudacheng06
2020-07-0809:57:59
Operatornew函數
operatornew
void*operatornew(std::size_tsize)throw(std::bad_alloc);
void*operatornew(std::size_tsize,cons
xudacheng06
2020-07-0809:57:59
C++中指針和引用的區別(轉)
http://www.cnblogs.com/dolphin0520/
指針和引用在C++中很常用,但是對於它們之間的區別很多初學者都不是太熟悉,下面來談談他們2者之間的區別和用法。
1.指針和引用的定義和性質區別:
(1)指針:指針
沉沦风帆
2020-07-0809:57:11
const和staticconst還有static區別
對於C/C++語言來講,
const就是隻讀的意思,只在聲明中使用;
static一般有2個作用,規定作用域和存儲方式.對於局部變量,static規
沉沦风帆
2020-07-0809:57:11
使用std::function和std::bind實現函數回調
文章目錄std::functionstd::bindcode
std::function
作爲c++11新增的內容,std::function的實例可以對任何可調用對象實體進行存儲、複製、和調用。
其實就是一個對所有可調用對象的封
Ango_
2020-07-0809:39:18
函數取地址與複製構造函數的使用
今天在CSDN的論壇上逛一了圈發現有如下兩段代碼:我在兩個提問的地方都已經給予了作答,同時發佈到blog上希望能有更多的人受益。
第一段是對複製構造函數的使用
classc
{
stringname;
public:
c(const
wenrenhua08
2020-07-0809:06:32
C++中的explicit構造函數
C++explicit構造函數
explicit按字面意思是明確的,顯式的。
在C++類中只要定義的構造函數編譯器就不會幫你生成默認構造函數。
wenrenhua08
2020-07-0809:06:32
C++中的const關鍵字
爲什麼使用const?採用符號常量寫出的代碼更容易維護;指針常常是邊讀邊移動,而不是邊寫邊移動;許多函數參數是隻讀不寫的。
c
wenrenhua08
2020-07-0809:06:21
鏈表的建立插入與刪除
//file鏈表的建立插入與刪除.cpp
#include"iostream.h"#include"string.h"#include"stdlib.h"
structlistnode{//鏈表結點的類型聲明 intnum; ch
陆家三少
2020-07-0808:35:09
文件的讀與寫操作
//文件的讀與寫操作.cpp
#include
延伸文章資訊
- 1C語言之int *f()、int(*f)()、int *a[]、int (*a)[] 區別小記 - IT人
int *a[] 是一個指標陣列,表示陣列中的每一個元素都是一個指標,可以指向一個int型別的數值。 int (*a)[] 這是一個指向多維陣列的指標變數。 例如: int ( ...
- 2What is a diference between ((int) a) and (int(a))?
c++ - int a = 0 and int a(0) differences - Stack Overflow
- 3C語言int a是什麼意思? 20 - 多學網
C語言inta是什麼意思,C語言int a是什麼意思? 20,1樓天空沒蜻int a指的是定義一個指抄向int型別數襲據的指標a,int a指的是定bai義一個整數變數dua ...
- 4C 中int a[] 和int*a 有什么区别? - 知乎
- 5整數的數字型別- C# 參考
int a = 123; System.Int32 b = 123;. 資料表 nint 最後兩個數據列中的和 nuint 類型是原生大小的整數。 從C# 9.0 開始,您可以使用 nint 和...