天天看點

C 語言小結( 自我總結,僅供參考 )

1./(★)/可以通過指針來改變int a 中 a 的值;因為指針指是對其位址的操作... ...(很重要!!!)

2.(★)函數名和數組名一樣都是 常量指針 ,隻能指向唯一的記憶體

3.c語言中 static uint8ucState=0類型為占8個bit的無符号整型的靜态變量ucState,賦初值為0。

(附加:誤區:const 是常量 而stastic是靜态變量  是不一樣的;前者修飾的量不能改變;後者是累加)

(附加:在給數組初始化時應當要注意的:例如: int num[100] = { 0 }; 也就是指派所有的元素為 0;但是

(★)(★)  如果是這個樣子: int num[100] = { 1 }; 結果是num[0] =1; 其餘元素為 0  )(★)(★)(★)

4.(★)

         也就是說隻要涉及到要用指針來儲存值時先必須配置設定記憶體!!       

        !!!

       如果僅僅是聲明一個指針*p,然後讓他指向一個struct x;即p=&x;此時無需配置設定記憶體                                          

5.數組之間的指派需要用到strcpy()函數,但是一旦轉化為指針,就可以直接是p1=p2;

6.char **和const char **事不一樣的,前者指向char *,後者指向const char *

原因:例如:const float *  其實是隻想一個具有const限定的float的指針

7./####################################################################/

/####################################################################/

/####################################################################/

/@@@@@@@/其實我們平時的聲明就是!!!申請記憶體!!! 例如int i;即申請了一個記憶體為sizeof(int)大小的記憶體的房間,房間名字叫  i 

!!!!!!

然後指派就是給房間住進客官!!!!!!(即指派是對房間名進行操作,即變量名!!)

8./@@@@@@@/inti=20;  int* p; p=&i;

指針的意義:名稱為 i 的房間裡住着20,p相當于服務員,一旦申請int* p後(相當于聘請好這個服務員)也必須申請了一處記憶體(即服務員

站在吧台) 我現在要找 20 ,怎麼辦? 去吧台找服務員,服務員指着 i 房間說他在那裡,然後我就找到了,是以此處的服務員就是一個指

針!!!!!!!!!!!! 他知道 20  的位址,即儲存的是 20 的位址 ,是以我才能找到20 這個人!!!

請注意:::::int* p  中 int* 是一個整體!!!!  是聲明指針的!符号!  p才是指針變量!!!是以 p 的值是 &20(即20 的位址(房間号))

;  是以要讀取20 就必須要讀 *p  !!!!!!

9./@@@@@@@/ 可以通過指針改變原 房間中的值  也就相當于 服務員 送走 20後;又迎來 15 客人!!!!!

即:  int i=20; int *p;p=&i;(p是指向i的指針) *p=15;  printf("%d",i);

此時 輸出的值是 15 而不是 20 !!!!!(root:可以通過指針改變原 房間中的值)

/####################################################################/

/####################################################################/

/####################################################################/

10.注意細節: int *p;

         int a[]={1,2,3,4,5,6};

要把 a[] 指派給p ,直接是p=a即可, 因為a是首位址!!!

/####################################################################/

/####################################################################/

11.請注意數組名與指針的差別: 數組名隻不過是一個const 指針(數組名隻是指向首位址!!!如果要取得後面的數就隻能是相對于首位址的

偏移量!!!而不能是指針的向後移動!!! 因為他是const指針,而指針變量p可以這樣),即是指針常量!!!  而指針本身的意義是指針變

量!!!!!!!!!! 是以又有一種情況是指針能做到而數組名做不到的: 

int *p;

int a[]={1,2,3,4,5,6};

p=a;

int i;

for(i=0;i<6;i++)

{

   printf("%d",p);   

   p++;              

}

偏移量: 例如  *(p+3); *(a+3); a[3]; 三者都是等價的  ^_^

/####################################################################/

/####################################################################/

12.指向指針的指針(即二階指針):

int **p;

int a=10;

我們可以知道 p 是儲存位址的位址的變量;是以必有 *p=&a;而p是儲存&a(也就是*p) 的位址的; 是以如果調用出 10 就必須是printf

(**p);  而printf(*p)其實是輸出 &a的位址  ......

//  好好了解  二階指針!!!

#include <stdio.h>

void main()

