天天看點

C++Primer讀書筆記:第4章 數組和指針

數組

  數組定義中的類型名可以是内置資料類型或類類型;除引用之外,數組元素的類型還可以是任意的複合類型。沒有所有元素都是引用的數組。

  除非顯式地提供元素初值,否則内置類型的局部數組的元素沒有初始化。此時,除了給定元素之外,其他使用這些元素的操作沒有定義。

不允許數組直接複制和指派

  int ia[] = { 0 , 1 , 2 }; // ok: array of ints

  int ia2[](ia); // error: cannot initialize one array with another

  int main()

  {

   const unsigned array_size = 3 ;

   int ia3[array_size]; // ok: but elements are uninitialized!

   ia3 = ia; // error: cannot assign one array to another

   return 0 ;

  }

警告:數組的長度是固定的

  與vector類型不同,數組不提供push_back或者其他的操作在數組中添加新元素,數組一經定義,就不允許再添加新元素。

  如果必須在數組中添加新元素,程式員就必須自己管理記憶體:要求系統重新配置設定一個新的記憶體空間用于存放更大的數組,然後把原數組的所有元素複制到新配置設定的記憶體空間中。

  導緻安全問題的最常見原因是所謂“緩沖區溢出(buffer overflow)”錯誤。當我們在程式設計時沒有檢查下标,并且引用了越出數組或其他類似資料結構邊界的元素時,就會導緻這類錯誤。

指針的引入

建議:盡量避免使用指針和數組

  指針和數組容易産生不可預料的錯誤。其中一部分是概念上的問題:指針用于低級操作,容易然生與繁瑣細節相關的(book keeping)錯誤。其他錯誤則源于使用指針的文法規則,特别是聲明指針的文法。

  許多有用的程式都可不使用數組或指針實作,現代C++程式采用vector類型和疊代器取代一般的數組、采用string類型取代C風格字元串。

指針可能的取值

  一個有效的指針必然是以下三種狀态之一:儲存一個特定對象的位址;指向某個對象後面的另一對象;或者是0值。若指針儲存0值,表明它不指向任何對象。未初始化的指針是無效的,直到給該指針指派後,才可使用它。

  int ival = 1024 ;

  int * pi = 0 ;       // pi initialized to address no object

  int * pi2 = & ival;   // pi2 initialized to address of ival

  int * pi3; // ok, but dangerous, pi3 is uninitialized

  pi = pi2;        // pi and pi2 address the same object, e.g. ival

  pi2 = 0 ;        // pi2 now addresses no object

避免使用未初始化的指針

  很多運作時錯誤都源于使用了未初始化的指針。

  如果可能的話,除非所指向的對象已經存在,否則不要先定義指針,這樣可避免定義一個未初始化的指針。

  如果必須分開定義指針和其所指向的對象,則将指針初始化為0.因為編譯器可檢測出0值的指針,程式可判斷該指針并未指向一個對象。

指針初始化和指派操作的限制

  對指針進行初始化或指派隻能使用以下四種類型的值:

  (1)0值常量表達式。

  (2)類型比對的對象的位址。

  (3)另一對象之後的下一位址。

  (4)同類型的另一個有效指針。

  把int型變量賦給指針是非法的,盡管此int型變量的值可能為0。

void*指針

  C++提供了一種特殊的指針類型void*,它可以儲存任何類型對象的位址:

  double obj = 3.14 ;

  double * pd = & obj;

  // ok: void* can hold the address value of any data pointer type

  void * pv = & obj; // obj can be an object of any type

  pv = pd; // pd can be a pointer to any type

void*表明該指針與一位址值相關,但不清楚存儲在此位址上的對象的類型。

  void*指針隻支援幾種有限的操作:與另一個指針進行比較;向函數傳遞void*指針或從函數傳回void*指針;給另一個void*指針複制。不允許用void*指針操縱它所指向的對象。

指針操作

解引用操作生成左值

關鍵概念:給指針指派或通過指針進行指派

  對于初學指針者,給指針指派和通過指針進行指派這兩種操作的差别确實讓人費解。謹記區分的重要方法是:如果對左操作數進行解引用,則修改的是指針所指向的值;如果沒有使用解引用操作,則修改的是指針本身的值。

指針和引用的比較

  第一個差別在于引用總是指向某個對象:定義引用時沒有初始化是錯誤的。第二個重要差別則是複制行為的差異:給引用指派修改的是該引用所關聯的對象的值,而并不是使引用與另一個對象關聯。引用一經初始化,就始終指向同一個特定對象(這就是為什麼引用必須在定義時初始化的原因)。

指向指針的指針

  指針本身也是可用指針指向的記憶體對象。指針占用記憶體空間存放其值,是以指針的存儲位址可存放在指針中。

  int ia[] = { 0 , 2 , 4 , 6 , 8 };

  int * ip = ia; // ip points to ia[0]

  ip = & ia[ 4 ]; // ip points to last element in ia

  ip = ia; // ok: ip points to ia[0]

  int * ip2 = ip + 4 ; // ok: ip2 points to ia[4], the last element in ia

指針的算數操作隻有在原指針和計算出來的新指針都指向同一個數組的元素,或指向該數組存儲空間的下一單元時才是合法的。如果指針指向一對象,我們還可以在指針上加1進而擷取指向相鄰的下一個對象的指針。

  C++還支援對這兩個指針做減法操作:

  ptrdiff_t n = ip2 - ip;   // ok: distance between the pointers

