C指針難點解析

序言

學習 C 語言的指針既簡單又有趣。通過指針,可以簡化一些 C 編程任務的執行,還有一些任務,如動態內存分配,沒有指針是無法執行的。所以,想要成爲一名優秀的 C 程序員,學習指針是很有必要的。

內存地址

在瞭解c指針之前,我們先來了解下內存地址。計算機程序運行時的數據一般保存在內存(ram)中,爲了存取內存中的數據,系統會對內存進行編址,然後程序就可以按地址存取數據。c語言中,每一個變量都有一個內存位置,每一個內存位置都定義了可使用連字號(&)運算符訪問的地址,它表示了在內存中的一個地址。請看下面的實例,它將輸出定義的變量地址:

#include <stdio.h>
int main ()
{
   int  var1;
   printf("var1 變量的地址: %p\n", &var1  );
   return 0;
}

運行結果是:var1 變量的地址: 0x7fff5cc109d4

什麼是指針?

指針是一個變量,其值爲另一個變量的地址,即指針變量的值就是上面說的內存地址。所有指針的值的實際數據類型,不管是整型、浮點型、字符型,還是其他的數據類型,都是一樣的,都是一個代表內存地址的長的十六進制數。訪問指針變量中可用地址的值是通過使用一元運算符 *來返回位於操作數所指定地址的變量的值。

#include <stdio.h>
int main ()
{
   int  var = 20;   /* 實際變量的聲明 */
   int  *ip;        /* 指針變量的聲明 */
   ip = &var;  /* 在指針變量中存儲 var 的地址 */
   printf("Address of var variable: %p\n", &var  );
   /* 在指針變量中存儲的地址 */
   printf("Address stored in ip variable: %p\n", ip );
   /* 使用指針訪問值 */
   printf("Value of *ip variable: %d\n", *ip );
   return 0;
}

運行結果是:

Address of var variable: bffd8b3c
Address stored in ip variable: bffd8b3c
Value of *ip variable: 20

指向指針的指針

現在我們瞭解了內存地址、指針以及它們之間的相互關係之後,下面看看指針當中的一些難點:指向指針的指針。指向指針的指針是一種多級間接尋址的形式,或者說是一個指針鏈。通常,一個指針包含一個變量的地址。當我們定義一個指向指針的指針時,第一個指針包含了第二個指針的地址,第二個指針指向包含實際值的地址。

C 中æåæéçæé

示例如下:

#include <stdio.h>
int main ()
{
   int  var;
   int  *ptr;
   int  **pptr;
   var = 3000;
   /* 獲取 var 的地址 */
   ptr = &var;
   /* 使用運算符 & 獲取 ptr 的地址 */
   pptr = &ptr;
   /* 使用 pptr 獲取值 */
   printf("Value of var = %d\n", var );
   printf("Value available at *ptr = %d\n", *ptr );
   printf("Value available at **pptr = %d\n", **pptr);
   return 0;
}

運行結果是:

Value of var = 3000
Value available at *ptr = 3000
Value available at **pptr = 3000

傳遞指針給函數

C 語言允許傳遞指針給函數,只需要簡單地聲明函數參數爲指針類型即可。例如:

#include <stdio.h>
#include <time.h>
void getSeconds(unsigned long *par);
int main ()
{
   unsigned long sec;
   getSeconds( &sec );
   /* 輸出實際值 */
   printf("Number of seconds: %ld\n", sec );
   return 0;
}

void getSeconds(unsigned long *par)
{
   /* 獲取當前的秒數 */
   *par = time( NULL );
   return;
}

運行結果是:Number of seconds :129445046

從函數返回指針

C 允許您從函數返回指針。爲了做到這點,您必須聲明一個返回指針的函數。另外,C 語言不支持在調用函數時返回局部變量的地址,除非定義局部變量爲 static 變量。現在,讓我們來看下面的函數,它會生成 10 個隨機數,並使用表示指針的數組名(即第一個數組元素的地址)來返回它們,具體如下:

