40篇學完C語言——(第八篇)【指針數組以及指向指針的指針】

語言: CN / TW / HK

數組

int a[10];

int a[10]; //a代表的是數組首元素的地址

a+1 步長 4

&a+1 步長 40 ;&a代表整個數組的地址;指針也是一種數據類型,指針的步長就看他指向內存空間的類型; 所以內存空間加1就是地址偏移40

#define  _CRT_SECURE_NO_WARNINGS 
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
void main(void)
{
    int a[10]; //a代表的是數組首元素的地址  &a代表整個數組的地址  a+1 4  &a+1步長 40 .

    //
    {
        //定義一個數組類型
        typedef int(myTypeArray)[10]; //
        myTypeArray myArray;
        myArray[0] = 10;
        myArray[1] = 11;
        printf("%d \n", myArray[0]);
        printf("%d \n", myArray[1]);
    }

    {
        //定義一個指針數組類型 
        typedef int(*PTypeArray)[10];  //int *p 

        PTypeArray myPArray; //sizeof(int) *10
        myPArray = &a;
        //int b = 10;
        //int *p = NULL;
        //p = &b;
        (*myPArray)[0] = 20;

        printf("a[0]: %d \n", a[0]);

    }

    {
        //定義一個指向 數組類型的指針 數組類的指針

        int(*MyPointer)[10]; //變量 吿訴C編譯器 給我分配內存
        MyPointer = &a;
        (*MyPointer)[0] = 40;
        printf("a[0]: %d \n", a[0]);
    }

    printf("hello...\n");
    system("pause");
    return;
}

1、為什麼需要指針?

指針解決了一些編程中基本的問題。

第一,指針的使用使得不同區域的代碼可以輕易的共享內存數據。當然你也可以通過數據的複製達到相同的效果,但是這樣往往效率不太好,因為諸如結構體等大型數據,佔用的字節數多,複製很消耗性能。但使用指針就可以很好的避免這個問題,因為任何類型的指針佔用的字節數都是一樣的(根據平台不同,有4字節或者8字節或者其他可能)。

第二,指針使得一些複雜的鏈接性的數據結構的構建成為可能,比如鏈表,鏈式二叉樹等等。

第三,有些操作必須使用指針。如操作申請的堆內存。還有:C語言中的一切函數調用中,值傳遞都是“按值傳遞”的,如果我們要在函數中修改被傳遞過來的對象,就必須通過這個對象的指針來完成。

2、指針是什麼?

指針是程序數據在內存中的地址,而指針變量是用來保存這些地址的變量。

由於內存中的每一個字節都有一個唯一的編號,因此,在程序中使用的變量,常量,甚至數函數等數據,當他們被載入到內存中後,都有自己唯一的一個編號,這個編號就是這個數據的地址。指針就是這樣形成的。

char ch = 'a';
int  num = 97;

我們可以大致畫出變量ch和num在內存模型中的存儲。(假設 char佔1個字節,int佔4字節)

3、指針與變量的關係

用來保存 指針 的變量,就是指針變量。如果指針變量p1保存了變量 num的地址,則就説:p1指向了變量num,也可以説p1指向了num所在的內存塊 ,這種指向關係,在圖中一般用 箭頭表示。

