原創:C/C++語言-指針(point)


因為我覺得學習C語言最重要的知識點之一就是指針,可是無論對於新手還是有一定經驗的人來說,指針的理解還是不夠系統的,於是結合我個人見解寫出了這么一份代碼形式的筆記,讀者可以自行探究加深理解和記憶。

此篇文章經過幾年很多次的修改,個人覺得足夠完善了,如有疑問歡迎聯系作者本人一起探討學習~,尊重作者的勞動,轉載請記得注明來源:http://www.cnblogs.com/weifeng727/p/5584151.html

 

  1 /*----------------------------------------
  2                 指針練習(精華)
  3 
  4     1)首先,要理解變量或數組的首地址,指的就是存放數據的RAM或ROM中地址號最小的那個字節地址。
  5 
  6     2)指針前面的類型說明符,具有2重意義(既決定了對一級指針進行解引用時,將會操作的字節數,以及對一級指針進行算術運算時,會跳轉的地址個數)。    
  7       ①決定了指針加一時,指針會跳轉多少個地址,
  8       例如:
  9               如果指針是
 10               char類型的,(指針+n) 指針會跳轉n*1個地址。
 11               int 類型的,(指針+n) 指針會跳轉n*2個地址。
 12               long類型的,(指針+n) 指針會跳轉n*4個地址。
 13               
 14       ②並且還決定了通過指針操作地址值時,實際上會返回多少個字節的值,且地址號大的字節先返回。 
 15       例如:    
 16               假設要操作指針的一次地址返回值,那么如果指針是
 17               char類型的,返回1個字節。
 18               int 類型的,返回2個字節。
 19               long類型的, 返回4個字節。
 20               
 21       數組前面的類型說明符,同樣具有2重意義,且跟上面的很相似。
 22       例如:
 23               #include"stdio.h"
 24               int c[]={0x1234,0x5678};
 25               void main()
 26               {
 27                 printf("%p %d\n",c,*c);            //數組是int類型意味着返回2個字節
 28                 printf("%p %d\n",(c+1),*(c+1));  //實際上(c+1)與c是夾着一個地址,因為數組類型符號是int,如果數組類型是long,則夾着3地址
 29               }
 30       
 31       也就是要注意類型所占的字節數,還有就是什么時候該看數組類型符號或者指針類型符號。
 32     3)&叫取首地址符號,*叫解引用符號。
 33 
 34     4)數組名是指一個首地址,所以,point=a(point是一個指針,a是一個數組名), a的前面不需要加&符號。
 35       變量名指的是一個值,a[1]指的也是一個值,這些值包含着一個或多個字節,在想要讓指針指向這些值的字節的地址時,
 36       需要在變量名以及a的前面加上&符號,即意思是要讓指針賦值符號(=)右邊的東西是地址。
 37 
 38     5)數組或變量的數據是一個一個字節的存放的,而且字節的地址是呈現連續的,賦值的時候,從左到右看
 39       越往右,字節的地址號越大。因此,對於多字節數據類型的數組而言,看起來有種“首尾相連”的效果,
 40       因為一個元素的最低位字節其地址的加一地址對應的字節,就是下一個元素的最高位字節。
 41       
 42       簡單點來說就是低地址存放高字節,這種現象稱為大端排列(常用單片機)。注意:有些時候則是低地址存放低字節,這種現象稱為小端排列(ARM)。
 43       
 44     6)指針可分為:函數指針,數組指針(多維指針),變量指針,結構體指針。  又可分為:多級指針,多維指針。  地址可分為:多級地址,多維地址。
 45 
 46     7)只有字符數組的最后一個元素會緊接一個隱藏元素,該元素值為0,映射的字符為“\0”。
 47 
 48     8)數據指針型函數,也叫指針函數(即返回值是一個地址)。   
 49     
 50     9)char (*p)[2];   //聲明一個1維指針(也叫1維數組指針)
 51       分析方括號([])對多維指針的操作時,要遵循一個原則:先明確指針的維數,再分析有多少組方括號,方括號里面的數字是多少,由此得到地址是如何跳轉的;
 52                                                         然后根據方括號的組數得知地址最終會發生多少次的解引用,如果解引用的次數少於地址的維數,
 53                                                         那么最終得到的還是一個地址,也如果解引用的次數等於地址的維數+1,那么得到是一個數據值。
 54                                                         每次對多維地址進行一次解引用后,地址的維數將會變小。
 55       一維數組名就是一個一級0維地址,二維數組名就是一個一級1維地址,多維數組名就是一個一級多維地址。每一個數組名都是一個地址。這些地址是固定的。
 56       一級多維指針的特點是:解引用的寫法很特殊;運算時地址的跳轉很特殊。
 57       探究代碼如下:
 58       int Array[2][3][2]={{{1,2},{3,4},{5,6}},{{7,8},{9,10},{11,12}}};  //Array是一個一級2維地址
 59 
 60       printf("%d\n", Array); 
 61       printf("%d\n", Array[1]);   //與上一行代碼相比,發生了 3*2*4=24個地址 的跳轉
 62       printf("%d\n", Array[1][1]);    //與上一行代碼相比,發生了 2*4=8個地址 的跳轉
 63 
 64       printf("%d\n",*(Array[1][1]));//對0維地址進行1次解引用,得到一個數據值,為9
 65 
 66       printf("%d\n",*(*(Array[1])));    //對1維地址進行2次解引用,得到一個數據值,為7
 67       printf("%d\n",*(Array[1]));        //對1維地址進行1次解引用,得到的是一個0維地址,且與Array[1]值一樣,但Array[1]是一個1維地址
 68 
 69     10) #include<stdio.h>  
 70         #include<stdlib.h>  
 71         int main()  
 72         {  
 73 
 74             //下面是存在着非法讀寫的演示,雖然非法讀寫是可以實現,但是這只能存在於同一個進程里面,而且這種情況沒有什么有利的實際意義
 75 
 76             int *p;    //int類型指針,操作4個字節
 77             p=(int *)malloc(2);  //向堆申請了2個字節,但不進行初始化,calloc方式的申請會進行初始化
 78             if(p)  
 79                 printf("Memory Allocated at: %p\n",p);  
 80             else  
 81                 printf("Not Enough Memory!\n");
 82             printf("%x\n",*p);     //由於只申請了2個字節,所以非法讀取了2個字節
 83             *p=0x12345678;        //由於只申請了2個字節,所以非法寫入了2個字節
 84             printf("%x\n",*p);    //由於只申請了2個字節,所以非法讀取了2個字節     
 85             free(p);   //釋放堆中申請過的2個字節,並且有可能把內存中的值也清0,這要取決於運行的內核
 86             //下面是非法讀寫了4個字節
 87             printf("%x\n",*p);    
 88             *p=0x87654321;
 89             printf("%x\n",*p);    
 90             return 0;  
 91         } 
 92 
 93     11)經探究發現,不同類型的指針指向的地址跟指針類型不一致時,有可能會報錯,也有可能只是警告而已
 94 
 95     12)unsigned char (*q)();  //聲明一個函數指針
 96        指針形式調用函數時不給定傳遞參數值的話,默認是傳遞-1   ,  指針調用函數的格式為:"(*指針名)(形參列表)" 或 "指針名(形參列表)"
 97 
 98     13)一級和多級指針的使用:
 99     int val=0x1122;
100     char *p3=&val;
101     char **p2=&p3;
102 
103     printf("%x\n", p3); 
104     printf("%x\n", p3+1); //跳轉1個地址(因為p3是個一級指針而且類型修飾符為char)
105 
106     printf("%x\n", *(p3)); //操作1個字節(因為p3是個一級指針而且類型修飾符為char)
107     printf("%x\n", *(p3+1));//操作1個字節(因為p3是個一級指針而且類型修飾符為char)
108 
109     printf("%x\n", (p2)); 
110     printf("%x\n", (p2+1)); //跳轉4個地址(因為內存中字節所使用的地址長度為32位且指針p2是一個二級指針)
111 
112     printf("%x\n", *(p2)); //操作4個字節(因為內存中字節所使用的地址長度為32位且指針p2是一個二級指針)
113 
114     14)對多級多維指針的探究:
115     //假設已經掌握對多級0維指針,和一級多維指針的使用
116 
117     unsigned char (**p)[3];  //這是一個二級2維指針
118     int Array3[10]={0x804a0e0,0x55667788,2,3,4,5,6,7,8,9};  //之所以第一個元素設為0x804a0e0,是因為如果該值取的不當,下面對(*(*p))進行解引用的時候,有可能在程序執行時導致內核塌陷,看起來好像是程序語法錯誤,也就是說要保證解引用的指針是在正確范圍內的
119     p=Array3;  //Array3是一個一級1維地址,先讓Array3轉變為二級2維地址,再讓二級2維指針p所指向
120 
121     printf("%x\n", p);  //原本p是一個二級2維指針,在這里p表示為一個二級2維地址
122     printf("%x\n", p+1);  //發生4個地址的跳轉(因為地址長度為4個字節),因為(p+1)是一個二級2維地址,也就是此行語句輸出值比上一行語句大4
123 
124     printf("%x\n", *p);   //解引用得到4個字節的值,因為p是一個二級2維地址。*p 得到的是一個一級2維地址
125     printf("%x\n", (*p+1));  //跳轉3個字節,因為 *p 屬於一級2維地址,也就是此行語句輸出值比上一行語句大3。因為一級指針地址的跳轉,是取決於維數,*p是一個一級2維地址,那么跳轉數為:sizeof(unsigned char)*[3]=3
126     
127     printf("%x\n", *(*p));  //對一級2維地址(*p)進行解引用,得到一級1維地址*(*p)
128 
129     printf("%d\n", *(*(*p))); //對一級1維地址*(*p)進行解引用,得到1個字節的值,值為0xe0也就是224,因為指針 p 的類型修飾符為char,而sizeof(unsigned char)=1
130 
131     printf("%d\n", *(*(*p))+1); //該行打印值比上一行大1,為225
132 
133     //總結:對多級多維指針進行解引用的時,每次解引用都會遵循先降級再降維,當級數沒降到1,那么維數不起作用。
134     //      當已經是一級指針了后,維數起作用,當做一級多維指針或一級1維指針處理  ,一級1維地址再解引用就得到值了
135     
136     15)指針常量和常量指針
137     int a =3;  
138     int b = 1;  
139     int c = 2;  
140     int const *p1 = &b;//const 在前,定義為常量指針  
141     int *const p2 = &c;//*在前,定義為指針常量
142     //常量指針p1:指向的地址可以變,但內容不可以重新賦值,內容的改變只能通過修改地址指向后變換。   
143     //p1 = &a是正確的,但 *p1 = a是錯誤的。
144     //指針常量p2:指向的地址不可以重新賦值,但內容可以改變,必須初始化,地址跟隨一生。
145     //p2= &a是錯誤的,而*p2 = a 是正確的。
146 
147     16)假設a是數組名,p是指針名,那么p=&a等效於p=a
148     int abc[4]={1,2,3,4};
149     int (*p)[4]; 
150     p=abc;  //等效於p=&abc;
151     for(i=0;i<4;++i)
152     {
153         printf("%d\n",p[0][i]);
154     }
155 
156     17)在sizeof()函數參數列表里,解引用符*的作用稍微發生了變化。
157     //下面證明了,解引用符號“ * ”在不同的場合應用,其作用發生了改變,如在sizeof()函數的參數中時,對數組名進行解引用,將會影響打印出來的數組“維積”
158     printf("%d\n", sizeof((**pp)));    //打印出數組“維積”,為4
159     printf("%d\n", sizeof(*p));  //打印數組的“維積”,為4*sizeof(int)=16
160     printf("%d\n", sizeof(p));  //打印出指針“維積”,為3*4*4=48
161     printf("%d\n", sizeof(*(**p[3][4])));  //打印出指針“維積”,為5*4=20
162 
163     18)關於指針和數組的一種特殊數據結構
164     int (**p[3][4])[2][5];  //定義一個二級3維指針2維數組
165     int pp[5][6];
166     int test;
167     //p=&test;   //因為p是數組名,是一個常量,所以p=&test;這句有錯誤
168 
169     18)int array[10];
170        double *a=&array[5];
171        double *b=array;
172        printf("a-b=%d\n",a-b);  //打印結果為:2。   4*5/8=2    此行代碼是這樣理解的: a-b得到一個double類型的地址值,然后轉換成int類型,那么有20/8=2
173     
174 ------------------------------------------*/
175 #include"stdio.h"
176 #include"stdlib.h"
177 
178 
179 void main()
180 {
181 /*
182     int i;
183     int a[10]={[4]=4,5,6,7,[1]=1,2,3,110};    //數組的初始化技巧
184     for (i=0;i<10 ;++i )
185     {
186         printf("%d\n",a[i]);
187     }
188 */
189 /*
190     int abc[4]={1,2,3,4};
191     int i=0;
192     int (*p)[4];
193     p=&abc;  //p=abc;等效於p=&abc;
194     printf("%x,%x\n",&abc,abc);
195     for(i=0;i<4;++i)
196     {
197         printf("%d\n",p[0][i]);
198     }
199 */
200 /*
201     int Array[2][3][2]={{{1,2},{3,4},{5,6}},{{7,8},{9,10},{11,12}}};  
202     printf("%d\n",*(*(*(Array+1)))); 
203     printf("%d\n",*(*(Array+1))); 
204     printf("%d\n",*(Array+1)); 
205     printf("%d\n",(Array+1)); 
206     //printf("%d\n",*(*(Array+1)));
207 */
208 /*
209     int *p; 
210     int c[]={0x1234,0x5678};
211     int a=4;
212     int *b=&a;
213     p=(int *)malloc(4);
214     //printf("%p %d\n",b,*b);
215     //printf("%p %d\n",c,*c);
216     //printf("%p %d\n",(c+1),*(c+1));
217     //printf("%x",*p);
218     *p=0x12345678;
219     printf("%p\n",p);
220     printf("%x\n",*p);
221     free(p);
222     printf("%p\n",p);
223     printf("%x\n",*p);
224     b++;
225     printf("%p %d\n",b,*b);
226     b++;
227     printf("%p %d\n",b,*b);
228     b++;
229     printf("%p %d\n",b,*b);
230     b++;
231 */          
232 /*
233     char d[6] = {0x01,0x02,0x03,0x04,0x05,0x06};
234     char (*p)[2];   
235     p = d;  
236     printf("%p\n", (p));  
237     printf("%x\n", *(p)); 
238     printf("%p\n", *(*(p))); 
239     printf("%p\n", (p+1));
240     printf("%x\n", *(p+1)); 
241     printf("%p\n", *(*(p+1)));
242 */
243 /*
244     //-------------一級和多級指針的使用----------//
245     int val=0x1122;
246     char *p3=&val;
247     char **p2=&p3;
248 
249     printf("%x\n", p3); 
250     printf("%x\n", p3+1); //跳轉1個地址(因為p3是個一級指針而且類型修飾符為char)
251 
252     printf("%x\n", *(p3)); //操作1個字節(因為p3是個一級指針而且類型修飾符為char)
253     printf("%x\n", *(p3+1));//操作1個字節(因為p3是個一級指針而且類型修飾符為char)
254 
255     printf("%x\n", (p2)); 
256     printf("%x\n", (p2+1)); //跳轉4個地址(因為內存中字節所使用的地址長度為32位且指針p2是一個二級指針)
257 
258     printf("%x\n", *(p2)); //操作4個字節(因為內存中字節所使用的地址長度為32位且指針p2是一個二級指針)
259     //------------------------------------------//
260 */
261 /*
262     //-------------對多維指針的方括號運算以及多次解引用----------//
263     printf("%d\n", Array); 
264     printf("%d\n", Array[1]);   
265     printf("%d\n", Array[1][1]);    
266 
267     printf("%d\n",*(Array[1][1]));
268 
269     printf("%d\n",*(*(Array[1])));    
270     printf("%d\n",*(Array[1]));        
271     //-----------------------------------------------------------//
272 */
273 /*
274     //----------------------對多級多維指針的探究-----------------//
275     //假設已經掌握對多級0維指針,和一級多維指針的使用
276 
277     unsigned char (**p)[3];  //這是一個二級2維指針
278     int Array3[10]={0x804a0e0,0x55667788,2,3,4,5,6,7,8,9};  //之所以第一個元素設為0x804a0e0,是因為如果該值取的不當,下面對(*(*p))進行解引用的時候,有可能在程序執行時導致內核塌陷,看起來好像是程序語法錯誤,也就是說要保證解引用的指針是在正確范圍內的
279     p=Array3;  //Array3是一個一級1維地址,先讓Array3轉變為二級2維地址,再讓二級2維指針p所指向
280 
281     printf("%x\n", p);  //原本p是一個二級2維指針,在這里p表示為一個二級2維地址
282     printf("%x\n", p+1);  //發生4個地址的跳轉(因為地址長度為4個字節),因為(p+1)是一個二級2維地址,也就是此行語句輸出值比上一行語句大4
283 
284     printf("%x\n", *p);   //解引用得到4個字節的值,因為p是一個二級2維地址。*p 得到的是一個一級2維地址
285     printf("%x\n", (*p+1));  //跳轉3個字節,因為 *p 屬於一級2維地址,也就是此行語句輸出值比上一行語句大3。因為一級指針地址的跳轉,是取決於維數,*p是一個一級2維地址,那么跳轉數為:sizeof(unsigned char)*[3]=3
286     
287     printf("%x\n", *(*p));  //對一級2維地址(*p)進行解引用,得到一級1維地址*(*p)
288 
289     printf("%d\n", *(*(*p))); //對一級1維地址*(*p)進行解引用,得到1個字節的值,值為0xe0也就是224,因為指針 p 的類型修飾符為char,而sizeof(unsigned char)=1
290 
291     printf("%d\n", *(*(*p))+1); //該行打印值比上一行大1,為225
292 
293     //總結:對多級多維指針進行解引用的時,每次解引用都會遵循先降級再降維,當級數沒降到1,那么維數不起作用。
294     //      當已經是一級指針了后,維數起作用,當做一級多維指針或一級1維指針處理  ,一級1維地址再解引用就得到值了
295 */
296 /*
297     int (**p[3][4])[2][5];  //定義一個二級2維指針2維數組
298     int pp[5][6];
299     int test;
300     //p=&test;   //因為p是數組名,是一個常量,所以p=&test;這句有錯誤
301 
302     //下面證明了,解引用符號“ * ”在不同的場合應用,其作用發生了改變,如在sizeof()函數的參數中時,對數組名進行解引用,將會影打印出來的響數組“維積”
303     printf("%d\n", sizeof((**pp)));    //打印出數組“維積”,為4
304     printf("%d\n", sizeof(*p));  //打印數組的“維積”,為4*sizeof(int)=16
305     printf("%d\n", sizeof(p));  //打印出指針“維積”,為3*4*4=48
306     printf("%d\n", sizeof(*(**p[3][4])));  //打印出指針“維積”,為5*4=20
307 */
308 /*
309     int a =3;  
310     int b = 1;  
311     int c = 2;  
312     int const *p1 = &b;//const 在前,定義為常量指針  
313     int *const p2 = &c;//*在前,定義為指針常量
314     //常量指針p1:指向的地址可以變,但內容不可以重新賦值,內容的改變只能通過修改地址指向后變換。   
315     //p1 = &a是正確的,但 *p1 = a是錯誤的。
316     //指針常量p2:指向的地址不可以重新賦值,但內容可以改變,必須初始化,地址跟隨一生。
317     //p2= &a是錯誤的,而*p2 = a 是正確的。
318 */
319 
320     int array[10];
321     double *a=&array[5];
322     double *b=array;
323     printf("a-b=%d\n",a-b);  //打印結果為:2。   4*5/8=2    此行代碼是這樣理解的: a-b得到一個double類型的地址值,然后轉換成int類型,那么有20/8=2
324 
325 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM