1.簡單指針 *p:
(1)用於數組
int *p 定義指針
該指針表示指向某個變量的地址;
當指針與自增符號結合時,簡單舉例如下:
int m[5]={1,3,5,7,9};p=m;
A.*p++; B.*++p; C.++*p; D.x=*p++; E.x=*++p; F.x=(*p)++; G.x=*(p++) ;
首先,看個簡單的例子,int m=0,n; n=m++; n=++m; n=(m++);中第一個式子得到m=1,n=0第二個式子得到m=1,n=1.
其中m++和++m的區別在於是 先賦值還是先計算。易搞混的是第三個式子n=(m++)得到的結果仍然是m=1,n=0 此處須說明,雖然括號將m++擴起優先計算,但是在C中,m++的含義是在計算其他賦值等式子之後再單獨進行++,可以理解爲單獨一句m=m+1,因此優先級考慮就與擴號無關了。
由此再看ABC:先來比較*與++的優先級:*做乘號,比++優先級低,但是與此處無關;*做取內容符號時,優先級同++p中的++,但是低於p++中的++。
在AB中,二者意思相同,均爲將指針後移一個單位,A中容易混淆。須記住它不是 *p = *p + 1; 它卻是 *p = *(p+1); 而且是後加加;由於++和*優先級相同,C選項中先計算*p,取到p指向的內容,再對內容做自增,與前二者不同,代碼如下:
#include<stdio.h>
main()
{
int m[5]={1,3,5,7,9},*a,*b,*c;
a=m;
b=m;
c=m;
*a++;
*++b;
++*c;
printf("this is *a: %d \n",*a);
printf("this is *b: %d \n",*b);
printf("this is *c: %d \n",*c);
}
輸出結果見下對於DEFG,結合上面兩個方面也就不難理解:
D先將p指針內容給x,隨後指針自增,得到*p爲3,x爲1;
E中先指向下一位再賦值,x=*p=3;
F中x仍爲指針原本指向的值x=1;此處先取到p的值,再對值自增,得到*p最後爲2;同時m[0]的值同步變爲2;
G中由上方分析可知,()的存在對式子和++的先後順序無影響,結果同D。
(2)用於字符串
字符串中,指針在用法上與數組有很大區別:
a.指針指向字符串須與字符串同數據類型;
b.講指針指向字符串後,指針即可代替該字符串進行使用,表示整串輸出字符串內容,而不是地址,如用puts(指針名)直接輸出整個字符串;
c.二者用取內容運算符*,均表示相應數組在對應位置的內容,字符串指針p的類型是string,*p則是char(具體應用在輸出格式上),注意,字符串指針*p指單個char變量,判斷是否結束,用*p==‘\0’;
d.一個易錯點!:
如下寫法會正常編譯,但是在運行時出現卡死:
int main() {
char *s1="abc",*s2="def";
s1[2]=s2[1] ;//這裏出錯
return 0;
}:
如果把字符串指針當做數組來用,必須先定義已有的數組,再令字符串指針指向該數組,否則會出錯。正確寫法:int main() {
char s1[]="abc",s2[]="def";
char *p1=s1,*p2=s2
p1[2]=p2[1] ;//這個時候才能把字符串數組當指針用
printf("%c",s1[2]);
return 0;
}
2.指針數組*p[],數組指針(*q)[]:
二者被我多次搞混,出現語法等錯誤,故放在一起討論
*p[n]表示的是一個指針數組,從符號優先級可以看出來,其和*(p[n])等價。即一個指針的集合,包含n個指針,他們相互獨立。這種用法與二級指針**p相似
其常見應用:表示二維字符數組:
int main( )
2.{ char *p[ ]={"PROGRAM","BASIC","C","JAVA"};
3. int i;
4. for (i=3;i>=0;i--) printf("%c",*p[i]);
5. printf("\n");
6.return 0;
7.}
其結果爲倒敘輸出四個單詞,char *p[n]表示一串指針時 等價於 n個char *p 等價於char **p(二級指針詳細部分見下)
另外 用二維整型數組表示*p[n]容易出現的一種錯誤,剛好與(*q)[n]做簡單對比:
int main(int argc, char *argv[]) {
int a[3][4]={1,2,3,4,
5,6,7,8,
9,10,11,12},i,j;
int *p[3]; //這裏的角標是3,因爲這是一個含有3個指針的數組,每個指針對應上面二維數組的一行
*p=a; //這裏用的*p,區別於下面。因爲這裏的p相當於二級指針,對應的*p纔是一級指針,指向數組
for(i=0;i<3;i++)
for(j=0;j<4;j++)
printf("%3d",a[i][j]) ;
printf("\n");
for(i=0;i<3;i++)
for(j=0;j<4;j++)
printf("%3d",*(*(p+i)+j)) ;
}
此處在輸入12個數之後 得到的結果只有前四個與上吻合,原因是指針數組內部變量相互獨立。輸出如下:
下面再來看 (*p)[n]
如int(*P)[10],p先和*結合,意味着p是一個指針,他指向int [10],即p是一個指向一個數組的指針
int main(int argc, char *argv[]) {
int a[3][4]={1,2,3,4,
5,6,7,8,
9,0,1,2},i,j;
int (*p)[4]; //注意這裏角標是4.原因是這個指針指向int [4]的數組
p=a; //注意這裏直接p=a,區別上面的*p=a;因爲這裏的p是一級的指針,指向數組
for(i=0;i<3;i++)
for(j=0;j<4;j++)
printf("%3d",a[i][j]) ;
printf("\n");
for(i=0;i<3;i++)
for(j=0;j<4;j++)
printf("%3d",*(*(p+i)+j)) ;
}
注意二者在表示上略有不同 此處n=4;等於二維數組的列寬;另外在賦予地址時,此處不需要間接引用符*,只需將q指針指向數組第一個內存位置即可。
輸出結果如下:
3.二級指針**p
二級指針表示指向指針的指針,可以理解爲指針數組*p[]
假如如此定義:int **p,(*q)[5];
這時的*(p+i)和q[i]是等價的;
int main( )
{ char *a[6]= {"ABCD","EFGH","IJKL","MNOP","QRST","UVWX"};
char **p;
int i;
p=a;
for( i=0; i<6; i++ ) printf("%s",*(p+i));
printf("\n");
return 0;
}
如上,其中的*(p+i)就等價於a[i],等價於int *q="...."二級指針一個很重要的用法就是在調用函數過程中使用,使在函數內部可以改變原先指針的指向:
void change_ptr(void **ptr, void *dest)
{
*ptr = dest;
}
change_ptr(&ptr,dest)
注意此處調用時 要取二級指針對應項的地址,才能在函數內部使二級指針指向指針,通過二級指針改變指針本身的內容。可以這樣改變指針內容是因爲,C中進入一個子函數的時候,會默認把原函數中需帶入的變量拷貝一份進入子函數;這時拷貝一個指針的地址,等於在函數中可以改變這個指針本身
關於二維數組和二級指針,指針數組相關,可以參考下面這個文章:
4.指向結構的指針
指向結構的指針用p->xxx來表示,其中xxx是結構內部定義的一個項;
如定義struct list{}a此時 p->xxx等價於 a.xxx等價於*(p).xxx
補充部分:
1.關於free(q): free和malloc要成對出現
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char *str = (char *)malloc(100);
strcpy(str, "hello");
free(str);
if(str != NULL)
{
strcpy(str, "world");
printf("%s\n", str);
}
return 0;
}
free後的指針,要及時進行NULL,否則會成爲野指針,影響穩定性;
另外free()不能用在這種情況下:
int *p=0;
p=malloc(100*1024);
p++;
free(p);
free()必須釋放指定分配的內存,不可以是進行移動、賦值後的指針。2.關於兩字符串相減
C中把字符串認爲是指針,字符串相減就是指針相加減,如
char s1="abcdef",s2="def";
int n=s2-s1;
得到的值n是很大的數,因爲兩字符串地址不相連
第二種情況
char *s1="abcdefg",*s2;
s2=s1;
s2+=5;
int n=s2-s1;
這裏得到的n就是二者地址差,因爲二者的字符串內容在同一段地址上,s2指針在後移之後是"fg",二者地址差是5.
第三種情況:兩個指針相減:
int a[]={2,4,6,8,10,12};
int *p1 = a;
int *p2 = &a[3];
printf("p2-p1=%d",p2-p1)
這裏可能有如下兩種結果: I.得到的是二者地址差,二者差3個int型的地址,也就是3*4=12或3*8=24;
II.得到的就是二者指向位置之差,即3.
經過編譯運算,這裏是第II種情況。指針相減不能得到地址差,而是得到“地址差÷sizeof(數據類型)”。
3.指針用於多個返回值
指針可用於函數中來返回多個值,具體用法:
設置swap函數,交換兩個變量的值:
void swap(int *x1,int *x2);
int main(int argc, char *argv[]) {
int a,b;
scanf("%d",&a);
scanf("%d",&b);
swap(&a,&b);
printf("a=%d,b=%d",a,b);
return 0;
}
void swap(int *x1,int *x2){
int temp;
temp=*x1;
*x1=*x2;
*x2=temp;
}
函數部分曾經錯寫爲:int *temp;temp=x1;x1=x2;x2=temp;
這裏的交換是交換過程操縱兩個指針變量,修改對應地址的內容,而不是對指針進行交換,函數內部的交換在返回main時失效。
4.不要這樣寫指針
int *p=5;
這種寫法容易使程序崩潰。道理很簡單:當我們定義一個指針int * p 的時候,這個指針會隨機指向某個位置,如果我們這時修改指針指向的內存,使之變爲“5”這個數字,那麼該地址中原本的內容會被覆蓋掉,如果是一些重要數據,則會導致崩潰。因此一般情況下,定義指針後要用已經定義的變量去“引導”指針指向固定好的位置。int a = 5;
in *p = a;
此外,在C中,數組p[]和指針*p在C中是同一個東西,即數組是一個常量指針,同時定義數組p[]和指針*p就會出現衝突錯誤。
易錯點:
當對一個指針進行自增操作(q++)時,不是地址值+1,而是地址值+n。如:
int *p ,則p++表示地址+4/+8;
char *q,則q++表示地址值+1。
另外,注意區分:*(p+1)和 *p + 1。二者區別一個是地址++,一個是內容++。
5.const 與 指針
有如下三種寫法:
int a;
const int *p1=&a; //寫法1
int const *p2=&a ; //寫法2
int *const p3=&a ; //寫法3
這裏三者區別,只看 const 與 * 誰前誰後:const 在 * 之前,p可以改變地址,但是不能利用p去改變指向處的變量值,如1,2;
const 在 * 之後,不可變的是指針p,因此指針p指向的地址不能變,如3;
另外,三者對變量a無影響,仍然可以直接通過a修改內容。
以上個人筆記,如有錯誤請指出,會持續進行補充