int main(void)
{
    int num = 97;
    int *p1  = #
    char* p2 = (char*)(#);
    printf("%d\n",*p1);    //輸出  97
    putchar(*p2);          //輸出  a
    return 0;
}

指針的值:很好理解,如上面的num 變量 ,其地址的值就是0028FF40 ,因此 p1的值就是0028FF40。數據的地址用於在內存中定位和標識這個數據,因為任何2個內存不重疊的不同數據的地址都是不同的。

指針的類型:指針的類型決定了這個指針指向的內存的字節數並如何解釋這些字節信息。一般指針變量的類型要和它指向的數據的類型匹配。

由於num的地址是0028FF40,因此p1 和 p2的值都是0028FF40

*p1 : 將從地址0028FF40 開始解析,因為p1是int類型指針,int佔4字節,因此向後連續取4個字節,並將這4個字節的二進制數據解析為一個整數 97。

*p2 : 將從地址0028FF40 開始解析,因為p2是char類型指針,char佔1字節,因此向後連續取1個字節,並將這1個字節的二進制數據解析為一個字符,即’a’。

同樣的地址,因為指針的類型不同,對它指向的內存的解釋就不同,得到的就是不同的數據。

4、指針類型

5、數組指針和指針數組

“數組指針”和“指針數組”,只要在名詞中間加上“的”字,就知道中心了——

數組的指針:是一個指針,什麼樣的指針呢?指向數組的指針。

指針的數組:是一個數組,什麼樣的數組呢?裝着指針的數組。

然後,需要明確一個**優先級順序:()>[]>***,所以:

(*p)[n]:根據優先級,先看括號內,則p是一個指針,這個指針指向一個一維數組,數組長度為n,這是“數組的指針”,即數組指針;

* p[n]:根據優先級,先看[],則p是一個數組,再結合 ,這個數組的元素是指針類型,共n個元素,這是“指針的數組”,即指針數組。

根據上面兩個分析,可以看出,p是什麼,則詞組的中心詞就是什麼,即數組“指針”和指針“數組”。

int *p1[5];//指針的數組
int (*p2)[5];//數組的指針
首先,對於語句int p1[5],因為“[]”的優先級要比 要高,所以 p1 先與“[]”結合,構成一個數組的定義,數組名為 p1,而“int

”修飾的是數組的內容,即數組的每個元素。也就是説,該數組包含 5 個指向 int 類型數據的指針,如圖 1 所示,因此,它是一個指針數組。


p2)[5],“()”的優先級比“[]”高,“*”號和 p2 構成一個指針的定義,指針變量名為 p2,而 int 修飾的是數組的內容,即數組的每個元素。也就是説,p2 是一個指針,它指向一個包含 5 個 int 類型數據的數組,如圖 2 所示。很顯然,它是一個數組指針,數組在這裏並沒有名字,是個匿名數組。

由此可見,對指針數組來説,首先它是一個數組,數組的元素都是指針,也就是説該數組存儲的是指針,數組佔多少個字節由數組本身決定;而對數組指針來説,首先它是一個指針,它指向一個數組,也就是説它是指向數組的指針,在 32 位系統下永遠佔 4 字節,至於它指向的數組佔多少字節,這個不能夠確定,要看具體情況。

5.1、 數組指針 (*p)[n]

#include "stdafx.h"

int main()
{
    //一維數組
    int a[5] = { 1, 2, 3, 4, 5 };
    //步長為5的數組指針,即數組裏有5個元素
    int (*p)[5];
    //把數組a的地址賦給p,則p為數組a的地址,則*p表示數組a本身
    p = &a;

    //%p輸出地址, %d輸出十進制
    //\n回車
    //在C中,在幾乎所有使用數組的表達式中,數組名的值是個指針常量,也就是數組第一個元素的地址,它的類型取決於數組元素的類型。
    printf("%p\n", a); //輸出數組名,一般用數組的首元素地址來標識一個數組,則輸出數組首元素地址
    printf("%p\n", p); //根據上面,p為數組a的地址,輸出數組a的地址
    printf("%p\n", *p); //*p表示數組a本身,一般用數組的首元素地址來標識一個數組
    printf("%p\n", &a[0]); //a[0]的地址
    printf("%p\n", &a[1]); //a[1]的地址
    printf("%p\n", p[0]); //數組首元素的地址
    printf("%d\n", **p); //*p為數組a本身,即為數組a首元素地址,則*(*p)為值,當*p為數組首元素地址時,**p表示首元素的值1
    printf("%d\n", *p[0]); //根據優先級,p[0] 表示首元素地址,則*p[0]表示首元素本身,即首元素的值1
    printf("%d\n", *p[1]); //為一個絕對值很大的負數,不表示a[1]...表示什麼我還不知道

    //將二維數組賦給指針
    int b[3][4];
    int(*pp)[4]; //定義一個數組指針,指向含4個元素的一維數組
    pp = b; //將該二維數組的首地址賦給pp,也就是b[0]或&b[0],二維數組中pp=b和pp=&b[0]是等價的
    pp++; //pp=pp+1,該語句執行過後pp的指向從行b[0][]變為了行b[1][],pp=&b[1]

    int k;
    scanf_s("%d", &k);

    return 0;
}

根據上面二維數組可以得出,數組指針也稱指向一維數組的指針,所以數組指針也稱行指針。

5.2、指針數組 *p[n]

指針數組:是數組——裝着指針的數組。

看下面的例子進行理解:

數組指針:是指針——指向數組的指針。

