1. 程式人生 > >C++指標詳解

C++指標詳解

指標是啥

眾所周知,你在程式中向計算機申請一個變數(如int a),計算機會分配給你的變數一個空間。用int a舉例子,計算機就給你了一個名叫a的房子,只能在裡面放int型別的值。當然,你可以直接寫a=10,這樣計算機會幫你找到名叫a的房子,把10放進去。
那麼這個房子在哪呢?就有了地址,每個變數都會有一個地址(即它在記憶體中的位置),指標,便是一根棍子,指著這個位置,指標裡存的就是這個地址。

怎麼用

宣告

在型別後面加上*即可,當然指標也是分型別的,如:

int* p;//一個指向(還沒確定指向哪裡)int型別房子的指標p
double* k;//指向double的k

如果你不確定這個型別,可以用void:

void* p;

但是,你如果這樣寫:

int* p,q;

事實上你是定義了一個指標和一個普通變數,所以我通常這樣寫:

int *p,*q;

注意定義指標後指標會指向一個隨機的位置,如果你對這個位置進行操作,就有可能發生記憶體錯誤。

操作

存入地址

這裡需要用到一個新運算子:&,它稱為取地址符,用於獲取一個變數的地址。
例如:

int *p;
int a;
p=&a;

你應該發現,scanf後面如果輸入一個變數要用&,所以scanf後面的引數就是一個地址。
你甚至可以輸出一個地址,如果用cout就直接輸出p即可,用printf需要用到格式控制符%p

,如:

int a;
scanf("%d",&a);
printf("%p",&a);

每個電腦上的結果可能會不一樣,例如我的:

輸出該地址上的值

如果你想知道指標所指向位置的值,需要再次用到*

int *p;
int a;
p=&a;
a=10;
printf("%d\n",*p);
a=15;
printf("%d\n",*p);

執行結果是什麼呢?
這裡寫圖片描述
不難理解,因為一個變數的地址永遠不會變。

加/減

你可能會想,指標是不是也有+,-操作呢?答案是肯定的,但不是把這個位置的值+,-,而是把地址往後或往前移動。
可以做一個實驗:

#include<cstdio>
int main() { int a=1,*p,*q; p=&a; q=p+1; printf("%p %p",p,q); }

結果:這裡寫圖片描述
為什麼多了4呢?因為int型別是4個位元組,所以加int型別的1會往後移4個位元組。
到這裡很想問了,那加char型別的'1'會怎麼樣?

#include<cstdio>
int main()
{
    int a=1,*p,*q;
    p=&a;
    q=p+'1';
    printf("%p %p",p,q);
}

結果:

並不是只移了1位,因為這裡的指標q是int型別的,你的'1'被自動轉為了ASCII碼,所以後移了很多位。
應該這樣做:

#include<cstdio>
int main()
{
    char a='1',*p,*q;
    p=&a;
    q=p+1;
    printf("%p %p",p,q);
}

這裡的+1就成了char的後移一位,也就是1個位元組,結果:

指標陣列

很好理解:

int *p[10];

宣告一個有十個指標(p[0],p[1],p[2],…,p[9])的陣列,每個元素都是一個指標。

其他

scanf

要輸入n(n≤1000000)個數存入a陣列再輸出,一般我們會這樣寫:

#include<cstdio>
#define MAXN 1000000
int a[MAXN+5],n;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        printf("%d ",a[i]);
}

知道了指標,我們有了更裝逼的方法:

#include<cstdio>
#define MAXN 1000000
int a[MAXN+5],n;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",a+i);
    for(int i=1;i<=n;i++)
        printf("%d ",*(a+i));
}

結果:

指標與其他資料結構

指標與陣列

我們知道,當int a[10]時,系統會連續開10個空間,所以我們可以這樣訪問陣列的元素:

