溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

C語(yǔ)言小知識(shí)之使用指針原因是什么

發(fā)布時(shí)間:2021-10-15 15:43:58 來源:億速云 閱讀:127 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要講解了“C語(yǔ)言小知識(shí)之使用指針原因是什么”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“C語(yǔ)言小知識(shí)之使用指針原因是什么”吧!

首先從一個(gè)簡(jiǎn)單的例子來看,寫一段代碼來交換x、y的值。

void main( void )
{
    u8 x = 10, y = 20;
    u8 temp;

    __asm( "sim" );                             //禁止中斷
    SysClkInit();
    delay_init( 16 );
    LED_GPIO_Init();
    Uart1_IO_Init();
    Uart1_Init( 9600 );
    ADC_GPIO_Init();
    __asm( "rim" );                             //開啟中斷
    while( 1 )
    {
        LED = ~LED;

        printf( "x = %d,y = %d\r\n", x, y );

        temp = x;
        x = y;
        y = temp;

        printf( "---> x = %d,y = %d \r\n\r\n\r\n", x, y );

        delay_ms( 200 );
    }
}

在STM8單片機(jī)中,交換x、y的值,并將值打印出來,打印結(jié)果如下:

C語(yǔ)言小知識(shí)之使用指針原因是什么

通過第三個(gè)變量temp很輕松的就將x、y的值交換了。

但是為了程序的美觀性,不想再主程序中寫這么多的代碼,于是決定用一個(gè)函數(shù)在外部來實(shí)現(xiàn)這個(gè)功能。

void swap( u8 x, u8 y )
{
    u8 temp;
    temp = x;
    x = y;
    y = temp;
}

void main( void )
{
    u8 x = 10, y = 20;
    __asm( "sim" );                             //禁止中斷
    SysClkInit();
    delay_init( 16 );
    LED_GPIO_Init();
    Uart1_IO_Init();
    Uart1_Init( 9600 );
    ADC_GPIO_Init();
    __asm( "rim" );                             //開啟中斷
    while( 1 )
    {
        LED = ~LED;

        printf( "x = %d,y = %d\r\n", x, y );
        sawp( x, y );
        printf( "---> x = %d,y = %d \r\n\r\n\r\n", x, y );

        delay_ms( 200 );
    }
}

在主函數(shù)外面定義一個(gè)函數(shù),專門用來交換x,y的值。程序運(yùn)行結(jié)果如下:

C語(yǔ)言小知識(shí)之使用指針原因是什么

此時(shí)發(fā)現(xiàn)x和y的值并沒有交換,這是怎么回事?那么在交換函數(shù)內(nèi)部,也將x和y的值打印出來。修改代碼如下:

void swap( u8 x, u8 y )
{
    u8 temp;
    
    printf( "in: x = %d,y = %d\r\n", x, y );
    temp = x;
    x = y;
    y = temp;
    
    printf( "in: ---> x = %d,y = %d \r\n\r\n\r\n", x, y );
}

打印結(jié)果如下:

C語(yǔ)言小知識(shí)之使用指針原因是什么

可以看到在交換函數(shù)內(nèi)部,x和y的值已經(jīng)交換了,但是在函數(shù)外部,x和y的值并沒有交換。這是為什么呢?單步調(diào)試直接觀察x和y在單片機(jī)內(nèi)部分存儲(chǔ)情況。

C語(yǔ)言小知識(shí)之使用指針原因是什么

進(jìn)入主程序之后,首先觀察主函數(shù)內(nèi)的x和y在內(nèi)存中的存儲(chǔ)情況,x值為10,也就是16進(jìn)制的0x0A,在內(nèi)存中0x000009的位置存儲(chǔ),y的值為20,也就是16進(jìn)制的0x14,在內(nèi)存中0x00000B,位置存儲(chǔ)。

接下里進(jìn)入到swap函數(shù)中。為了方便觀察,將swap函數(shù)內(nèi)部的x和y替換成了m和n。