看下面的例子進行理解

#include "stdafx.h"

int main()
{
    int a = 1;
    int b = 2;
    int *p[2];
    p[0] = &a;
    p[1] = &b;

    printf("%p\n", p[0]); //a的地址
    printf("%p\n", &a); //a的地址
    printf("%p\n", p[1]); //b的地址
    printf("%p\n", &b); //b的地址
    printf("%d\n", *p[0]); //p[0]表示a的地址,則*p[0]表示a的值
    printf("%d\n", *p[1]); //p[1]表示b的地址,則*p[1]表示b的值

    //將二維數組賦給指針數組
    int *pp[3]; //一個一維數組內存放着三個指針變量,分別是p[0]、p[1]、p[2],所以要分別賦值
    int c[3][4];
    for (int i = 0; i<3; i++)
        pp[i] = c[i];

    int k;
    scanf_s("%d", &k);

    return 0;
}

最後,從上文來看:

數組指針是一個指針變量,佔有內存中一個指針的存儲空間;

指針數組是多個指針變量,以數組的形式存儲在內存中,佔有多個指針的存儲空間。

6、舉例1

typedef struct
{
    unsigned short ad[3][5][100]; 
} photo_sample_data_stru;             
photo_sample_data_stru  *Photo_Sample_Read_Base_Point,Photo_Sample_Read_data ;

unsigned short data_0_0_x[100],data_0_1_x[100];
unsigned short   *Photo_Sample_Point0,*Photo_Sample_Point1;
uint32_t addr;
void data_init(void)
{
    uint16_t Photo_Sample_Line,photo_sample_time;
    for(Photo_Sample_Line=0;Photo_Sample_Line<5;Photo_Sample_Line++)
    {
        for(photo_sample_time=0;photo_sample_time<100;photo_sample_time++)
        {
            Photo_Sample_Read_Base_Point->ad[0][Photo_Sample_Line][photo_sample_time]= photo_sample_time; //第一路; 
            Photo_Sample_Read_Base_Point->ad[1][Photo_Sample_Line][photo_sample_time]= photo_sample_time; //第二路;
            Photo_Sample_Read_Base_Point->ad[2][Photo_Sample_Line][photo_sample_time]= photo_sample_time; //第三路;
        }
    }
}