#include<cstdio>
int main()
{
    int a[10]={0,2,4,6,8,10,12,14,16,18};
    int *p;
    for(p=&a[0];p<&a[0]+10;p++)
        printf("%d ",*p);
}

結果就不截圖了。
其中有一個:p<&a[0]+10是什麼意思呢?
不難理解,10是陣列的大小,&a[0]是陣列的首地址,&a[0]+10就是陣列末尾地址的下一位
這裡的p還有一個名稱,叫做陣列的迭代器,我們不說這些東西。
其實獲取陣列首地址還有一個辦法,直接這樣:

int a[10];
int *p;
p=a;

這樣就可以了,事實上,陣列你可以看成一個指標,可以這樣寫:

#include<cstdio>
int a[10]={0,2,4,6,8,10,12,14,16,18};
int main()
{
    int *p;
    p=a;
    printf("%d\n",*a);
    printf("%d\n",*(a+5));//注意加括號,否則就是輸出“a指向的值加5”了
    printf("%d\n",*p);
    printf("%d\n",*(++p));
}

結果:

相反的,指標也可以看做一個數組,所以經常有這樣寫的:

int sum(int *a,int n)
{
    int ans=0;
    for(int i=1;i<=n;i++)
        ans+=a[i];
    return ans;
}

那麼可以這樣用:

#include<cstdio>
int sum(int *a,int n)
{
    int ans=0;
    for(int i=1;i<=n;i++)
        ans+=a[i];
    return ans;
}
int main()
{
    int A[10],n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&A[i]);
    printf("%d\n",sum(A,n));
}

結果:

函式指標

你需要知道,在一個程式中,不僅僅是變數需要分配記憶體,函式也一樣,那麼函式自然也可以有指標,是函式的入口地址。函式指標宣告只比函式宣告多一個*一對括號,例如:

int (*Psum)(int*,int)

其中吧*Psum括起來的括號一定不能少,不然編譯器會認為你聲明瞭一個叫Psum的函式,返回型別是int*。
也就是說可以這樣用:

#include<cstdio>
int (*Psum)(int*,int);
int sum(int *a,int n)
{
    int ans=0;
    for(int i=1;i<=n;i++)
        ans+=a[i];
    return ans;
}
int main()
{
    int A[10],n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&A[i]);
    Psum=sum;//不用'&',因為這裡函式沒有括號視為是函式的地址
    printf("%d\n",(*Psum)(A,n));
}

到了這裡,我們就知道sort是如何把函式作為函式的引數的了,可以擬出一個sort(只針對int陣列):

void sort(int *begin,int *end,bool (*cmp)(int,int))
{
    /*......*/
}

結構體指標


和宣告一般指標一樣,名稱前加*即可。
要訪問這個指標所指向結構體當中的元素有2種方法:
1.’*’法:在指標前加上*,再把它們括起來,就可以當該指標指向的結構體用了。
2.’->’法,和一般結構體用法一樣,只是把成員運算子.變為->即可。
詳見示例:

#include<cstdio>
struct student
{
    int snum;
    int age,grade;
};
student T;
student *p;
int main()
{
    p=&T;
    T.snum=15;
    T.age=12;
    T.grade=90;
    printf("%d\n",(*p).snum);
    printf("%d\n",p->age);
    printf("%d\n",T.grade);
}

結果:

多重指標

和陣列一樣,你可以在宣告時連續打2個(或多個)*,例如:

#include<cstdio>
int main()
{
    int **p,*q;//p就是指向一個指標的指標,q是指向一個普通變數的指標
    int a;
    a=1;
    q=&a;
    p=&q;
    printf("%p  %p  %p  %p\n",p,*p,q,&a);
    printf("%d  %d  %d",**p,*q,a);
}

猜猜結果:
這裡寫圖片描述
前面的地址有可能會不一樣,但輸出的格式肯定是:
Q P P
A A A
為什麼*p要用%p輸出?因為*pq,而q是一個指標,所以用%p