#include <stdio.h>
#include <time.h>
#include <stdlib.h> 
/* 要生成和返回隨機數的函數 */
int * getRandom( )
{
   static int  r[10];
   int i;
   /* 設置種子 */
   srand( (unsigned)time( NULL ) );
   for ( i = 0; i < 10; ++i)
   {
      r[i] = rand();
      printf("%d\n", r[i] );
   }
   return r;
}
 
/* 要調用上面定義函數的主函數 */
int main ()
{
   /* 一個指向整數的指針 */
   int *p;
   int i;
   p = getRandom();
   for ( i = 0; i < 10; i++ )
   {
       printf("*(p + [%d]) : %d\n", i, *(p + i) );
   }
   return 0;
}

運行結果是:

1523198053
1187214107
1108300978
430494959
1421301276
930971084
123250484
106932140
1604461820
149169022
*(p + [0]) : 1523198053
*(p + [1]) : 1187214107
*(p + [2]) : 1108300978
*(p + [3]) : 430494959
*(p + [4]) : 1421301276
*(p + [5]) : 930971084
*(p + [6]) : 123250484
*(p + [7]) : 106932140
*(p + [8]) : 1604461820
*(p + [9]) : 149169022

函數指針

函數指針是指向函數的指針變量。通常我們說的指針變量是指向一個整型、字符型或數組等變量,而函數指針是指向函數。函數指針可以像一般函數一樣,用於調用函數、傳遞參數。

函數指針變量的聲明:

typedef int (*fun_ptr)(int,int); // 聲明一個指向參數和返回值都爲int的函數指針類型

以下實例聲明瞭函數指針變量 p,指向函數 max:

#include <stdio.h>
int max(int x, int y)
{
    return x > y ? x : y;
}
int main(void)
{
    /* p 是函數指針 */
    int (* p)(int, int) = & max; // &可以省略,這裏和一般的整型指針有點不一樣,賦值整型指針必須有&符號
    int a, b, c, d;
    printf("請輸入三個數字:");
    scanf("%d %d %d", & a, & b, & c);
    /* 與直接調用函數等價,d = max(max(a, b), c) */
    d = p(p(a, b), c); 
    printf("最大的數字是: %d\n", d);
    return 0;
}

 運行結果是:

請輸入三個數字:1 2 3
最大的數字是: 3

函數指針變量可以作爲某個函數的參數來使用的,回調函數就是一個通過函數指針調用的函數。簡單講:回調函數是由別人的函數執行時調用你實現的函數。示例:

實例中 populate_array 函數定義了三個參數,其中第三個參數是函數的指針,通過該函數來設置數組的值。實例中我們定義了回調函數 getNextRandomValue,它返回一個隨機值,它作爲一個函數指針傳遞給 populate_array 函數。populate_array 將調用 10 次回調函數,並將回調函數的返回值賦值給數組。

#include <stdlib.h>  
#include <stdio.h>
// 回調函數
void populate_array(int *array, size_t arraySize, int (*getNextValue)(void))
{
    for (size_t i=0; i<arraySize; i++)
        array[i] = getNextValue();
}
// 獲取隨機值
int getNextRandomValue(void)
{
    return rand();
}
int main(void)
{
    int myarray[10];
    populate_array(myarray, 10, getNextRandomValue);
    for(int i = 0; i < 10; i++) {
        printf("%d ", myarray[i]);
    }
    printf("\n");
    return 0;
}

運行結果是:

16807 282475249 1622650073 984943658 1144108930 470211272 101027544 1457850878 1458777923 2007237709 

總結:

  1. 指針指向變量的地址,即指針的值是另一個變量的地址。
  2. 指針也是變量,所以指針它也有自己的地址,指針自己的地址和它指向的地址是兩個不一樣的地址。
  3. 獲取指針指向的地址不需要帶*號。
  4. 獲取指針指向的地址所存取的值需要帶*號。
  5. 可以有多級指針。
  6. 調用函數指針不需要帶*號,和一般的基類指針操作不一樣。
  7. 函數指針是指向函數的指針變量,函數指針可以作爲某個函數的參數。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章