C語(yǔ)言小知識(shí)之使用指針原因是什么

可以看出在進(jìn)入子函數(shù)之后,m和n的地址和值,都和main函數(shù)中的x和y一樣。接下來交換m和n的值。

C語(yǔ)言小知識(shí)之使用指針原因是什么

交換完成后發(fā)現(xiàn),內(nèi)存中0x000009位置的值和0x00000B位置的值亞發(fā)生了交換。然后退出子函數(shù),返回到main函數(shù)中。

C語(yǔ)言小知識(shí)之使用指針原因是什么

這時(shí)候奇怪的事情發(fā)生了,剛才內(nèi)存中交換的值又變回來了?這是為什么呢?在這里就不得不說在C語(yǔ)言中關(guān)于局部變量的問題,局部變量在C語(yǔ)言中是沒有固定的存儲(chǔ)位置的,它是由系統(tǒng)的堆棧統(tǒng)一來管理的,當(dāng)進(jìn)入函數(shù)內(nèi)部時(shí),系統(tǒng)就會(huì)給這些局部變量臨時(shí)分配一個(gè)存儲(chǔ)空間存儲(chǔ)它的值,當(dāng)程序要離開函數(shù)時(shí),系統(tǒng)就會(huì)將局部變量的值保存在堆棧中,然后變量的存儲(chǔ)位置就被釋放了。當(dāng)程序進(jìn)入另一個(gè)函數(shù)中時(shí),又會(huì)給這個(gè)函數(shù)內(nèi)部局部變量分配空間,這時(shí)候可能就會(huì)出現(xiàn)兩個(gè)函數(shù)中的局部變量都使用了同一個(gè)內(nèi)存空間。此時(shí)這個(gè)內(nèi)存空間的值改變后,并不影響上一個(gè)函數(shù)中局部變量的值,因?yàn)樯弦粋€(gè)函數(shù)中的局部變量值此時(shí)在堆棧中存放。當(dāng)程序要離開當(dāng)前的這個(gè)函數(shù)時(shí),又會(huì)將當(dāng)前的局部變量值保存在堆棧中?;氐缴弦粋€(gè)函數(shù)后,又將堆棧中存儲(chǔ)的值恢復(fù)給變量,此時(shí)變量的地址又是臨時(shí)申請(qǐng)的,可能此刻申請(qǐng)的地址值還是和上一次一樣。但是并不代表這個(gè)地址就永遠(yuǎn)屬于這個(gè)局部變量。

這個(gè)就很類似于超市中的儲(chǔ)物柜,你要進(jìn)去超市買東西,先將東西存到一個(gè)柜子中,買完?yáng)|西后,又將東西存儲(chǔ)物柜取了出來。然后隔了幾個(gè)小時(shí),又要去這個(gè)超市買東西,又需要將東西存起來,但是此時(shí)存儲(chǔ)的柜子編號(hào)還是上上一次存儲(chǔ)時(shí)一樣。但是這并不能代表這個(gè)柜子的編號(hào)就是專屬于你的了。它只是儲(chǔ)物柜臨時(shí)分配給你的空間,當(dāng)你取出東西后這個(gè)空間就會(huì)被系統(tǒng)收回,如果你下一次還需要用,系統(tǒng)又會(huì)自動(dòng)給你分配,但是這兩次分配的剛好是一個(gè)編號(hào)而已。

那么要如何解決這種變量交換的問題呢?有兩種方法,第一種就是直接將要交換的這個(gè)兩個(gè)變量定義為全局變量,讓它在程序運(yùn)行的過程中獨(dú)占一個(gè)地址空間,這樣就不會(huì)有其他變量來使用這個(gè)位置了。但是這樣的話就會(huì)比較浪費(fèi)內(nèi)存空間,只使用了一次,但是卻要永久的占用。第二種方法就是直接使用指針。

下面將代碼改成使用指針的方式。

