C++基礎知識學習:數組

1、數組

1. 定義數組程序員

數組是由類型名,標識符和維數組成的複合數據類型。其中,類型名能夠是內置類型或類類型,除引用以外,數組元素的類型還能夠是任意的複合類型。數組

數組的維數必須用大於等於1的常量表達式定義。在定義數組時,可爲其元素提供一組用逗號分隔的初值,這些初值用花括號{}括起來,稱爲初始化列表:安全

const unsigned array_size = 3;
int ia[array_size] = {0, 1, 2};

若是沒有顯式提供元素初值,則數組元素會像普通變量同樣初始化。iphone

對於數組元素的顯式初始化應當注意兩點:函數

第一,顯式初始化的數組不需指定數組維數值,例如ui

int ia[] = {2, 22, 222};

第二,若是指定維數大於列出的元素初值數,對剩餘元素來講,內置類型初始化爲零,類類型調用默認構造函數初始化。例如spa

double coords[3] = {}; // all elements default initialized to 0
int nums[5] = {0, 1, 2}; // nums[3] and nums[4] default initialized to 0
// lables[1] and labels[2] default initialized to empty string
string labels[3] = {"Hello"};

總結來看,內置類型的數組,在定義後要進行初始化,對於顯式初始化方式,一旦指定了其維數可確保全部元素都會被初始化,即便初始化列表中指定的元素初值數不足。而對於類類型的數組來講,在定義時就會調用默認構造函數初始化,若是顯式初始化,則調用相應構造函數進行初始化。例以下面本身寫的一個簡單類數組:指針

#include 
using namespace std;

class Apple
{
    public:
    Apple(int startVer):m_version(startVer)
    {
    }
    int GetVersion()
    {
        return m_version;
    }
    
    private:
    int m_version;
};

int main(void)
{
    Apple iphones[4] = {4, 5, 6, 7};
    int latest = sizeof(iphones)/sizeof(iphones[0])-1;
    cout<<"iphone "<<iphones[latest].GetVersion()<<endl;

    return 0;
}

不論是內置類型數組(包括字符數組),仍是類類型數組,都不支持直接複製與賦值操做。code

通常經過下標操做符來訪問數組的元素。在用下標訪問元素時,vector使用vector::size_type做爲下標的類型,而數組下標的正確類型是size_t。程序員必須對數組下標的使用負責,防止緩衝區溢出(buffer overflow)錯誤。對象

2. 數組下標與指針

在表達式中使用數組名時,該名字會自動轉換爲指向數組第一個元素的指針。使用指針的算術操做(pointer arithmetic)來獲取指定內容的存儲地址相比下標操做更加方便。例如:

int ia[] = {0, 2, 4, 6, 8};
int last = *(ia +4); // ok: last initialized to value of ia[4]
int *ip = ia; // ip points to ia[0]
int *ip2 = ip+4; // ok: ip2 points to ia[4]

使用下標訪問數組時,其實是對指向數組元素的指針作下標操做。只要指針指向數組元素,就能夠對它進行下標操做

int *p = &ia[2];
int j = p[1]; // ok: p[1] equivalent to *(p+1)
int j = p[-2]; //ok: p[-2] equivalent to *(p-2), same to ia[0]

2、動態數組

數組類型的變量有三個重要的限制:

  • 數組長度固定不變
  • 在編譯時必須知道其長度
  • 數組只在定義它的塊語句內存在

動態分配的數組沒必要在編譯時知道其長度,一般在運行時才肯定其數組長度,與數組變量不一樣,動態分配的數組將一直存在,直到程序顯式釋放它爲止。

每個程序在執行時都佔用一塊內存空間,用於存放動態分配的對象,此內存空間稱爲程序的自由存儲區(free store)或堆(heap)。C語言使用一對標準庫函數malloc和free在自由存儲區分配存儲空間,而C++語言則使用newdelete表達式實現相同的功能。

1. 動態數組的定義與初始化

動態分配數組只需指定類型和數組長度,沒必要爲數組對象命名,new表達式返回指向新分配數組的第一個元素的指針:

int *pia = new int[10]; // array of 10 uninitialized ints
inr *pia2 = new int[10](); // 10 elements of ints array initialized to 0

new表達式須要指定指針類型以及在方括號中給出的數組維數,該維數能夠是任意的複雜表達式。在動態分配數組時,若想初始化內置類型,則只能採用值初始化方式,而類類型無論有沒有值初始化都會調用默認構造函數初始化。總的來講,動態分配的數組,其數組元素只能初始化爲元素類型的默認值,而不能採用初始化列表的方式提供初值。