{

 int a=10;

 int*point;     //一維指針 儲存 10 的位址

 int**ppoint;   //二維指針 儲存 一維指針的位址

   point=&a;

 ppoint=&point;  //此處千萬不能是 ppoint=&(&a);之類;  因為此處的 &a已經是常量了;不能對常量取位址;而point是變量;

是以可以...

 printf("%d\n",*point);

 printf("%d\n",**ppoint);

}

void find2(char array[], char search, char **ppa)

    {

           int i;

           for (i=0; *(array + i) != 0; i++)

           {

                   if(*(array + i) == search)

                   {

                      *ppa = array + i;

                      break;

                   }

                  else if(*(array + i) == 0)

                   {

                     *ppa = 0;

                      break;

                   }

           }

    }

ppa指向指針p 的位址。

對*ppa的修改就是對p值的修改。

13.函數指針和指針函數:

函數指針: 例如:void (*fun)(int )

(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)

void MyFun(int x)   //聲明

{}

void (*fun)(int );  //聲明

void main()

{

   fun=&MyFun;    //把函數位址給函數指針

   fun=MyFun;     //這是什麼?哈哈;也是把函數位址給函數指針

   MyFun(10);     //輸出結果

   (*fun)(10);    //可以這樣

   fun(10);       //還可以這樣

   (*MyFun)(10);   //竟然還可以這樣

以上說明了什麼?》》》》》函數名就是 指針(★)     

MyFun 的函數名與FunP函數指針都是一樣的,即都是函數指針。

MyFun 函數名是一個函數指針常量,而 FunP 是一個函數數指針變量,這是它

們的關系。

}

(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)

指針函數: 例如:char *fun(int x) 必須有傳回值,而且類型是char * 型

14.

   (1)為了防止頭檔案被重複引用,應當用 ifndef/define/endif 結構産生預

    處理塊。(★)

    (2)用#include<filename.h>格式來引用标準庫的頭檔案(編譯器将從

   标準庫目錄開始搜尋)。

    用 #include“filename.h” 格式來引用非标準庫的頭檔案(編譯器将

   從使用者的工作目錄開始搜尋,如果工作目錄沒有找到,在傳回标準庫開始搜尋)。

15.

/..............#ifndef..#define...#endif............................................./

   #ifndef GRAPHICS_H     // 防止graphics.h  被重複引用 (★)(★)(★)

   #define GRAPHICS_H                                   (★)(★)(★)

   #include<math.h>       // 引用标準庫的頭檔案

   …

   #include“myheader.h”  // 引用非标準庫的頭檔案

   …

   voidFunction1(…);     // 全局函數聲明

   …

   classBox               // 類結構聲明

   {

   …

   };

   #endif

  以上是一個頭檔案塊;是用ifndef/define/endif 結構産生  預處理塊(所有聲明在一起是一個

   整體);

   然後我要生成這些函數或者類;必須在 graphics.cpp中單獨寫入;

   例如:

   #include"graphics.h"  //聲明包含在此頭檔案中

   void Function1()

   {

     printf("hello world");

   }

   class Box  //類結構聲明

   {

     public:

           ....

     private:

           ....

     protected:

           ....

   };

   現在我要應用此頭檔案:

   #include "graphics.cpp"//聲明包含在此頭檔案中

   void main()

  {

    .....

  }

/............#ifndef..#define...#endif..................................................../   

16.頭檔案的作用

 (1 )通過頭檔案來調用庫功能。在很多場合,源代碼不便(或不準)向使用者公布,隻要

      向使用者提供頭檔案和二進制的庫即可。使用者隻需要按照頭檔案中的接口聲明來調用庫功

      能,而不必關心接口怎麼實作的。編譯器會從庫中提取相應的代碼。

(2 )頭檔案能加強類型安全檢查。如果某個接口被實作或被使用時,其方式與頭檔案中

     的聲明不一緻,編譯器就會指出錯誤,這一簡單的規則能大大減輕程式員調試、改錯的

     負擔。

17.代碼格式(注意空格的添加和 {} 的随時添加 !!!)

