C語言學習趣事_關於C語言中複雜類型定義

來源:互聯網
上載者:User

       說到C語言, 很多人都是又愛又恨啊,既感到用C語言給了程式員極大的開放度和自由度,同時又對C語言的靈活性和高難度性。

       就目前中國教育做法來說吧,估計大部分高校給學生選的入門級語言就是C語言, 然而經過大學幾年的學習,大部分的學生也只能做到寫個“HelloWord” 這樣的代碼。即便是電腦專業的畢業生,在離開學校後,大部分也是對C語言的掌握也只是停留在簡單的應用,更不用說非電腦專業的學生了, 就像我這樣非電腦專業畢業的,到現在也不會用C語言編寫一個具有實際應用意義的程式。

        估計C語言中最難讓人擺平的估計要算是指標了, 不但難以捉摸,同時又非常複雜。尤其是當具有複雜的資料類型定義的時候。

1、指標

     何謂指標,這個問題估計不需要說明了。從硬體角度來看,指標應該指的是CPU地址匯流排上呈現的電平狀態的數字化表示,估計大家都知道經典8051中的定址過程, 通過地址匯流排選擇需要操作的儲存地址;在8051中我們知道共有16根地址匯流排, 因此具有2^16方的可定址空間,就是具有64K的定址空間。當所有地址匯流排呈現低電平時選擇的是0000_0000_0000_0000,即0000H的地址;而當地址匯流排全部呈現高電平狀態則選擇的是1111_1111_1111_1111,即FFFFH的地址。這個同樣適合8086架構下的定址, 如果用組合語言編寫程式,就可以直接指定要操作的地址,或者說可以直接定址地址。

2、C語言中的指標

    C語言高效的一個原因就在於可以直接對地址進行操作,雖然不如組合語言那樣的直接,但是相對於其他一些語言例如VB等語言來說,C的指標操作已經非常“進階”了。C語言的發明者真夠神的, 發明了指標這樣難以駕馭的指標,但是C語言中指標的定義則非常的簡單。

3、C語言中指標類型定義

     定義文法:

     指標指向的基底類型   *  指標標識符

     例如:   int  * pValue;   這樣就定義了一個可以指向int類型變數的指標,哈哈,還真神奇,這樣就可以控制硬體的連線上的電平了,

4、指標用法

測試代碼:

Exp1:

#include <stdio.h>

#define  PINT int *

int max(int x, int y)
{
   return x>y ? x : y;
}

int main(int argc, char **argv)
{
  int pTest;
  PINT pToInt;
  int (*p)(int, int);
  pToInt=&pTest;
  pTest=100;
  p=max;
  printf("%d, %d, %d", *p,max,*pToInt);
  getch();
}

這個地方發現一個編譯上的差距: 在WinTC上編譯 *pToInt 輸出的是 7, 而在VC 6.0中輸出的則是預想的100; 為什麼呢? 目前沒有搞明白,哎..........

同時輸出的*p== max; 這段測試代碼用到了一個比較特殊的指標定義,函數指標, 從輸出來看與實際預想的一樣。如所示可以知道WinTC、VC6對地址的解釋不一致。唯一可以解釋的就是在Win32中地址是平坦的即flat的定址方式, 而在Win16或者Dos上就存在很多種定址方式,比方說tiny,small等方式造成這樣的,因為我在WinTC中測試的結果是 sizeof int = 2; 而在VC6.0中測試的結果是 sizeof int = 4,估計是因為這個原因導致上面的輸出不一致。

     在函數參數中使用指標:

      int main(int argc, char **argv)   // main函數的參數中就使用指標,並且是指標的指標。

      等價版本的main函數原型

      int  main(int argc,char *argv[])

      在函數參數中傳遞函數指標

       int get_max( int x, int y, int (* p)(int x,int y) )

       {

               return   (*p)(x,y);

        }

結合上面的程式可以實現以下程式碼:

#include <stdio.h>

#define PINT int *

int max(int x,int y)
{
    return x>y?x:y;
}
int get_max(int x, int y,int (*p)(int x, int y))
{
      return (*p)(x,y);
}
int main(int argc,char **argv)
{
    int (*p)(int,int),
        test;
    PINT pToInt;
    p=max;
    test=get_max(10,20,*p);
    pToInt=&test;

    printf("%d, %d, %d",*p,max,*pToInt);

    getchar();
}

 5、複雜指標定義:

指標常量和常量指標:

     int   const * p;  // 定義一個指向常量的指標,  這個指標可以隨意改變指向

     int   * const p; //定義一個指標常量, 這個指標只能指向一個變數,並且指向後不能在改變

     const int * const p; //定義一個指向常量的指標常量, 指標變數本身的值不可修改,並且指標指向的變數也不能被修改。

    例如:
    int * const pconst=&test;   //這裡定義的pconst指標就不能再指向別的整型變數
    const int constvalue=50;    //定義一個整型常量, 等價於   int const constvalue
    int const *constvar=&constvalue;   //定義一個指向整型常量的指標, 指標指向的變數值不可修改,但是指標指向可以更改
    const int * const constpvar=&constvalue;  //定義一個指向整型常量的指標, 指標的指向不可修改。

    這類指標定義的一個簡單的閱讀方法就看: const修飾的是什麼,  當其修飾資料類型的時候則定義的是資料類型的變數不能修改;

                                                           當修飾的是指標變數的時候則指標的指向不可改變。

    從上面的執行個體可以看出: 當沒有指標存在的時候,const 的位置不會影響變數的使用,但是也可以根據其修飾的對象來理解。

指標數組和數組指標

    int  *p[];

    int   (*p)[]; 

    這兩類指標的定義著實非常令人糾結啊,  到底怎麼解釋和理解呢? 一團霧水啊...........

    首先看第一個: int  *p[ ];

    如上定義:  p 的左右各有一個運算子, *和[]; 指標運算子和數組運算子, 在C規範裡面, 數組運算子的優先順序別高於指標運算子。

    在這裡同樣可以利用運算子的優先順序來理解這個指標, 

    因為[ ]的優先順序高於 * ,引起p應該先和[ ]結合,這裡就是可以看出,p是一個數組, 然後p再與* 結合,可以看出p是一個指標,最後看資料基底類型,p的資料基底類型是int型的;綜合上面的描述可以知道: p被定義為一個儲存int型指標的數組。 即p是一個數組,其數組元素的類型是int型指標。

   例如:  int  px;

             int   py;

             int   pz;

             int *p[3];

        則可以有:

            p[0]=&px;

            p[1]=&py;

            p[2]=&pz;

       如果要引用其指向的變數的內容的話,可以這樣使用:   int  sizex=*p[0];   這就是指標數組, 就是說數組元素全是指標。

       接下來看第二個:

       int (*p)[];

       同樣可以利用優先順序別來理解:  ()和[ ]具有相同的優先順序, 因此 p 是一個指標, 然後再用 [ ]來修飾p; 則可以看出p將指向一個數群組類型資料,這就是說 int (*p)[]是指向int型數組的指標。這個指標不能指向別的數組。

       例如:

       int  iArray[4][5];
       int  (*pArray)[5];
       pArray=iArray;    //這樣可以編譯成功, 因為pArray的類型是 :  int (*) [] ;  而 iArray 的類型是  int [4][5]; 可以進行資料類型的轉換

       但是如果:

       pArray=&iArray;  //編譯不成功,為什麼呢? 因為pArray的類型是 :  int (*) [] ;  而&iArray 的類型是  int *[][];很顯然資料類型不一樣

      如果:

      pArray=&iArray[0];  // 編譯成功。

       我們知道在二維數組中, 可以這樣理解:其行元素相當於指標,即 iArray[0]、 iArray[1]、 iArray[2]、iArray[3], 但是其儲存的並不是指標,其儲存的是一個具有5個元素數組的首地址。但是需要這樣才能 iArray=&iArray[0];  (這裡二維數組的首地址與 iArray[0] 的地址相同 )。

     對於數組指標的理解,可以將變數去除後然後進行剝離得出其資料類型然後進行理解。例如:

     pArray: int (*)[];

     &iArray[0]: int (*)[ ];

      特殊的引用方式:

     int  iArray[4];
     int  (*pArray)[4]=NULL;    //指定義不初始化同樣可以,但是為了防止出現游離指標,最好用NULL初始化;
     pArray=&iArray;     // 這個編譯成功 

指向函數的數組指標

     int (*a[10])(int); // 這個定義一個數組,數組共有10個元素,每個元素儲存一個指向 int (*)(int )函數的指標, 同樣利用優先順序來理解。

指標的指標   

       int **pToPoint;

       int *pToInt;

       int  age;

       pToAge=&age;

       PToPoint=&pToAge;

       通常這個應用於數組和字串的處理, 可以見兩種main函數原型的定義。

       哎..........雖然寫了這麼多但是對於這個C語言中的指標認識還是不夠,尤其是數組指標那一塊,估計需要慢慢琢磨才能真正的理解,

       同時對於 **P和*p[]之間差別可以參照:數組地址和指標進行複合運算 。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.