結果是4,這兩個指針所指向的元素間隔為4個對象。兩個指針減法操作的結果是标準庫類型ptrdiff_t的資料。與size_t類型一樣,ptrdiff_t也是一種與機器相關的類型,在cstddef頭檔案中定義。size_t是unsigned類型,而ptrdiff_t則是signed_t整型。

  允許在指針上加減0,使指針保持不變。如果一指針具有0值,則在該指針上加0仍然是合法的,結果得到另一個值為0的指針。也可以對兩個空指針做減法操作,得到的結果仍是0。

解引用和指針算術操作之間的互相作用

  在指針上加一個整型數值,其結果仍然是指針。允許在這個結果上直接進行解引用操作,而不必先把它賦給一個新指針:

  int last = * (ia + 4 );   // ok: initializes last to 8, the value of ia[4]

加法操作兩邊用圓括号括起來是必要的。如果寫為:

  last = * ia + 4 ; // ok: last = 4, equivalent to ia[0]+4

意味着對ia進行解引用,獲得ia所指元素的值ia[0],然後加4。

計算數組的超出末端指針

  const size_t arr_size = 5 ;

  int arr[arr_size] = { 1 , 2 , 3 , 4 , 5 };

  int * p = arr;      // ok: p points to arr[0]

  int * p2 = p + arr_size; // ok: p2 points one past the end of arr

     // use caution -- do not dereference!

C++允許計算數組或對象的超出末端的位址,但不允許對此位址進行解引用操作。而計算數組超出末端位置之後或數組首位址之前的位址都是不合法的。

指針和const限定符

  指向const對象的指針

  const double * cptr;     // cptr may point to a double that is const

const限定了cptr指針所指向的對象類型,而并非cptr本身。也就是說,cptr本身并不是const。

  不能使用void*指針儲存const對象的位址,而必須使用const void*類型的指針儲存const對象的位址:

  const int universe = 42 ;

  const void * cpv = & universe; // ok: cpv is const

  void * pv = & universe;      // error: universe is const

不能使用指向const對象的指針修改基礎對象,然而如果該指針指向的是一個非const對象,可用其他方法修改其所指的對象。

  const指針

  C++語言還提供了const指針——本身的值不能修改:

  int errNumb = 0 ;

  int * const curErr = & errNumb;     // curErr is a constant pointer

  curErr = curErr;             // error: curErr is a constant pointer

  指向const對象的const指針

  既不能修改所指對象的值,也不允許修改指針的指向。

  指針和typedef

  假設給出以下語句:

  typedef string * pstring;

  const pstring cstr;

請問cstr變量是什麼類型?

  const string * pstring; // wrong interpretation of const pstring cstr

  string * const cstr; // equivalent to const pstring cstr

C風格字元串

C風格字元串的标準庫函數(要使用這些标準庫函數,必須包含相應的C頭檔案:cstring)

strlen(s)       strcmp(s1, s2)      strcat(s1, s2)

strcpy(s1, s2)    strncat(s1, s2, n)    strncpy(s1, s2, n)

注意:這些标準庫函數不會檢查其字元串參數。

永遠不要忘記字元串結束符null

調用者必須確定目标字元串具有足夠的大小

如果必須使用C風格字元串,則使用标準庫函數strncat和strncpy比strcat和strcpy函數更安全:

  char largeStr[ 16 + 18 + 2 ]; // to hold cp1 a space and cp2

  strncpy(largeStr, cp1, 17 ); // size to copy includes the null

  strncat(largeStr, " " , 2 );    // pedantic, but a good habit

  strncat(largeStr, cp2, 19 ); // adds at most 18 characters, plus a null

  對大部分的應用而言,使用标準庫類型string,除了增強安全性外,效率也提高了,是以應該盡量避免使用C風格字元串。

建立動态數組

動态數組的定義

  int * pia = new int [ 10 ];   // array of 10 uninitialized ints

new表達式傳回指向新配置設定數組的第一個元素的指針。

初始化動态配置設定的數組

  可使用跟在數組長度後面的一對空圓括号,對數組元素做值初始化:

  int * pia2 = new int [ 10 ]();   // array of 10 uninitialized ints

對于動态配置設定的數組,其元素隻能初始化為元素類型的預設值,而不能像數組變量一樣,用初始化清單為數組元素提供各不相同的初值。

const對象的動态數組

  // error: uninitialized const array

  const int * pci_bad = new const int [ 100 ];

  // ok: value-initialized const array

  const string * pci_ok = new const int [ 100 ]();

允許動态配置設定空數組

  char arr[ 0 ];        // error: cannot define zero-length array

  char * cp = new char [ 0 ];     // ok: but cp can't be dereferenced

用new動态建立長度為0的數組時,new傳回有效的非零指針。該指針與new傳回的其他指針不同,不能進行解引用操作,因為它畢竟沒有指向任何元素。而允許的操作包括:比較運算,是以該指針能在循環中使用;在該指針上加(減)0;或者減去本身,得0值。

動态空間的釋放

  動态配置設定的記憶體最後必須進行釋放,否則,記憶體最終将會逐漸耗盡。

新舊代碼的相容

  混合使用标準庫類string和C風格字元串

  string st3( " Hello world " );     // st3 holds Hello world

  char * str = st2;           // compile-time type error

  char * str = st2.c_str();      // almost ok, but not quite

  const char * str = st2.c_str();  // ok

c_str傳回的指針指向const char類型的數組。

  

使用數組初始化vector對象

  const size_t arr_size = 6 ;

  int int_arr[arr_size] = { 0 , 1 , 2 , 3 , 4 , 5 };

  // ivec has 6 elements: each a copy of the corresponding element in int_arr

  vector < int > ivcec(int_arr, int_arr + arr_size);

轉載于:https://www.cnblogs.com/elite/articles/1910541.html