關於指針的一點摘要

1。一點基礎
      int *p = NULL;
      定義了 p是一個指針。
       p這個指針的步長是4,也就是 ++p後,p的內存地址會增加4個byte。
       p的初始值是NULL,NULL的定義一般是(void *)0或者0.
       如果p是全局變量,那麼內存單元在link時分配在靜態存儲區;如果是局部變量,那麼內存單元在運行時分配在stack上。
      定義指針時,編譯器並不爲指針所指向的對象分配空間,它只是分配指針本身的空間,除非在定義時同時賦給指針一個字符串常量進行初始化。
      eg.
      
char *p = "breadfruit";
  注意只有對字符串常量纔是如此。另外,在ANSI C中,初始化指針時所創建的字符串常量被定義爲只讀。如果試圖通過指針修改這個字符串的值,程序就
  會出現未定義的行爲。該字符串常量,一般存放在數據段,但在有些編譯器中,存放在文本段,以防止它被修改。
  與指針相反,由字符串常量初始化的數組是可以被修改的。
  [Parts are referred from <<C Expert Programming>>]

 2。常見用法
      (1). 訪問內存數據。
       從底層的觀點來看,一個指針就是一個內存地址。
      (2). 動態分配時,儲存分配出的內存地址。
       eg.
      
char *p = NULL;
       p = (char *)malloc(MAX_MEM_SIZE);
  (3). 在函數中作爲參數傳入或傳出,在函數內訪問傳入數據
   eg.
   
    int gid_name(char *name, gid_t *gid)
       {
           [...]
           *gid = ptr->gid = gr->gr_gid;
           return (0);
       }
       如上函數,返回值只用以標記錯誤條件。
       使用指針類型參數有時也是爲了避免函數調用時的大量數據copy,比如結構體,這時使用指針會更有效率。
       eg.
       static double
diffsec(struct timeval *now, struct timeval *then)
       {
           return ((now->tv_sec  -  then->tv_sec)*1.0 + (now->tv_usec  -  then->tv_usec) / 1000000.0);
       }
       函數中通過指針傳入的兩個結構體的成員值計算結果。
       像上面這種情況,當通過指針傳入的參數不需被修改時,最後在前面添上const關鍵字,表示在函數作用域內,該參數只讀。
      (4). 數據成員訪問
       訪問結構體的成員:p->f;
       訪問數組元素:
       
 p = &a[0];
        *(p + 3) = 0;
      (5). 替代數組作爲函數參數
       在C中,當數組名作爲函數參數時,傳入的實際上是這個數組的第一個元素的地址, 即,注意當數組作爲函數的參數進行傳遞時,該數組自動退化爲同類型的指針。這時,函數中任何對數組的改動將直接影響到該數組本身。
       同樣,C函數中return的數組名也會只返回一個指向該數組第一個元素的指針。所以,當從函數中返回一個數組時,要確保該數組不是從函數中定義的
      局部變量,局部變量內存分配在棧上,否則,一旦函數退出,這個數組的內容會被覆蓋,返回的數據會因此無效。避免這種情況的一個方法是將該數組定義爲static。
       eg.
      
char *inet_ntoa(struct in_addr ad)
       {
         unsigned long int s_ad;
         int a, b, c, d;
         static char addr[20];
 
         s_ad = ad.s_addr;
         d = s_ad % 256;
         s_ad /= 256;
         c = s_ad % 256;
         s_ad /= 256;
         b = s_ad % 256;
         a = s_ad / 256;
         sprintf(addr, "%d.%d.%d.%d", a, b, c, d);
         return addr;
       }
      如上函數,如果不把數組addr聲明爲static,當函數inet_ntoa() return時,addr數組的內容會無效。
      (6). 函數指針
           C不允許函數作爲參數傳入其他函數中,但允許傳遞一個函數指針給另一個函數。
      (7). 作爲別名
           eg.
           struct
output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0};
           [...]
           struct
output *out1 = &output;
       在上例中,指針變量out1可以用在使用變量output的地方。
       一般這樣使用有三個原因:
       i. 提高效率
        如果一個數據結構比較大,那麼在某些場合,比如賦值語句中使用指針會避免對整個結構體的拷貝。
      
  static struct termios cbreakt, rawt, *curt;
        [...]
        curt = useraw ? &rawt : &cbreakt;
       ii. 訪問static的數據
        一個指針常用來指向不同的靜態數據, 
        eg.
        char *s;
        [...]
        s = *(opt->bval) ? "True" : "False";
       iii. 在不同的函數中訪問同一個全局數據
        eg.
        static long rntb[32] = 
        {
            3, 0x9a319039, 0x32d9c024, 0x9b663182, 0x5da1f342,
            [...]
        };
 
        static long *fptr = &rntb[4];
        [...]
 
        void srrandom(int x)
        {
            [...]
            fptr = &state[rand_sep];
            [...]
        }
 
        long rrandom()
        {
            [...]
            *fptr += *rptr;
            i = (*fptr >> 1) & 0x7fffffff;
        }
        本例中,fptr被初始化指向一個隨機數種子表,然後在函數srrandom()中再次被賦值,最後,在rrandom函數中被用來修改所指向的數據。
 
      (8). 處理string, 如strlen函數:
       size_t strlen(const char *str)
       {
           register const char *s;
 
           for (s = str; *s; ++s) ;
           return(s - str);
       }
      (9). 直接內存訪問
       既然指針本質上表現爲一個內存地址,那麼在底層代碼中會使用指針去訪問特定硬件的內存地址。
       static void vid_wrchar(char c)
       {
           volatile unsigned short *video = NULL;
 
           video = (unsigned short *)(0xe08b8000) +
            vid_ypos * 80 + vid_xpos;

           *video = (*video & 0xff00) | 0x0f00 | (unsigned short)c;
       }
   注意,現代操作系統一般不允許直接硬件訪問,所以一般這樣的代碼只會出現在嵌入式系統或者內核和驅動程序中。
 
     3。 使用規則建議
      這些規則主要是爲了防止內存泄漏的
      (1). 用malloc申請內存之後,應該立即檢查指針值是否爲NULL。防止使用指針值爲NULL 的內存。
       char *p = (char *)malloc(USER_DEFINE_SIZE);
       if (NULL == p)
       {
            /* Exception Handle */
       }
      (2). 爲數組和動態內存賦初值。防止將未被初始化的內存作爲右值使用。
       For Array:
       int aVar[100] = { 0 };
 
       For Dynamic allocate memory:
       char *p = NULL; /* Initial */
       p = (char *)malloc(USER_DEFINE_SIZE); /* Malloc buffer */
       if (NULL == p)
       {
            /* Exception Handle */
       }
       memset(p, 0, USER_DEFINE_SIZE);
      (3). 避免數組或指針的下標越界,特別要當心發生“多1”或者“少1”操作。
      (4). 動態內存的申請與釋放必須配對,防止內存泄漏。
       For free allocate memory:
       if (NULL != p)
       {
            free(p);
       }
      (5). 用free釋放了內存之後,立即將指針設置爲NULL,防止產生“野指針”。
      [Reference from <<High Quality C/C++ Programming Guide>>]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章