void swap( u8 *m, u8 *n )
{
    u8 temp;   
    temp = *m;
    *m = *n;
    *n = temp;    
}
void main( void )
{
    u8 x = 10, y = 20;
    __asm( "sim" );                             //禁止中斷
    SysClkInit();
    delay_init( 16 );
    LED_GPIO_Init();
    Uart1_IO_Init();
    Uart1_Init( 9600 );
    ADC_GPIO_Init();
    __asm( "rim" );                             //開啟中斷
    while( 1 )
    {
        LED = ~LED;

        printf( "x = %d,y = %d\r\n", x, y );
        sawp( &x, &y );
        printf( "---> x = %d,y = %d \r\n\r\n\r\n", x, y );

        delay_ms( 200 );
    }
}

在向swap函數(shù)傳遞參數(shù)的時(shí)候需要使用&符號(hào)。打印輸出結(jié)果

C語(yǔ)言小知識(shí)之使用指針原因是什么

此時(shí)x和y的值已經(jīng)成功交換了。現(xiàn)在也將子函數(shù)內(nèi)部的交換情況打印一下。

void swap( u8 *m, u8 *n )
{
    u8 temp;
    
    printf( "in: m = %d,n = %d\r\n", m, n );
    temp = *m;
    *m = *n;
    *n = temp;
    
    printf( "in: ---> m = %d,n = %d \r\n\r\n\r\n", m, n );
}

C語(yǔ)言小知識(shí)之使用指針原因是什么

從輸出的結(jié)果來看,怎么m和n的值一個(gè)時(shí)1021,一個(gè)時(shí)1020.這個(gè)值是什么呢?直接單步調(diào)試看。

C語(yǔ)言小知識(shí)之使用指針原因是什么

此時(shí)main函數(shù)中x的地址變成了0x0003FD,y的地址變成了0x0003FC.接著進(jìn)入子函數(shù)。

C語(yǔ)言小知識(shí)之使用指針原因是什么

這時(shí)可以看出m的值為0x03FD,n的值為0x03FC.*m的值為0x0A也是就10,*0x14也就是20.
接下來開始交換值。

C語(yǔ)言小知識(shí)之使用指針原因是什么

可以看出m和n的值沒變,但是m和n的值交換了。接下來回到主函數(shù)中。

C語(yǔ)言小知識(shí)之使用指針原因是什么

此時(shí)主函數(shù)中x和y的值也交換了。

那剛才串口打印出來的1021和1020是什么呢?1021的十六進(jìn)制是0x03FD,1020的十六進(jìn)制是0x03FC.也就是說剛才打印的m的值和x的地址一樣,n的值和y的地址一樣。

那為什么m和n會(huì)變成x和y地址,*m 和 *n又會(huì)變成 x 和 y 的值。這里就要說指針的本質(zhì)了。在存儲(chǔ)器內(nèi)部,它是不認(rèn)識(shí)什么變量和指針的,對(duì)于存儲(chǔ)空間來說,它只有地址和值。也就是說在什么地址處,存儲(chǔ)什么值。對(duì)于普通的變量來說,變量的名稱就會(huì)被編譯器編譯成地址,也就是說x和y就是它自己地址的別名。x和y的值就是地址中對(duì)應(yīng)的值。

當(dāng)操作普通變量x和y的時(shí)候,系統(tǒng)默認(rèn)操作的就是它的值。而指針剛好和它相反,指針默認(rèn)是把地址作為它的值,當(dāng)操作指針的時(shí)候,默認(rèn)操作的就是地址。為了將普通變量和指針進(jìn)行區(qū)分,那么如果要使用指針的時(shí)候,就需要給它貼一個(gè)標(biāo)簽,告訴系統(tǒng),我這個(gè)是特殊變量,它是直接操作地址的,不是操作值的。

所以在定義指針的時(shí)候給變量前面加一個(gè)*號(hào),就表示告訴系統(tǒng),我這個(gè)是特殊的。比如定義了一個(gè)int *m。就表示告訴系統(tǒng),當(dāng)我默認(rèn)操作m的時(shí)候,你就給我它的地址,而不要給我它的值。當(dāng)需要取值的時(shí)候就需要添加上標(biāo)簽 *m,告訴系統(tǒng)我現(xiàn)在要取的值,不是地址,這是特殊情況,不要把默認(rèn)的地址給我。