(a)為風格良好的代碼行                 (b )為風格不良的代碼行。

 int width;  //寬度                      int width, height, depth; // 寬度高度深度

 int height; // 高度

 int depth;  // 深度

 x = a +b;                                X = a + b;   y = c +d;  z = e + f;

 y = c + d;

 z = e + f;

 if (width <height)                      if (width < height) dosomething();

 {

   dosomething();

 }

 for (initialization; condition;update)   for (initialization;condition; update)

 {                                             dosomething();

     dosomething();                      other();

 }

 // 空行

 other();

   if 、for 、while等關鍵字之後應留一個空格再跟左括号 ‘(’,以突出關鍵字。

  ‘,’之後要留白格,如 Function(x, y, z) 。如果‘;’不是一行的結束

   符号,其後要留白格,如 for(initialization;  condition; update)。

  指派操作符、比較操作符、算術操作符、邏輯操作符、位域操作符,如“=”、“+= ” “>=”、   “<=”、“+ ”、“* ”、“% ”

、“&&”、“||”、“<<”, “^    ”等二進制操作符的前後應當  加空格。 

 void Func1(int x, int y, intz);          // 良好的風格

 void Func1 (int x,int y,intz);           // 不良的風格

 if (year >=2000)                         // 良好的風格

 if(year>=2000)                            // 不良的風格

 if ((a>=b)&&(c<=d))                     // 良好的風格

 if(a>=b&&c<=d)                            // 不良的風格

 for (i=0; i<10;i++)                      // 良好的風格

 for(i=0;i<10;i++)                         // 不良的風格

 for (i = 0; I < 10; i++)                 // 過多的空格

 x = a < b ? a :b;                        // 良好的風格

 x=a<b?a:b;                                // 不好的風格

 int *x =&y;                              // 良好的風格

 int * x = &y;                            // 不良的風格

 array[5] =0;                             // 不要寫成 array [ 5 ] = 0;

 a.Function();                             // 不要寫成 a . Function();

 b->Function();                            // 不要寫成 b -> Function();

18. 命名 規則

   匈牙利命名規則的主要思想:

   是“在變量和函數名中加入字首以增進人們對程式的了解”。例如所有的字元變量均以

    ch為字首,若是指針變量則追加字首p。如果一個變量由ppch 開頭,則表明它是指向字

   符指針的指針。

   “匈牙利”法最大的缺點是煩瑣,例如

   int   i,  j,  k;

   float  x,  y, z;

    倘若采用“匈牙利”命名規則,則應當寫成

   int   iI,  iJ, ik;   // 字首 i 表示 int 類型

   float  fX,  fY, fZ;   // 字首 f 表示 float 類型

共性:

(1)

Windows  應用程式的辨別符通常采用“大小寫”混排的方式,如 AddChild。

而 Unix  應用程式的辨別符通常采用“小寫加下劃線”的方式,如add_child。别把這兩混在一起使用!

(2)

盡量避免名字中出現數字編号,如 Value1,Value2等,除非邏輯上的确需要編号。這是為了防止程式員偷懶,不肯為命名動腦筋而導緻産生

無意義的名字(因為用數字編号最省事)。 

(3)

1>類名和函數名采用大寫字母開頭的字母組合而成,例如:class TreeNode; voidSetValue();

2>變量和參數采用小寫字母開頭,組合名的後面字母仍用大寫字母 例如:inttreeNode;  BOOL flag;

3>所有常量都用大寫字母和下劃線的組合 例如:const int MAX; const intMAX_LENGTH;

4>靜态變量前都加上s_;然後的命名和變量時一樣的 !!!例如:static ints_treeNode;

5>全局變量前都加上g_(global) 例如:intg_treeNode;  (一般情況下不用(少用)全局變量)

6>類的資料成員前加上m_(member)以示差別

19.寫if 語句與零值比較

(1):BOOL值 例如:bool flag;   if(flag)//flag 為真 和  if (!flag) //flag 為假

       其它的用法都屬于不良風格,例如:

       if (flag == TRUE)   //注意空格

       if (flag == 1)    //注意空格   

       if (flag == FALSE)  //注意空格

       if (flag ==0)     //注意空格

(2):整形與零值的比較

       if (curentValue == 0)  //注意空格

       if (curentValue != 0)  //注意空格

(3):浮點值與零值的比較

       if ((curentValue > -0.000001)&& (curentValue <0.000001))

       千萬不能直接與0.0之類比較; if (curentValue==0.0)  //錯誤

       要轉化為:if((curentValue >-EPSINON)&& (curentValue<EPSINON))

       其中 EPSINON 是允許的誤差(此處是0.000001)

(4):指針變量與零值的比較

       定義一個指針變量 p

       if (p == NULL)  //不要寫成 p ==0   //注意空格

       if (p != 0)

       不要寫成:

       if (p == 0 )

       if (p != 0)

       if (p)

       if (!p)

(5):重點補充:有時候把 if (p == NULL) 寫成 if (NULL ==p)   其實是防止把if (p == NULL)寫成

         if (p = NULL) 而導緻出錯;編譯器編譯時 if (p = NULL)是不會報錯的,但是如果寫成

         if (NULL = p) 是會報錯的...(值得學習!!!) 

(6): 不良風格:

         if (condition)  //注意空格 

            return x;

         return y;

    改寫後:

        if (condition)   //注意空格

        {

           return x; 

        }

        else

        {

           return y;

        }

    或者:

        return (condition ? x : y);  //注意空格

20. for 語句

       (1):在多重循環中,如果有可能,将最大循環放在最裡面!in fact 為了減少 CPU的跨切循環層        的次數        

       (2):如果循環體記憶體在邏輯條件判斷,最好将它移到循環外面:

       例如:

       for (i = 0; i <= N;i++)           if(condition)

       {                                  {

          if(condition)                     for (i = 0; i <= N;i++)         

          {                                  {  

             DoSomething();                     DoSomething();  

          }                                  }

          else                            }

          {                               else

             DoOtherthing();              {

          }                                  for (i = 0; i <=0; i++)

       }                                     {

                                                    DoOtherthing();  

                                              }

                                           } 

      如果 N 不大 ;那麼兩個執行效率差不多,可以用左邊的;如果 N 很大 那就用右邊的

      (3):最好采用半開半閉的方法

          将上面語句改成:

          for (i = 0; i < N+1; i++)

          {

               ... ...

          }

21. switch語句: 結尾的 default 一定要加上;以防别人誤以為你忘記 default 的處理

22.

   函數方面:

   (1).書寫要完整:例如: voidTreeNode(int x, int y);

                         void TreeNode(int , int );  //不良寫法

                         int value(void);

                         intvalue();       //不良寫法

  (2).對于指針作為參數的處理,如果隻做輸入用,最好在前面加上 const ;避免被無意修改!!!

      例如: char *Strcpy(char *strCopyTo, const char *strCopyFrom);

  (3).如果輸入參數以值傳遞的方式傳遞對象,則宜改用“const &“方式來傳遞,這樣可以省去臨時對      

      象的構造和析構過程,進而提高效率。 (★)(★)(★)(不懂!!!!!!)

   (4).盡量不要使用類型和數目不确定的參數。C标準庫函數 printf 是采用不确定參數的典型代表,     

      其原型為:int printf(const chat *format[, argument]…);這種風格的函數在編譯時喪失了嚴    

      格的類型安全檢查。  (★)(★)(★)(不懂!!!!!!)

  (5).有時候函數原本不需要傳回值,但為了增加靈活性如支援鍊式表達,可以附加傳回值。 

      例如字元串拷貝函數strcpy  的原型:

      char *Strcpy(char *strDest,const char*strSrc);   //格式

      strcpy  函數将strSrc 拷貝至輸出參數strDest  中,同時函數的傳回值又是 strDest 。這樣做

      并非多此一舉,可以獲得如下靈活性:

      char str[20];

      int  length = strlen( strcpy(str, “Hello World”)); //經典

   (6).關于函數的 return 語句(★)(★)(★)

      1> return 語句不可以傳回指向棧記憶體的指針或者引用 ,因為該記憶體在函數結束時自動銷毀

         例如:  char *Fun(void)

                 {

                     char str[] = "hello word";  //str 記憶體位于棧上

                     ...

                     returnstr;                 //錯誤

                 }

     2> 要盡量提高函數的執行效率

        例如:

                return String(s1 + s2);  和  temp= String(s1 + s2); return temp;

                兩者的執行效率是不同的!! 前者更好!!前者是建立一個臨時的對象并傳回它。

                對于後者:首先,temp 對象被建立,同時完成初始化;然後拷貝構造函數把temp 拷貝

                到儲存傳回值的外部存儲單元中;最後,temp 在函數結束時被銷毀(調用析構函數)

                。然而“建立一個臨時對象并傳回它”的過程是不同的,編譯器直接把臨時對象建立

                并初始化在外部存儲單元中,省去了拷貝和析構的化費,提高了效率。

                類似的 我們 要寫:return (x + y); 而不是:temp = x + y; returntemp;  

     3>(★)

        程式一般分為 Debug 和 Release 版本,Debug 版本用于内部測試;Release版本用于發行給用        

        戶。 斷言( assert ) 的使用時很重要的!!!一般在函數的入口處最好用斷言來判斷參數的可

        行性。而且 assert 隻在 Debug 起作用,是一種宏結構,不是一種函數,是為了避免對我們的

        函數主題産生不必要的影響。assert 的作用是:隻要其條件不滿足,就會終止程式的運作!!

        例如:

            char *Copy(char *copyTo, const char *copyFrom)

            {

                assert((CopyTo != NULL) &&(CopyFrom != NULL)); // 使用斷言

                byte *to=(byte*)copyTo;     //防止改變位址

                byte *from=(byte *)copyFrom;  //防止改變位址

                while(*to++ ==*from++);   

                return copyTo 

            }

      4>(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)

        (★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)

         引用與指針的比較 :( 經 典 )

         引用:n是m的一個引用,m是被引用物;int m ; int &n = m;//好好看看它的形式哦,( ⊙o⊙

               )哇,怎麼會是這樣啊,?這樣也行?O(∩_∩)O~;例如一個人叫 m;現在他有一個綽号               叫 n ;我們叫 n