2. 容許動態分配空數組

size_t n = get_size();
int *p = new int[n];
for(int *q = p; q != p + n; ++q)
    /* process the array *

上例中,即便n = 0(get_size返回0)時,程序仍能正常運行。C++雖然不容許定義長度爲0的數組變量,但使用new動態建立長度爲0的數組是合法的。

對數組爲零的動態數組容許的操做包括:比較運算,指針加減零運算。

3. 動態空間的釋放

若是再也不須要使用動態建立的數組,程序員必須顯式地將其佔用的存儲空間返還給自由存儲區。C++中釋放指針所指向的數組空間:

delete [] pia;

關鍵字delete與指針間的空方括號是必不可少的,不然將產生內存泄漏(memory leak)。

3、字符數組與C風格字符串

1. 字符數組

字符數組既能夠用一組由花括號括起來,逗號隔開的字符字面值進行初始化,也能夠用一個字符串字面值進行初始化。需注意的是字符串字面值包含一個額外的空字符(null)用於結束字符串,例如:

char chs1[] = {‘C’, '+', '+'};
char chs2[] = {'C', '+', '+', '\0'};
char chs3[] = "C++"; // null terminator added automatically

其中,字符數組chs2和chs3值相同,而且維數都是4。

動態字符數組的定義與初始化:

char *pNewChars = new char[newSize]; // uninitialized
if(pNewChars != NULL)
{
    char *iter = pNewChars;
    while(iter != (pNewChars + newSize))
        *iter++ = '\0';
}

2. C風格字符串(C-style character string)

實際上,C風格字符串既不能確切地歸結爲C語言的類型,也不能歸結爲C++語言的類型,能夠說它是以空字符null結束的字符數組,例如上面的ch2和ch3,字符串字面值是該類型的實例。

C++語言中通常經過(const) char*類型的指針來操縱C風格字符串。須要注意的是,C風格字符串必定是以null結束的,若是發現char*的數組中不是以null結束,那麼它就不是C風格字符串,這樣來看,C風格字符串實際上是字符數組的一個子集。

const char* cp = "some value"; // null terminated

標準庫類string

string類型支持長度可變的字符串。要使用string類對象,必須包含相關頭文件:

#include <string>
表1 幾種初始化string對象的方式

string s1;                                  默認構造函數,空字符串

string s2(s1);                            將s2初始化爲s1的一個副本

string s3("Value");                    將s3初始化爲一個字符串字面值副本

string s4(n, 'c');                        將s4初始化爲字符'c'的n個副本

表中的第三種初始化方式就是使用字符串字面值(屬於C風格字符串類型)進行初始化string類對象的,但須要注意的是string類型不符合C風格字符串的特徵,例如:

string str("Hi, C++");
cout<<"string size = "<<str.size()<<endl; // size = 7

string對象會自動將字符串字面值末尾null結束符去掉,所以獲得的string對象size = 7。但string類提供了一個名爲c_str的成員函數來轉換獲得C風格字符串:

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

注意:c_str函數返回的指針是指向const char類型的數組,須要賦給const char*類型

C風格字符串的標準庫函數

C語言標準庫提供了一系列處理C風格字符串的庫函數,須要包含C頭文件:

#include <cstring>

cstring是string.h頭文件的C++版本,string.h文件則是C語言的標準庫。

表2 C風格字符串的標準庫函數

strlen(s)                               返回s的長度,不包括字符串結束符null

strcmp(s1, s2)                    比較兩個字符串s1和s2是否相同,相等返回0;若s1大於s2返回正數,若s1小於s2返回負數

strcat(s1, s2)                    將字符串s2鏈接到s1,並返回s1

strcpy(s1, s2)                    將s2複製給s1,並返回s1

strncat(s1, s2, n)           將s2的前n個字符鏈接到s1後面,並返回s1

strncpy(s1, s2, n)           將s2的前n個字符複製給s1,並返回s1

在使用這些標準庫函數時,必須保證傳遞的指針具備非零值,而且指向以null結束的字符數組中的第一個元素。對於會修改傳入字符串的標準庫函數,程序員必須確保目標字符串必須足夠大,不然會產生嚴重錯誤。

正是存在不少缺陷,C++中推薦儘可能使用標準庫類型string來代替這些操做,提升安全性和效率。

 

Reference book: C++ primer

相關文章
相關標籤/搜索