鍍金池/ 教程/ 數(shù)據(jù)庫/ 12.4 C 語言指向數(shù)組元素的指針
8.3 C 語言函數(shù)的形參和實參
12.2 C 語言指針變量的聲明
12.5 ?C 語言字符數(shù)組和字符指針
7.3 單片機(jī) LED 點(diǎn)陣的介紹
11.5 UART 串口通信的基本應(yīng)用
9.9 單片機(jī)蜂鳴器控制程序和驅(qū)動電路
10. 單片機(jī)實例練習(xí)與經(jīng)驗積累
10.3 單片機(jī)交通燈控制程序和設(shè)計原理
9.8 實用的 28BYJ-48 步進(jìn)電機(jī)控制程序
8.2 C 語言函數(shù)的調(diào)用
12.4 C 語言指向數(shù)組元素的指針
7.1 C 語言變量的作用域
11.2 RS232 通信接口
12.7 1602 液晶的讀寫時序介紹
7.2 C 語言變量的存儲類別
8. C 語言函數(shù)進(jìn)階與單片機(jī)按鍵
10.4 51單片機(jī) RAM 區(qū)域的劃分
12.1 C 語言變量的地址
11. UART 串口通信
7. 變量進(jìn)階與點(diǎn)陣 LED
8.4 單片機(jī)按鍵介紹
9.3 電機(jī)的分類
9.1 單片機(jī) IO 口的結(jié)構(gòu)
單片機(jī)通信實例與 ASCII 碼
8.1 單片機(jī)最小系統(tǒng)解析(電源、晶振和復(fù)位電路)
9.2 單片機(jī)上下拉電阻
11.4 單片機(jī) IO 口模擬 UART 串口通信
9.5 讓 28BYJ-48 步進(jìn)電機(jī)轉(zhuǎn)起來
9.7 28BYJ-48 步進(jìn)電機(jī)控制程序基礎(chǔ)
12.8 1602 液晶指令介紹
12.3 C 語言指針的簡單示例
8.7 單片機(jī)矩陣按鍵的掃描
7.4 單片機(jī) LED 點(diǎn)陣的圖形顯示
8.6 單片機(jī)按鍵消抖程序
10.2 單片機(jī)中 PWM 的原理與控制程序
7.6 單片機(jī) LED 點(diǎn)陣的橫向移動(動態(tài)顯示)
11.3 USB 轉(zhuǎn)串口通信
12.9 1602 液晶簡單顯示程序
9.4 28BYJ-48 步進(jìn)電機(jī)原理
8.5 ?單片機(jī)獨(dú)立按鍵掃描程序
12. C 語言指針基礎(chǔ)與1602液晶的初步認(rèn)識
9. 單片機(jī)中的步進(jìn)電機(jī)與蜂鳴器
10.1 單片機(jī)數(shù)字秒表程序
7.5 單片機(jī) LED 點(diǎn)陣的縱向移動(動態(tài)顯示)
8.8 單片機(jī)簡易加法計算器程序
11.1 單片機(jī)串行通信介紹
10.5 單片機(jī)長短按鍵的應(yīng)用
12.6 1602 液晶介紹(電路和引腳圖)
9.6 28BYJ-48 步進(jìn)電機(jī)轉(zhuǎn)動精度與深入分析

12.4 C 語言指向數(shù)組元素的指針

指向數(shù)組元素的指針和運(yùn)算法則

所謂指向數(shù)組元素的指針,其本質(zhì)還是變量的指針。因為數(shù)組中的每個元素,其實都可以直接看成是一個變量,所以指向數(shù)組元素的指針,也就是變量的指針。

指向數(shù)組元素的指針不難,但很常用。我們用程序來解釋會比較直觀一些。

