初談指針(2)

初談指針(2)

前言

上一篇(初探指針(1))從指針的概念開始入手,依次是:什麼是指針>>指針傳遞>>多級指針>>指針函數。儘管指針遠不於此,但尋常應付想來足夠了。這一次想談談關於指針易混淆的地方。
倒不知是否翻譯之故,一度出現了指針函數和函數指針,指針數組和數組指針,指針常量和常量指針,字符指針數組和二維字符數組……通通被我認爲屬於一個羣體——不用就會忘記系列。其實拋開考試中會故意找茬,實際運用到都算不上太難。但這些術語名字嘛……想起我還小的時候,范冰冰李冰冰傻傻分不清。數組

指針與二維數組

首先咱們須要理清指針與數組的關係:數組不等於指針,但在某些狀況下兩者等價函數

定義一個一維數組int a[i] = {...};,當咱們使用a[i]的時候,實際上編譯器作了轉換處理,變成了*(a+i)ui

一維數組是方便理解的,那麼二維數組呢?
a[i][j]*(*(a+i)+j)具備等價效果,對後者分析以下:spa

  1. a表示二維數組中第一行的地址,a+i則表示第i+1行的地址
  2. *(a+i)表示第i+1行的列地址(這個列爲第一列),*(a+i)+j則表示第i+1行、第j+1列的地址
  3. *(*(a+i)+j)表示取出這個地址上的數據

有了這個鋪墊,咱們就能夠愉快的開始下面的旅程了。翻譯

指針函數

前面說到在指針函數中,return的時候注意不要是局部變量的地址,這句話自己是有瑕疵的。但若是就這樣作了,保準兒程序不會出錯。可是等一等,咱們須要這般畏首畏尾嗎?指針

與「猥(畏)瑣(縮)」說不!
通常說來,存放在棧中的變量不能夠被return地址。這是因爲棧中的數據由系統產生,也由系統回收,很難說咱們在return棧中的某個數據的地址時,系統是否是早一步把它幹掉了。code

但這幾個數據是能夠的:全局變量,靜態變量,常量數據,堆中的數據。一個運行的程序裏,這些數據並不存放在棧中,具體在哪裏涉及到內存分配的問題,這裏不作延伸了。regexp

函數指針

函數指針的根本是指針,它可以指向一個函數。遊戲

定義:int (*p)(int, int);
表示p是一個可以指向函數的指針,而且它指向的函數是int類型,且這個函數有兩個參數,這個兩個參數都是int類型。內存

#include <stdio.h>


int add_xy(int x, int y)
{
    return x+y;
}

int main(int argc, char **agrv)
{
    int (*p)(int, int);
    p = &add_xy;

    int result = p(1, 2);
    printf("%d\n", result);

    return 0;
}

// 輸出
>>3

觸類旁通:char *(*p)(int, char *) 表示p是一個指向函數的指針,這個函數有兩個參數:一個int類型,一個char*類型(char*的指針),而且這個函數返回char *的指針

必定要區分: char *p(int, char *),當沒有小括號把指針運算符和指針變量包起來的時候,表示指針函數的聲明,函數的名字爲p

指針數組

指針數組的根本是數組,即:一個數組裏的成員都是指針

定義:int *p[];
按照優先級順序,這裏p[]是一個總體,因此能夠看做:int * —> p[]

示例:

#include <stdio.h>

int main(int argc, char **agrv)
{
    int a=1, b=2, c=3;
    int *p[3] = {&a, &b, &c};  // 定義一個指針數組,而且初始化

    int i = 0;
    for(i=0;i<3;i++){
        printf("%d\n", *(*(p+i)));
        // printf("%d\n", *p[i]); 也能夠這樣寫
    }

    return 0;
}

// 輸出
>>1
>>2
>>3

數組指針

數組指針的根本是指針,即:一個指向數組的指針

定義:int (*p)[];
按照優先級順序,*p是一個總體,因此能夠看做int *p —> []

示例:

#include <stdio.h>

int main(int argc, char **argv)
{
    int a[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

    int (*p)[3] = a;  // 定義一個數組指針,而且初始化

    int i = 0, j = 0;
    int row = 3, col = 3;
    for(i=0; i<row; i++){
        for(j=0; j<col; j++){
            printf("%d ", *(*(p+i)+j));
            // printf("%d ", (*(p+i))[j]);  也能夠這樣寫
        }
    }

    return 0;
}

// 輸出
>>1 2 3 4 5 6 7 8 9

指針常量

指針常量表示:這個指針爲「只讀」(從始至終只能指向一個地址,能夠經過指針運算符來修改所指向的地址上的數據,必定在定義的時候賦初值

定義:int *const p;
指針(*)常量(const),也就是說指針運算符在const的前面;const修飾的是p,因此p不能被賦值

#include <stdio.h>

int main(int argc, char **argv)
{
    int a = 1;
    int *const p = &a;  

    printf("%d\n", a);

    *p = 2;
    printf("%d\n", a);

    return 0;
}

// 輸出
>>1
>>2

常量指針

常量指針表示:這個指針指向的地址上的數據爲「只讀」(該指針能夠隨意更改指向的地址,但不能經過取地址運算符來修改所指向的地址上的數據

定義:int const *p;
常量(const)指針(*),也就是說,const在指針運算符的前面;而且能夠看作const 修飾的是*p,因此*p不能被賦值

示例:

#include <stdio.h>

int main(int argc, char **argv)
{
    int const *p = NULL;
    int a = 1, b = 2;

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

    p = &b;
    printf("%d\n", *p);

    return 0;
}

// 輸出
>>1
>>2

字符指針數組和二維字符數組

字符指針數據,也就是一個數組,裏邊的成員都是char *的指針,即:char *str[] = {"...", "...", ...};
二維字符數組,一個二維數組,裏邊成員都是char類型的字符,即:char str[][n] = {"...", "...", ...};

#include <stdio.h>

int main(int argc, char **agrv)
{
    char *names[] = {"Tom", "Alice", "Bob"};

    int i = 0;
    for(i=0; i<3; i++){
        printf("%s ", *(names+i));
        // printf("%s ", names[i]);  也能夠這樣寫
    }
    printf("\n");

    char hobbies[][10] = {"swimming", "sleeping", "reading"};
    for(i = 0; i<3; i++){
        printf("%s ", *(hobbies+i));
        // printf("%s ", hobbies[i]);  也能夠這樣寫
    }
    printf("\n");

    return 0;
}

// 輸出
>>Tom Alice Bob
>>swimming sleeping reading

在使用上,兩者是沒差的,但從內存上分析就存在區別。
字符指針數組中的成員都是一個char *的指針,也就是說,它們都是地址,真正的字符串存放在常量區;二維字符數組裏邊存放的都是一個個char類型的字符。建議使用前者,由於字符指針數組存儲字符串要比二維字符數組開銷小。

連連看

我記得有一個送命遊戲叫作:韓國女星連連看。其實對我重度臉盲患者,豈止韓國女星,即使是如今的許多大陸女星,我也着實難分辨(其實如今的新興男星我也基本分不清了)。好在能不能區分出明星仍是其次,重要的是區分如下的表達式。若是可以輕易識別,指針也算入門了。

  • int *p[n];
  • int (*p)[n];
  • int (*p)(int, int);
  • int (*p[])(int, int);
  • int p(int, int);
  • int *p(int, int);
  • int const *p;
  • int *const p;
  • int const * const p;
相關文章
相關標籤/搜索