;其實也就是在叫 m ;其實 n 就是 m;m 就是n;是同一個人;是以對                n的處理也就是對 m 的處理 ! ! ! ! ! ! (真的要

注意哦對n的處理就是對 m 的處理 )

         注意點:(1).引用在被建立的同時必須被初始化(必須的哦)

                 (2).一旦被初始化,就不能改變引用的關系咯

                 例如:int iI =5;    //注意  空格  命名法則( 匈牙利 )

                       int iJ = 10;

                       int &iK = iI;  //引用參數的定義以及初始化

                       iK =iJ;       //此時 iK 的值改變咯(知道 iK 其實就是 iI )

                                       //是以此時就是相當于 iI 改變了

                       printf("%d\n", iK); ----> 10

                       printf("%d\n", iI); ----> 10

                       printf("%d\n", iJ); ---->10  

        引用傳遞參數的小例子:

                       void Add(int &x)  //引用傳遞的 處理像是指針的處理

                       {

                           x += 1;

                       } 

                       void main()

                       {

                           int x;

                           Add(x);   //引用傳遞的 傳入 像是值傳遞

                           printf("%d\n", x);  

                       }            

       (★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)

       (★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)                         

23. (深入研究 記憶體 問題 )

   歡迎進入記憶體這片雷區。偉大的 Bill Gates 曾經失言:

    640Kought to be enough for everybody

                                           — Bill Gates  

   23-1.記憶體配置設定的方式:

                 (1).從靜态儲存區域配置設定:記憶體在程式編譯時就已經配置設定好了,這塊記憶體在程式整個                     

                     運作期間都是存在的。例如:全局變量;static 變量

                 (2).在棧上建立:在執行結束時被自動釋放;

                     例如之前的一段程式:

                     char *String(void)

                     {

                         char str[]="hello world";  // 在棧上建立的

                         ... ...

                         returnstr;               // 錯誤 錯誤 錯誤 錯誤 錯誤 錯誤 錯誤

                     }                             //記憶體在程式結束時被自動釋放

                 (3).在堆上建立:也就是所謂的動态配置設定(malloc 或者 new 申請記憶體單元);程式員

                     自己決定何時釋放( free 或 delete )

   23-2.常見的錯誤:

                 (1).記憶體未配置設定成功卻使用了它,解決方法:在使用前檢查 指針 是否為 NULL;

                     如果指針 p 是參數;那麼可以 用斷言 assert(p != NULL) 來判斷;

                     如果用 malloc 和 new 申請記憶體,必須用if (p == NULL) or if (p != NULL)

                     來防止出現錯誤!

                 (2).配置設定成功;但是未初始化就使用了它

                 (3).操作超過記憶體邊界

                 (4).請注意:自己經常犯的錯誤:在動态配置設定時要時刻牢記的不僅是申請記憶體;更重      

                     要的是釋放記憶體!!! 規則:malloc 和 free次數必須相同;new 和delete次              

                     數必須相同;

                 (5).釋放了記憶體卻還在使用它:

                     典例1: 棧指針或者棧引用的傳回問題 : 在程式執行完後,記憶體被自動釋放;所

                             以是不可以将棧記憶體的指針或引用傳回的! ! ! ! !!  

                     典例2: 動态配置設定後,用free和new 釋放了記憶體;但是指針沒有設定為 NULL ;

                             導緻指針成為野指針   

  【規則1】不要忘記為數組和動态記憶體賦初值。防止将未被初始化的記憶體作為右值使用。

   【規則2】避免數組或指針的下标越界,特别要當心發生“多 1”或者“少 1 ” 操作。

  【規則3】動态記憶體的申請與釋放必須配對,防止記憶體洩漏。

   【規則4】用free  或 delete  釋放了記憶體之後,立即将指針設定為NULL,防止産生“野指針”。 

  23-3.

     指針與數組對比:( 宏觀上 )(★)(★)(★)(★)(★)(★)(★)(★)(★)

               數組要麼在靜态存儲區被建立(如全局數組),要麼在棧上被建立。數組名對應着(而

               不是指向)一塊記憶體,其位址與容量在生命期内保持不變,隻有數組的内容可以改變(此

               句話說明或者照應了我以前的說法:數組名實際上就是一個 const 指針,特點是:指向

               唯一記憶體;但是記憶體中的值可以改變;函數名本質上也是指針;具體的請看以前的...

               O(∩_∩)O~)。 指針可以随時指向任意類型的記憶體塊,它的特征是“可變”,是以我們

               常用指針來操作動态記憶體。指針遠比數組靈活,但也更危險。

    例如:    char str[] = "hello";  //在棧記憶體中的配置設定

               char *p ="world";    //實際上是位于靜态存儲區 相當于指針 p 指向的是const

                                      //即:const char *p;

               str[0] = 'X';

               p[0] ='X';           //由以上可知此句是錯誤的;const 是不能改變的額

               puts( str );

               puts( p );

  23-4.計算記憶體容量:

           (★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)

      (1)int str[100]; 用sizeof();來計算,輸出的是 100 ;現在有一個指針p指向 數組str;

      計算 sizeof( p ); 輸出來的結果是 4 ( 很詫異吧,O(∩_∩)O哈哈~ );

      實際上sizeof計算的是 sizeof( char * ) == 4;C 和 Cpp都是不能知道指針所指的記憶體容量的 

      除非在配置設定記憶體的時候記住它;是以sizeof( 指針) == 4 ; 是以計算記憶體不能用指針計算!!!

      (2)一個典例:

                   int TreeNode( char str[100] ) 或者   void TreeNode( char str[100])

                   {                                    {

                       return(sizeof(str));                 printf("%d\n",sizeof(str));  

                   }                                    }

                   以上兩種情況輸出的結果都是 4;哇塞,不會吧;會的;因為此處的數組已經退化               

                   為指針咯

      (3)注意:例如:  char str[]="hello"; printf("%d\n", sizeof(str));

                 請問輸出的結果是多少呢?   對了是 6 ;請不要把 '\0'不當人!!!我靠!!!

      (4)還要注意:sizeof() 是計算 記憶體大小的 ;而 strlen() 是計算實際字元串大小的哦

  23-5. 指針參數如何傳遞的?

       編譯器在編譯時總是要給每個參數制作一個副本;指針參數 p 的副本是 _p ;執行函數時;副本

       _p 的改變就是 指針 P 的改變;

       典例:

                 void GetMemory(char *p, int num)

                 {

                      p = (char *)malloc(sizeof(char) *num)      

                 }  

                 void main()

                 {

                      char *str = NULL;

                      GetMemory(str, 100);

                      assert(str != NULL);

                      strcpy(str, "hello,world");

                      puts(str);  

                      free(str);   //重要重要重要重要重要重要重要重要重要重要重要

                 }

      貌似這個程式非常正确;其實是從根本上錯了;str 根本就沒有配置設定到記憶體;不信就“斷言”吧;

      其實想想也是很簡單的;不就是相當于要為   str 配置設定記憶體嗎?剛開始str中是NULL;現在我要

      改變它就是了;那麼不就可以通過指向 str 的指針來改變嗎? 是以不就可以定義一個指向指針的

      指針來處理嗎?

     (★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)

      簡單一句話-:如果參數是指針;請不要指望讓它去申請記憶體;  

      總之如果要用 函數 來實作給 字元串 配置設定記憶體;請不要傳遞實參 str 即不要寫成形參是指針 *p

      形式;不要指望它;要找就找 &str 和 **p 他們可以做到為你配置設定記憶體 !!!

     void GetMemory(char **p, int num )

     {

        *p = (char *)malloc(sizeof(char) * num);

     }

     void main()

     {

        char *str = NULL;

        GetMemory(&str, 10);

        assert(str != NULL);

        strcpy(str, "hello");

        puts(str);

        free(str);//重要重要重要重要重要重要重要重要重要重要重要

        str = NULL;  //好習慣

     }

    另類形式:

     char *GetMemory(int num)

     {

          p = (char *)malloc(sizeof(char) * num);

          returnp;    

     }  

     void main()

     {

        char *str = NULL;

        str = GetMemory(str, 10);

        assert(str != NULL);

        strcpy(str, "hello");

        puts(str);

        free(str);//重要重要重要重要重要重要重要重要重要重要重要

        str = NULL;  //好習慣

     }

    (★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)

    這裡還強調 return 函數的問題;;;

    例如:    char*GetChar()

              {

                  char str[] = "hello";  //此處是 棧 記憶體;程式結束時自動消亡

                  ...

                  return str;  //錯誤錯誤錯誤錯誤錯誤錯誤錯誤錯誤錯誤錯誤錯誤錯誤錯誤錯誤

              }

              void  main()

              {

                  char *str;

                  str = GetChar();  //得到的是  亂碼

                  puts(str);

              }

  23-6.

      探讨 free 和 delete 把指針怎麼了???

      O(∩_∩)O哈!  它們隻不過把指針的記憶體給釋放掉咯;但是并沒有把指針本身給幹掉((★))

      是以這個指針本身還是存在的;發現指針 p 被 free  以後其位址仍然不變(非NULL),隻是

      該位址對應的記憶體是垃圾,p 成了“野指針”。如果此時不把 p 設定為 NULL ,會讓人誤

      以為 p 是個合法的指針。進而導緻錯誤;而且此時如果用  if (p != NULL) or if(p == NULL)

      都是判斷不了的 !!!是以切記:::::free or delete 之後必須要使之指針為NULL

      (★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)

       切記:在 free 和 delete 指針之後;必須把指針指派為 NULL ;防止指針成為 “野指針” !

      (★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)

  23-7.

     切記:動 态 内 存 是 不 會 被 自 動 釋 放 的... ...

     隻要你的整個的大的 main() 函數還在執行;不管是那個局部的多麼少的動态配置設定都不會自動釋放

     ;必須的是我們自己 free() 或者 delete() 它們;并且要指派指針為 NULL;防止變為野指針!

     ( 請不要偷懶或者對這個問題不以為然;會出大問題的!!!靠... ... )

      》》》》》》指針死了,并不代表它的記憶體回收咯;記憶體釋放咯,也并不代表指針死了(可能變野          

                  指針咯);

      》》總結: 釋放記憶體  和  讓指針變為NULL是不可能同時達到目的的;但是我們又是必須要做的

 23-8.    

       ————》請杜絕野指針

                            ------By  pt

      首先我們來看一下什麼要的指針叫“ 野指針 ” ;野指針,顧名思義,是沒有人需要的指針,

      或者說你人們害怕的指針!請不要把野指針和和 NULL 指針混淆, NULL指針 可不是野指針

   之前的 23-6. 也提到了野指針,但那時候重點講的是 要 free (or delete) 記憶體,現在

     重點講野指針:

                 野指針的産生:

                             1. 在定義指針的同時沒有初始化指針,這時在使用時它就會亂指一氣;

                                是以在定義時要将指針初始化;可以是 NULL 或者 指向...

                                    char *p = NULL;

                                或者

                                    char *p = (char *)malloc(100);

                             2.就是之前所說的 free 和 delete 之後沒有指派指針為 NULL;(注意)

                             3.指針操作超過了變量作用的範圍

                               例如:

                                 class A

                                {

                                 public:

                                     void Func(void){ cout << “Func ofclass A” << endl; }

                                };

                                void Test(void)

                                {

                                     A  *p;

                                         {

                                          A  a;

                                          p =&a;    // 注意 a 的生命期

                                          p->Func();    // p 是正常指針

                                         }

                                     p->Func();    // p 是“野指針”

                                }

  23-9.

      free 和 malloc  與  delete 和 new之間的差别

      free 和 malloc 是庫函數,但是 delete 和 new 不是庫函數;對于一個外部的對象而言,它在創

      建時要執行 構造函數,在消亡時要執行 析構函數;但是free 和 malloc 是庫函數是庫函數,它

      們不在編譯器的控制範圍之内,不能把執行構造函數和析構函數的任務強加給 它們,是以就出現

      了delete 和 new ;理論上講:delete 和 new 對于内部資料處理時也能代替free 和 malloc,但

      是由于 C 中隻能是free 和 malloc,是以free 和 malloc是不能被遺忘的......                 

  23-10.

       記憶體耗盡怎麼辦???-----》也就是說我在申請動态記憶體時沒有那麼大的記憶體咯,傳回了NULL

                                  我該怎麼辦???

       處理 1.

              判斷 if ( p = NULL )

                   {

                       return       // 可以用return 語句傳回

                   }

       處理 2.

              判斷 if ( p = NULL )

                  {

                       cout << “Memory Exhausted”<< endl;

                       exit(1);    // 馬上用exit(1)殺死整個應用程式

                  } 

  附錄:關于 exit(1) 和 exit(0)我在這裡也要解釋一下,其實在正常沒有傳回值的情況下他們兩是完

       全一樣的》》》都是殺死應用程式;當時如果有傳回值是,exit(0) 表示的是 非正常情況下的結

       束,而 exit(1),或者 exit(2) 等非 0 的數都可以表示正常結束應用程式,一般都用exit(1);

  不過現在對于 32位以上的應用程式來說,基本上是不可能記憶體耗盡的,因為即使記憶體耗盡了,”虛存