unsigned char number[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
unsigned char *p;

如果我們寫 p = &number[0];那么指針 p 就指向了 number 的第0號元素,也就是把number[0]的地址賦值給了 p,同理,如果寫 p = &number[1];p 就指向了數(shù)組 number 的第1號元素。p = &number[x];其中 x 的取值范圍是0~9,就表示 p 指向了數(shù)組 number 的第 x 號元素。指針本身,也可以進(jìn)行幾種簡單的運(yùn)算,這幾種運(yùn)算對于數(shù)組元素的指針來說應(yīng)用最多。

  1. 比較運(yùn)算。比較的前提是兩個指針指向同種類型的對象,比如兩個指針變量 p 和 q 它們指向了具有同種數(shù)據(jù)類型的數(shù)組,那它們可以進(jìn)行 <,>,>=,<=,==等關(guān)系運(yùn)算。如果 p==q 為真的話,表示這兩個指針指向的是同一個元素。
  2. 指針和整數(shù)可以直接進(jìn)行加減運(yùn)算。比如還是上邊我們那個指針 p 和數(shù)組 number,如果 p = &number[0],那么 p+1 就指向了 number[1],p+9 就指向了 number[9]。當(dāng)然了,如果 p = &number[9],p-9 也就指向了 number[0]。
  3. 兩個指針變量在一定條件下可以進(jìn)行減法運(yùn)算。如 p = &number[0]; q = &number[9];那么 q-p 的結(jié)果就是9。但是這個地方大家要特別注意,這個9代表的是元素的個數(shù),而不是真正的地址差值。如果我們的 number 的變量類型是 unsigned int 型,占2個字節(jié),q-p 的結(jié)果依然是9,因為它代表的是數(shù)組元素的個數(shù)。

在數(shù)組元素指針這里還有一種情況,就是數(shù)組名字其實就代表了數(shù)組元素的首地址,也就是說:

p = &number[0];
p = number;

這兩種表達(dá)方式是等價的,因此以下幾種表達(dá)形式和內(nèi)容需要大家格外注意一下。

根據(jù)指針的運(yùn)算規(guī)則,p+x 代表的是 number[x]的地址,那么 number+x 代表的也是number[x]的地址。或者說,它們指向的都是 number 數(shù)組的第 x 號元素。

(p+x)和(number+x)都表示 number[x]。

指向數(shù)組元素的指針也可以表示成數(shù)組的形式,也就是說,允許指針變量帶下標(biāo),即 p[i]和 *(p+i)是等價的。但是為了避免混淆與規(guī)范起見,這里我們建議大家不要寫成前者,而一律采用后者的寫法。但如果看到別人那么寫,也知道是怎么回事即可。

二維數(shù)組元素的指針和一維數(shù)組類似,需要介紹的內(nèi)容不多。假如現(xiàn)在一個指針變量 p 和一個二維數(shù)組 number[3][4],它的地址的表達(dá)方式也就是 p=&number[0][0],有一個地方要注意,既然數(shù)組名代表了數(shù)組元素的首地址,那么也就是說 p 和 number 都是指數(shù)組的首地址。對二維數(shù)組來說,number[0],number[1],number[2]都可以看成是一維數(shù)組的數(shù)組名字,所以 number[0]等價于 &number[0][0], number[1]等價于 &number[1][0], number[2]等價于 &number[2][0]。加減運(yùn)算和一維數(shù)組是類似的,不再詳述。

指向數(shù)組元素指針的實例

在 C 語言里邊,sizeof()可以用來獲取括號內(nèi)的對象所占用的內(nèi)存字節(jié)數(shù),雖然它寫作函數(shù)的形式,但它并不是一個函數(shù),而是 C 語言的一個關(guān)鍵字,sizeof()整體在程序代碼中就相當(dāng)于一個常量,也就是說這個獲取操作是在程序編譯的時候進(jìn)行的,而不是在程序運(yùn)行的時候進(jìn)行。這是一個實際編程中很有用的關(guān)鍵字,靈活運(yùn)用它可以為程序帶來更好的可讀性、易維護(hù)性和可移植性,在后續(xù)的例程學(xué)習(xí)中將會慢慢有所體會的。

sizeof()括號中可以是變量名,也可以是變量類型名,其結(jié)果是等效的。而其更大的用處是與數(shù)組名搭配使用,這樣可以獲取整個數(shù)組占用的字節(jié)數(shù),就不用自己動手計算了,可以避免錯誤,而如果日后改變了數(shù)組的維數(shù)時,也不需要再到執(zhí)行代碼中逐個修改,便于程序的維護(hù)和移植。

下面我們提供了一個簡單的串口演示例程,可以體驗一下指針和 sizeof()的用法。例程首先接收上位機(jī)下發(fā)的命令,根據(jù)命令值分別把不同數(shù)組的數(shù)據(jù)回發(fā)給上位機(jī),程序還用到了指針的自增運(yùn)算,也就是+1 運(yùn)算,大家可以認(rèn)真考慮一下指針 ptrTxd 在串口發(fā)送的過程中的指向是如何變化的。在上位機(jī)串口調(diào)試助手中分別下發(fā)1、2、3、4,就會得到不同的數(shù)組回發(fā),注意這里都用十六進(jìn)制發(fā)送和十六進(jìn)制顯示。

此外,這個程序還應(yīng)用到一個小技巧,大家要學(xué)會使用。我們前邊講了串口發(fā)送中斷標(biāo)志位 TI 是硬件置位,軟件清零的。通常來講,我們想一次發(fā)送多個數(shù)據(jù)的時候,就需要把第一個字節(jié)寫入 SBUF,然后再等待發(fā)送中斷,在后續(xù)中斷中再發(fā)送剩余的數(shù)據(jù),這樣我們的數(shù)據(jù)發(fā)送過程就被拆分到了兩個地方——主循環(huán)內(nèi)和中斷服務(wù)函數(shù)內(nèi),無疑就使得程序結(jié)構(gòu)變得零散了。這個時候,為了使程序結(jié)構(gòu)盡量緊湊,在啟動發(fā)送的時候,不是向 SBUF 中寫入第一個待發(fā)的字節(jié),而是直接讓 TI=1,注意,這時候會馬上進(jìn)入串口中斷,因為中斷標(biāo)志位置1了,但是串口線上并沒有發(fā)送任何數(shù)據(jù),于是,我們所有的數(shù)據(jù)發(fā)送都可以在中斷中進(jìn)行,而不用再分為兩部分了。大家可以在程序中體會一下這個技巧的好處。

#include <reg52.h>
bit cmdArrived = 0; //命令到達(dá)標(biāo)志,即接收到上位機(jī)下發(fā)的命令
unsigned char cmdIndex = 0; //命令索引,即與上位機(jī)約定好的數(shù)組編號
unsigned char cntTxd = 0; //串口發(fā)送計數(shù)器
unsigned char *ptrTxd; //串口發(fā)送指針

unsigned char array1[1] = {1};
unsigned char array2[2] = {1,2};
unsigned char array3[4] = {1,2,3,4};
unsigned char array4[8] = {1,2,3,4,5,6,7,8};

void ConfigUART(unsigned int baud);

void main(){
    EA = 1; //開總中斷
    ConfigUART(9600); //配置波特率為9600

    while (1){
        if (cmdArrived){
            cmdArrived = 0;
            switch (cmdIndex){
                case 1:
                    ptrTxd = array1; //數(shù)組1的首地址賦值給發(fā)送指針
                    cntTxd = sizeof(array1); //數(shù)組1的長度賦值給發(fā)送計數(shù)器
                    TI = 1; //手動方式啟動發(fā)送中斷,處理數(shù)據(jù)發(fā)送
                    break;
                case 2:
                    ptrTxd = array2;
                    cntTxd = sizeof(array2);
                    TI = 1;
                    break;
                case 3:
                    ptrTxd = array3;
                    cntTxd = sizeof(array3);
                    TI = 1;
                    break;
                case 4:
                    ptrTxd = array4;
                    cntTxd = sizeof(array4);
                    TI = 1;
                    break;
                default:
                    break;
            }
        }
    }
}
/* 串口配置函數(shù),baud-通信波特率 */
void ConfigUART(unsigned int baud){
    SCON = 0x50; //配置串口為模式1
    TMOD &= 0x0F; //清零 T1 的控制位
    TMOD |= 0x20; //配置 T1 為模式2
    TH1 = 256 - (11059200/12/32)/baud; //計算 T1 重載值
    TL1 = TH1; //初值等于重載值
    ET1 = 0; //禁止 T1 中斷
    ES = 1; //使能串口中斷
    TR1 = 1; //啟動 T1
}
/* UART 中斷服務(wù)函數(shù) */
void InterruptUART() interrupt 4{
    if (RI){ //接收到字節(jié)
        RI = 0; //清零接收中斷標(biāo)志位
        cmdIndex = SBUF; //接收到的數(shù)據(jù)保存到命令索引中
        cmdArrived = 1;//設(shè)置命令到達(dá)標(biāo)志
    }
    if (TI){ //字節(jié)發(fā)送完畢
        TI = 0; //清零發(fā)送中斷標(biāo)志位
        if (cntTxd > 0){ //有待發(fā)送數(shù)據(jù)時,繼續(xù)發(fā)送后續(xù)字節(jié)
            SBUF = *ptrTxd; //發(fā)出指針指向的數(shù)據(jù)
            cntTxd--; //發(fā)送計數(shù)器遞減
            ptrTxd++; //發(fā)送指針遞增
        }
    }
}