void main()
{
    int num=2021;
    printf("num addr = 0x%x\r\n",(int)#);
    printf("num  = %d\r\n",num);
    printf("\r\n");

    Photo_Sample_Read_Base_Point = &Photo_Sample_Read_data;
    data_init();
    printf("Photo_Sample_Read_Base_Point addr = 0x%x\r\n",(int)(Photo_Sample_Read_Base_Point));
    printf("Photo_Sample_Read_Base_Point  = %d\r\n",(int)(Photo_Sample_Read_Base_Point->ad[0][0][0]));
    printf("\r\n");

    Photo_Sample_Point0 = Photo_Sample_Read_Base_Point->ad[0][0];
    printf("Photo_Sample_Point0 addr = 0x%x\r\n",(int)Photo_Sample_Point0);
    for( i=0;i<10;i++)
    {
        data_0_0_x[i]=Photo_Sample_Point0[i];
        printf("Photo_Sample_Point0[%d] = 0x%x \r\n",i,Photo_Sample_Point0[i]);
    }
    printf("\r\n");

    Photo_Sample_Point1 = Photo_Sample_Read_Base_Point->ad[0][1];
    printf("Photo_Sample_Point1 addr = 0x%x\r\n",(int)Photo_Sample_Point1);
    for( i=0;i<10;i++)
    {
        printf("Photo_Sample_Point1[%d] = 0x%x\r\n",i,Photo_Sample_Point1[i]);
        data_0_1_x[i]=Photo_Sample_Point1[i];
    }
    printf("\r\n");
}

先看輸出結果:

分析1:Photo_Sample_Read_Base_Point是指向Photo_Sample_Read_data的指針。Photo_Sample_Read_Base_Point地址是0x1000034C

分析2:Photo_Sample_Point0,Photo_Sample_Point1這兩個指針,以及指針給數組賦值。

Photo_Sample_Point0 addr = 0x1000034c

Photo_Sample_Point1 addr = 0x10000414

Photo_Sample_Point0[i] 實際為Photo_Sample_Read_data.ad[0][0][i]
Photo_Sample_Point1[i] 實際為Photo_Sample_Read_data.ad[0][1][i]

7、舉例2:

typedef struct
{
    unsigned char photo_ad_data[20][2];     
} photo_sample_stru;                       
photo_sample_stru         *photo_data_point[6];        
static photo_sample_stru   pmt_Data_Load[6];
static unsigned int Data_Loc_o;
static unsigned int Data_Loc_p; 
void main()
{
    for (Data_Loc_o=0;Data_Loc_o<6;Data_Loc_o++)  //  清空數據緩衝區  
    {
        photo_data_point[Data_Loc_o] =  &pmt_Data_Load[Data_Loc_o];
            //pmt_first =  photo_data_point[0];
        for (Data_Loc_p=0;Data_Loc_p<20;Data_Loc_p++)                    
        {
            photo_data_point[Data_Loc_o]->photo_ad_data[Data_Loc_p][0] = 0;
            photo_data_point[Data_Loc_o]->photo_ad_data[Data_Loc_p][1] = 0;

        }
    }
}

結果如下:

分析: 我們下面看出,photo_sample_stru *photo_data_point[6]; 是指向photo_sample_stru 類型的 指針數組 (即指針的數組)。利用指針把pmt_Data_Load裏面數據清零。

8、舉例3

#define MAX_SEG         100
#define MAX_CYCLE       10 //最大循環個數的宏定義

typedef  uint8_t UCHAR ;
typedef struct
{
   short           RunNow;                       //運行標誌(正常開機,斷電後重啟)
   short           RunEnd;                       //運行結束標誌
   short           FolderName[12];                //目錄名
   short           FileName[12];                  //文件名
   short           SaveYear;                      //年(文件的保存時間)
   short           SaveMonth;                     //月(文件的保存時間)
   short           SaveDate;                      //日(文件的保存時間)
   short           SaveHour;                      //時(文件的保存時間)
   short           SaveMinute;                    //分(文件的保存時間)
   short           SaveSecond;                    //秒(文件的保存時間)
   short           HotlidSet;                      //熱蓋設置
   short           VolumeSet;                     //樣本容量
   short           ModeSet;                       //運行的模式
   short           TestModeSet;                   //模擬模式
   short           FirstPause;                    //首節暫停
   short           EditSeg;                       //節點個數
   short           EditCycle;                     //循環個數
   short           TempSet[MAX_SEG];              //節點温度
   short           HourSet[MAX_SEG];              //節點時間---時
   short           MinuteSet[MAX_SEG];            //節點時間---分鐘
   short           SecondSet[MAX_SEG];            //節點時間---秒
   short           SpeedSet[MAX_SEG];             //速度
   short           TempCYCSet[MAX_SEG];           //每個循環節點提升温度
   short           MinuteCYCSet[MAX_SEG];         //分――每個循環節點增加時間
   short           SecondCYCSet[MAX_SEG];         //秒――每個循環節點增加時間
   short           GradSet[MAX_SEG];              //梯度 
   short           CycleSet[MAX_CYCLE];           //每個循環次數
   short           CycleBeginSet[MAX_CYCLE];      //循環開始節點
   short           CycleEndSet[MAX_CYCLE];        //循環終止節點
   short           GradExtSet[6][MAX_SEG];        //獨立運行信息
   //short           AloneSet[MAX_SEG];             //分區設置
   short           TubeVolSet;                    //試管體積
   short           TubeTypeSet;                   //試管類型
   short           Fill[580];             //用於填充,結構總長度2Kwords
}StructFile;

typedef struct
{
    unsigned long    File_Add;//地址
    short   Seg;
    short   cyc_inside;
    short   cyc_outside;
    short   outside_flag;
    short   OverShootFlag;
    short   OverTime;
    short   Now_Temp;
}StructCalTime;

StructCalTime *Cal_Point;
StructCalTime Cal_TimeA;

void main()
{
    uint8_t *u8_point;
    u8_point = (uint8_t*)(&Cal_TimeA.File_Add);
    for(uint16_t i=1;i<sizeof(StructCalTime);i++)
    *(u8_point+i+4)=i;//賦值

    Cal_TimeA.File_Add=(unsigned long)(&Cal_TimeA);//取地址
    Cal_RunFile = *((StructFile*)(Cal_TimeA.File_Add));//重指向

//  Cal_RunFile = *((StructFile*)((unsigned long)(&Cal_TimeA)));//直接賦值
}