“可以幫我們忙,自動用硬碟空間來頂替... ...

------》 

  個人建議最好用exit(1)殺死整個應用程式

  23-11.

       new 的使用 比malloc要簡單多了

       例如:   int *p = (int*)malloc(sizeof(int) * num);

                int *p = new int[num];

       例如:

                #include <iostream>

                using namespace std;

                void main()

               {

         int *pi = new int[10];   //用 new申請記憶體更友善

         int i ;

         for (i = 0; i < 5; i++)

         {

     cin >> pi[i]>> endl;

     //scanf("%d\n",&pi[i]);

         }

         for (i = 0; i < 5; i++)

         {

     cout << *(pi + i)<< endl;

         }

                deletepi;    //釋放記憶體  挂嘴邊

               }

     在 C++的類中,對于不同的對象的處理是不同的,是以會有不同的 記憶體的申請方式

     ---》  class  Obj

            {... ...};

            Obj *obja = newObj;    //聲明一個

            Obj *objb = new Obj(1);  //... 外加指派為1 ;            

    (★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)  

     如果用 new 建立對象數組,那麼隻能使用對象的無參數構造函數。例如

     Obj  *objects = new Obj[100]; // 建立 100 個動态對象

     不能寫成:

     Obj  *objects = new Obj[100](1);// 建立 100個動态對象的同時賦初值 1

     在用 delete 釋放對象數組時,留意不要丢了符号‘[]’。例如

     delete []objects;  // 正确的用法

     deleteobjects;    //錯誤的用法

     後者相當于 delete objects[0],漏掉了另外99 個對象。

   (★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)

  23-12.

       (★)(★)(★)(★)

 (★)(★)(★)(★)

 #ifndef BOOKSTORE_H  //有可能一個源檔案中包含了兩個以上此頭檔案, 這時防止重複處理相同的頭檔案

       #define BOOKSTORE_H

       #endif

條件訓示符#ifndef 檢查BOOKSTORE_H 在前面是否已經被定義 這裡 BOOKSTORE_H

是一個預編譯器常量  習慣上預編譯器常量往往被寫成大寫字母 如果BOOKSTORE_H

在前面沒有被定義 則條件訓示符的值為真 于是從#ifndef 到#endif 之間的所有語句都被包

含進來進行處理 相反 如果#ifndef 訓示符的值為假 則它與#endif 訓示符之間的行将被忽

   為了保證頭檔案隻被處理一次 把如下#define 訓示符

    #defineBOOKSTORE_H

    放在#ifndef後面 這樣在頭檔案的内容第一次被處理時 BOOKSTORE_H 将被定義

   進而防止了在程式文本檔案中以後#ifndef 訓示符的值為真

   隻要不存在兩個必須包含的頭檔案要檢查一個同名的預處理器常量 這樣的情形 這

   個政策就能夠很好地運作

   #ifdef訓示符常被用來判斷一個預處理器常量是否已被定義 以便有條件地包含程式代

繼續閱讀