當(dāng)要將普通變量傳遞給指針時(shí),因?yàn)橹苯硬僮髯兞磕J(rèn)就是普通變量的值,而指針存貯的是地址,所以當(dāng)普通變量和指針傳遞數(shù)據(jù)的時(shí)候,也要給普通變量添加一個(gè)標(biāo)簽 & ,這個(gè)符號(hào)就告訴系統(tǒng),我現(xiàn)在不要默認(rèn)的值,我要的是特殊情況的地址。

所以將x和y傳遞給指針的時(shí)候,前面要加&符號(hào)。

swap( &x, &y );對(duì)應(yīng)的就是 swap( u8 *m, u8 *n );

剛才上面不是說了嗎,指針默認(rèn)的是地址,加上*號(hào)就是值了。那么這樣直接傳遞過去不就是相當(dāng)于 *m = &x 了嗎?

由于這個(gè)子函數(shù)是定義和傳值在一起操作了,省略了一步,標(biāo)準(zhǔn)操作應(yīng)該是。

int *m;

m=&x;

先定義一個(gè)指針,然后將普通變量的特殊情況,也就是取普通變量的地址,傳遞給指針的默認(rèn)情況。這樣m的默認(rèn)情況下就代表的值x的地址,而x的值就是*m。

如果定義變量和給變量賦值在一條語(yǔ)句時(shí),上面的代碼就可以簡(jiǎn)寫為

int *m = &x;

所以上面的函數(shù) swap( &x, &y ); 給指針傳遞值的時(shí)候,指針的定義和賦值是在一條語(yǔ)句完成的, swap( u8 *m, u8 *n ); 這是一種常用的簡(jiǎn)寫形式。

在swap函數(shù)內(nèi)部操作 *m 也就相當(dāng)于直接操作的是x,操作 *n 就是直接操作的y,所以交換 *m 和 *n的值,就相當(dāng)于交換x和y的值。

在沒有使用指針時(shí)通過子函數(shù)交換,此時(shí)傳遞的是變量的值,相當(dāng)于把變量的值拷貝了一份,給了子程序。

而有了指針之后,相當(dāng)于將變量的地址直接給了子函數(shù)。相當(dāng)于給變量x和y又起了一個(gè)別名。操作別名的時(shí)候,也就相當(dāng)于直接操作的是x。

由此可見,指針只是為了方便編寫程序而設(shè)置的一種給變量起別名的方法,也就是相當(dāng)于給自己柜子配了了一把鑰匙。只要?jiǎng)e人有這個(gè)鑰匙,也就可以打開你的柜子。所以指針在使用的時(shí)候會(huì)有危險(xiǎn)性。

如果系統(tǒng)中有關(guān)鍵數(shù)據(jù),那么如果這個(gè)數(shù)據(jù)用指針傳遞給了外部函數(shù),那么當(dāng)外部函數(shù)修改數(shù)據(jù)的時(shí)候,系統(tǒng)就會(huì)存在風(fēng)險(xiǎn)。有可能外部函數(shù)修改了一個(gè)值,而這個(gè)值是非法的,自己的系統(tǒng)就奔潰了。所以在使用指針的時(shí)候一定要注意安全性問題。

通過上面的例子,相信對(duì)指針就有了更深層次的理解了。它是為了方便操作變量而設(shè)置的特殊情況,是被貼了標(biāo)簽的變量。至于什么指針變量,變量指針,那都是起的名字而已,搞不清楚這些概念也不用去糾結(jié),只要在使用的時(shí)候,知道如何使用就行了。

感謝各位的閱讀,以上就是“C語(yǔ)言小知識(shí)之使用指針原因是什么”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)C語(yǔ)言小知識(shí)之使用指針原因是什么這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI