第3章:字符串、向量和數(shù)組

  • #1.命名空間的using聲明
  • #2.標(biāo)準(zhǔn)庫類型string
    • 2.1 定義和初始化string對象
    • 2.2 string對象上的操作
    • 2.3 處理string對象中的字符
  • #3.標(biāo)準(zhǔn)庫類型vector
    • 3.1 定義和初始化vector對象
    • 3.2 向vector對象中添加元素
    • 3.3 其他vector操作
  • #4.迭代器介紹
    • 4.1 使用迭代器
    • 4.2 迭代器運算
  • #5.數(shù)組
    • 5.1 定義和初始化內(nèi)置數(shù)組
    • 5.2 訪問數(shù)組元素
    • 5.3 指針和數(shù)組
    • 5.4 C風(fēng)格字符串
    • 5.5 與舊代碼的接口
  • #6.多維數(shù)組

#1. 命名空間的using聲明

using聲明的作用是無須專門的前綴(形如命名空間::)也能使用所需的名字。using聲明具有如下形式:

using namespace::name;

一旦聲明了上述語句,就可以直接訪問命名空間中的名字:

#include <iostream>

//using聲明,當(dāng)我們使用名字cin時,從命名空間std中獲取它
using std::cin;
int main() {
    int i;
    cin >> i; //正確:cin和std::cin的含義相同
    cout << i; //錯誤:沒有對應(yīng)的using聲明,必須使用完整的名字
    std::cout << i; //正確:顯示地從std中使用cout
    return 0;
}
每個名字都需要獨立的using聲明

按照規(guī)定,每個using聲明引入命名空間中的一個成員。

#include <iostream>

using std::cin;
using std::cout;
using std::endl;
int main() {
    cout << "Enter two nnumbers:" << endl;
    int v1, v2;
    cin >> v1 >> v2;
    cout << "The sum of " << v1 << " and " << v2 << " is " << v1 + v2 << endl;
    system("pause");
    return 0;
}
頭文件不應(yīng)包含using聲明

位于頭文件的代碼一般來說不應(yīng)該使用using聲明。這是因為頭文件的內(nèi)容會拷貝到所有引用它的文件中去。如果在頭文件中有using聲明,那么每個使用了該頭文件的文件都會有這個聲明??赡軙斐擅譀_突。


#2. 標(biāo)準(zhǔn)庫類型string

標(biāo)準(zhǔn)庫string表示可變長的字符序列,使用string類型必須首先包含string頭文件。作為標(biāo)準(zhǔn)庫的一部分,string定義在命名空間std中。

#include <string>
using std::string

2.1 定義和初始化string對象

如何初始化類的對象由類本身決定。一個類可以定義很多種初始化對象的方式,只不過這些方式之間必須有區(qū)別:或者是初始值的數(shù)量不同,或者是初始值的類型不同。

string s1; //默認(rèn)初始化,s1是一個空串
string s2(s1); //s2是s1的副本
string s2 = s1; //等價于s2(s1),s2是s1的副本
string s3("value"); //s3是字面值"value"的副本,除了字面值后面的那個空字符外
string s3 = "value"; //等價于s3("value"),s3是字面值"value"的副本
string s4(n,'c'); //把s4初始化為連續(xù)n個字符c組成的串
直接初始化和拷貝初始化

C++語言有幾種不同的初始化的方式,通過string我們可以清楚地看到這些初始化方式之間的差別和聯(lián)系。如果使用等號(=)初始化一個變量,實際上執(zhí)行的是拷貝初始化,編譯器把等號右側(cè)的初始化值拷貝到新創(chuàng)建的對象中去。與之相反,如果不使用等號,則執(zhí)行的是直接初始化

string s5 = "hiya"; //拷貝初始化
string s6("hiya"); //直接初始化
string s7(10,'c'); //直接初始化,s7的內(nèi)容是cccccccccc

string s8 = string(2,'c');//拷貝初始化,s8的內(nèi)容是cc
//s8所執(zhí)行的操作,可以分解為下面兩個步驟:
//【1】用數(shù)字2和字符c兩個參數(shù)創(chuàng)建出來一個string對象,【2】然后這個string對象又拷貝給了s8
string tmp(2,'c'); //tmp的內(nèi)容是cc
string s8 = tmp; //將tmp拷貝給s8

2.2 string對象上的操作

一個類除了要規(guī)定初始化其對象的方式之外,還要定義對象上能執(zhí)行的操作。

讀寫string對象
#include <iostream>
#include <string>

using namespace std;

int main() {
    string s; //空字符串
    cin >> s; //將string對象讀入s,遇到空白停止
    cout << s << endl; //輸出s
}
讀取未知數(shù)量的string對象
#include <iostream>
#include <string>

using namespace std;

int main() {
    string word;
    while(cin >> word) { //反復(fù)讀取直到文件末尾
        cout << word << endl; //逐個輸出單詞,每個單詞后面跟一個換行。
    }
    return 0;
}
使用getline讀取一整行

有時我們希望能在最終得到的字符串中保留輸入時的空白字符,這時應(yīng)該用getline函數(shù)代替原來的>>運算符。getline的參數(shù)是一個輸入流和一個string對象,函數(shù)從給定的輸入流中讀取內(nèi)容,值到遇到換行符為止(換行符也被讀進(jìn)來了),然后把所讀的內(nèi)容存入到那個string對象中去(注意不存換行符)。getline只要一遇到換行符就結(jié)束讀取操作并返回結(jié)果。

#include <iostrem>
#include <string>

using namepace std;

int main() {
    string line;
    //每次讀取一整行,直到文件的末尾
    while(getline(cin,line)) {
        cout << line << endl;
    }
    return 0;
}

==觸發(fā)getline函數(shù)返回的那個換行符實際上被丟棄掉了,得到的string對象中并不包含該換行符。==

string的empty和size操作

empty函數(shù)是根據(jù)string對象是否為空返回一個布爾值,size函數(shù)返回string對象的長度(即string對象中字符的個數(shù))。

string::size_type類型

string類及其它大多數(shù)標(biāo)準(zhǔn)庫類型都定義了幾種配套的類型,這種配套類型體現(xiàn)了標(biāo)準(zhǔn)庫類型與機器無關(guān)的特性,類型size_type即是其中一種。

==如果表達(dá)式中已經(jīng)有了size()函數(shù)就不要再使用int了,這樣可以避免混用int和unsigned可能帶來的問題。==

2.3 處理string對象中的字符

我們經(jīng)常需要處理string對象中的字符,比如檢查一個string對象是否包含空白,或者把string對象中的字母改寫成小寫,再或者查看某個特定的字符是否出現(xiàn)等。

處理每個字符?使用基于范圍的for語句

如果想對string對象中的每個字符執(zhí)行什么操作,目前最好的辦法是使用C++11新標(biāo)準(zhǔn)提供的一個語句:范圍for語句。

for(declaration : expression) {
    statement
}

使用范圍for語句把string對象中的字符每行一個個輸出:

#include <iostream>
#include <string>

using namespace std;

int main() {
    string str("helloworld");
    //每行輸出str的一個字符
    for(auto c: str) { //對于str中的每個字符
        cout << c << endl; //輸出當(dāng)前字符,后面緊跟換行符
    }
    return 0;
}
使用范圍for語句改變字符串的內(nèi)容

如果想要改變string對象中字符的值,必須把循環(huán)變量定義成引用類型。如果我們想把字符串改寫為大寫字母的形式:

int main() {
    string s("helloworld!!!");
    for(auto &c : s) { //對于s中的每個字符
        c = toupper(c); //c是一個引用,因此賦值語句將改變s中的字符值
    }
    cout << s << endl;
}
只處理一部分字符?

要想訪問string對象中的單個字符有兩種方式:一種是使用下標(biāo),另外一種是使用迭代器。下標(biāo)運算符([])接收到參數(shù)是string::size_type類型的值,這個參數(shù)表示要訪問的位置;返回值是該位置上字符的引用。

int main() {
    string s("some string");
    if(!s.empty()) { //確保s[0]的位置確實有字符
        s[0] = toupper(s[0]); //將s的首字符設(shè)置為大寫
    }
    return 0;
}
使用下標(biāo)迭代執(zhí)行

將s的第一個詞改寫成大寫形式:

#include <iostream>
#include <string>
#include <cctype>

using namespace std;

int main() {
    string s("hello world");
    for(decltype(s.size()) index = 0;index != s.size()&&!isspace(s[index]);++index) {
        s[index] = toupper[s[index]];
    }
    cout << s << endl;
    return 0;
}

#3. 標(biāo)準(zhǔn)庫類型vector

標(biāo)準(zhǔn)庫類型vector表示對象的集合,其中所有對象的類型都相同。C++語言既有類模板,也有函數(shù)模板,其中vector是一個類模板。

模板本身不是類或函數(shù),相反可以將模板看作為編譯器生成類或函數(shù)編寫的一份說明。編譯器根據(jù)模板創(chuàng)建類和函數(shù)的過程稱為實例化。

==vector是模板而非類型,由vector生成的類型必須包含vector中元素的類型,例如vector<int>。==

3.1 定義和初始化vector對象

和任何類型一樣,vector模板控制著定義和初始化向量的方法。

vector<T> v1; //v1是一個空vector,它潛在的元素是T類型的,執(zhí)行默認(rèn)初始化。
vector<T> v2(v1); //v2中包含有v1所有元素的副本
vector<T> v3(n,val); //v3包含n個重復(fù)的元素,每個元素的值都是val
vector<T> v4(n); //v4包含了n個重復(fù)地執(zhí)行了值初始化的對象
vector<T> v5{a,b,c...}; //v5包含了初始值個數(shù)的元素,每個元素被賦予相應(yīng)的初始值
vector<T> v5 = {a,b,c...}; //等價于v5{a,b,c...}
列表初始化vector對象

C++11標(biāo)準(zhǔn)還提供了另外一種為vector對象的元素賦初值的方法,即列表初始化。

vector<string> svec{"a","b","c"}; //列表初始化
vector<string> svec("a","b","c"); //錯誤
創(chuàng)建指定數(shù)量的元素

還可以用vector對象容納的元素數(shù)量和所有元素統(tǒng)一初始值來初始化vector對象:

vector<int> ivec(10,-1); //10個int類型的元素,每個值被初始化為-1
值初始化

通常情況下,可以只提供vector對象容納的元素數(shù)量而不用略去初始值。此時庫會根據(jù)元素的類型來創(chuàng)建一個值初始化的元素初值,并把它賦給容器中的所有元素。

vector<int> ivec(10); //10個元素,每個都初始化為0

對這種初始化方式有兩個特殊限制:

  1. 有些類要求必須明確提供初始值,如果vector對象中元素的類型不支持默認(rèn)初始化,就必須提供初始值。
  2. 如果只提供元素的數(shù)量,而沒有設(shè)定初始值只能使用直接初始化。
列表初始值還是元素數(shù)量?

在某些情況下,初始化的真實含義依賴于傳遞初始值時用的是花括號還是圓括號。例如,用一個整數(shù)來初始化vector<int>時,整數(shù)的含義可能是vector對象的容量也可能是元素的值。通過花括號和圓括號可以區(qū)分上述含義:

vector<int> v1(10); //v1有10個元素,每個值都是0
vector<int> v2{10}; //v2有1個元素,該元素的值是10

vector<int> v3(10,1); //v3有10個元素,每個的值都是1
vector<int> v4{10,1}; //v4有2個元素,值分別是10和1

如果用的是圓括號(),可以說提供的值是用來構(gòu)造vector對象的;如果用的是花括號{},表示進(jìn)行列表初始化該對象。另一方面,如果初始化時使用了花括號的形式但是提供的值又不能用來列表初始化,就要考慮這樣的值來構(gòu)造vector對象了。

vector<string> v5{"hi"}; //列表初始化:v5有一個元素
vector<string> v6("hi"); //錯誤:不能使用字符串字面值構(gòu)建vector對象
vector<string> v7{10}; //v7有10個默認(rèn)初始化的元素
vector<string> v8{10,"hi"}; //v8有10個值為"hi"的元素

要想使用列表初始化vector對象,花括號的值必須與元素類型相同。

3.2 向vector對象中添加元素

vector提供了成員函數(shù)push_back來向其中添加元素。push_back負(fù)責(zé)把一個值當(dāng)成vector對象的尾元素“壓到(push)”vector對象的“尾端(back)”。

vector<int> v1; //空vector
for (int i = 0; i != 100; i++) {
    v1.push_back(i); //依次把整數(shù)值放到v1的尾端
}
//循環(huán)結(jié)束后v1中有0-99共100個元素
向vector對象添加元素蘊含的編程假定

確保所寫的循環(huán)正確無誤,特別是在循環(huán)有可能改變vector對象容量的時候。如果循環(huán)體內(nèi)部包含有向vector對象添加元素的語句,則不能使用范圍for循環(huán)。

==范圍for語句體內(nèi)不應(yīng)該改變其所遍歷序列的大小。==

3.3 其他vector操作

#include <iostream>
#include <vector>

using namspace std;

int main() {
    vector<int> v{1,2,3,4,5,6,7,8,9};
    for(auto &i : v) { //引用v中的每一個元素
        i*=i; //求平方
    }
    for(auto i: v) { //對于v中的每個元素i
        cout << i << " "; //輸出i的值
    }
    cout << endl;
    return 0;
}


#4. 迭代器的介紹

我們可以使用下標(biāo)運算來訪問string對象的字符或vector對象的元素,另外還有一種更通用的機制也可以實現(xiàn)同樣的目的,使用迭代器。所有的標(biāo)準(zhǔn)庫容器都可以使用迭代器,但是其中只有少數(shù)幾種支持使用下標(biāo)運算符。

4.1 使用迭代器

和指針不一樣的是,獲取迭代器不是使用取地址符,有迭代器的類型同時擁有返回迭代器的成員:beginend成員,其中begin成員負(fù)責(zé)返回指向第一個元素的迭代器,end成員負(fù)責(zé)返回指向容器“尾元素的下一位置”的迭代器。end成員返回的迭代器常被稱作尾后迭代器。

==如果容器為空,則begin和end返回的是同一個迭代器,都是尾后迭代器。==

迭代器運算符
*iter           //返回迭代器所指元素的引用
iter->mem       //解引用iter并獲取該元素名為mem的成員,等價于(*iter).mem
++iter          //令iter指示容器中的下一個元素
--iter          //令iter指示容器中的上一個元素
iter1 == iter2  //判斷兩個迭代器是否相等,如果兩個迭代器指示的是同一個元素或者它們是同一個容器的尾后迭代器,則相等;反之,不相等
iter1 != iter2
#include <iostream>
#include <string>

using namespace std;

int main() {
    string s("hello world");
    if (s.begin() != s.end()) {
        auto it = s.begin();
        *it = toupper(*it);
    }
    std::cout << s << std::endl;
    return 0;
}
將迭代器從一個元素移動到令一個元素

迭代器使用遞增(++)運算符來從一個元素移動到下一個元素。

==因為end返回的迭代器并不實際指示某個元素,所以不能對其進(jìn)行遞增或解引用操作。==

迭代器類型

一般來說我們也不知道迭代器的精確類型。而實際上,那些擁有迭代器的標(biāo)準(zhǔn)庫類型使用iterator和const_iterator來表示迭代器類型:

vector<int>::iterator it1; //it1只能讀寫vector<int>的元素
string::iterator it2; //it2只能讀寫string對象中的字符
vector<int>::const_iterator it3; //it3只能讀元素,不能寫元素
string::const_iterator it4; //it4只能讀元素,不能寫元素
begin和end運算符

begin和end返回的具體類型由對象是否是常量決定,如果對象是常量,begin和end返回const_iterator;如果對象不是常量,返回iterator。

vector<int> v;
const vector<int> cv;
auto it1 = v.begin(); //it1的類型是vector<int>::iterator
auto it2 = cv.begin(); //it2的類型是vector<int>::const_iterator

有時候這種默認(rèn)的行為并非我們所要。如果對象只需要讀操作而無須寫操作的話最好使用常量類型。為了便于得到const_iterator類型的返回值,C++11新標(biāo)準(zhǔn)引入了兩個新函數(shù),分別是cbegin和cend:

auto it3 = v.cbegin(); //it3的類型是vector<int>::const_iterator
結(jié)合解引用和成員訪問操作

解引用迭代器可獲得迭代器所指向的對象,如果該對象的類型恰好是類,就有希望進(jìn)一步訪問它的成員。例如,對于一個由字符串組成的vector對象來說,要檢查其元素是否為空,令it是該vector的迭代器:

(*it).empty();

為了簡化上述表達(dá)式,C++定義了箭頭運算符(->)。箭頭運算符把解引用和成員訪問兩個操作結(jié)合在一起。it->mem和(*it).mem表達(dá)的意思相同。

const vector<string> text{"hello","world"};
for (auto it = text.cbegin(); it != text.cend() && !it->empty(); ++it) {
    std::cout << *it << std::endl;
}
某些對vector對象的操作會使迭代器失效

已知的一個限制是不能在for循環(huán)中向vector對象添加元素。另外一個限制是任何一種可能改變vector對象容量的操作,比如push_back,都會使該vector對象的迭代器失效。

==謹(jǐn)記,任何使用了迭代器的循環(huán)體,都不要向迭代器所屬的容器添加元素。==

4.2 迭代器運算

迭代器的遞增運算令迭代器每次移動一個元素,所有的標(biāo)準(zhǔn)庫容器都有支持遞增運算的迭代器。
String和vector的迭代器提供了更多額外的運算符,一方面可使得迭代器的每次移動可跨過多個元素,另外也支持迭代器進(jìn)行關(guān)系運算。所有的這些運算被稱為迭代器運算。

迭代器的算術(shù)運算

可以令一個迭代器和一個整數(shù)值相加(或相減),其返回值是向前(或向后)移動了若干個位置的迭代器。獲得指向vector中間元素的迭代器:

//計算得到最接近vi中間元素的一個迭代器
auto mid = vi.begin() + vi.size()/2;
使用迭代器運算

使用迭代器運算的一個經(jīng)典算法是二分搜索。

//text必須是有序的
//beg和end表示我們搜索的范圍
auto beg = text.begin(),end = text.end();
auto mid = text.begin() + (end - beg)/2;//初始化狀態(tài)下的中間點
//當(dāng)還有元素尚未檢查并且我們還沒有找到sought時執(zhí)行循環(huán)
while(mid != end&&*mid != sought) {
    if(sought < *mid) {
        end = mid;
    }else {
        beg = mid + 1;
    }
    mid = beg + (end - beg)/2;
}

#5. 數(shù)組

數(shù)組是一種類似于標(biāo)準(zhǔn)庫類型vector的數(shù)據(jù)結(jié)構(gòu),但是在性能和靈活性的權(quán)衡上又與vector有所不同。與vector類似的地方是,數(shù)組中存放的是同類型的元素;與vector不同的是,數(shù)組大小固定不變。

5.1 定義和初始化內(nèi)置數(shù)組

數(shù)組是一種復(fù)合類型。數(shù)組的聲明形式如a[d],其中a是數(shù)組名,d是數(shù)組的維度。數(shù)組的維度必須為常量表達(dá)式:

unsigned cnt = 42;
constexpr unsigned sz = 42; //常量表達(dá)式
int arr[10]; //含有10個整數(shù)的數(shù)組
int *parr[sz]; //含有42個指針的數(shù)組
string bad[cnt]; //錯誤:cnt不是常量表達(dá)式
string strs[get_size()]; //當(dāng)get_size是constexpr時正確,否則錯誤。
顯示初始化數(shù)組元素

可以對數(shù)組元素進(jìn)行列表初始化,此時允許忽略數(shù)組的維度。如果在聲明時沒有指明維度,編譯器會根據(jù)初始值的數(shù)量計算并推測出來;相反,如果指明了維度,那么初始值的總數(shù)量不應(yīng)該超出指定的大小。

const unsigned sz = 3;
int ial[sz] = {0,1,2}; //含有3個元素的數(shù)組,元素值分別為0,1,2
int a2[] = {0,1,2}; //數(shù)組維度為3
string a3[3] = {"hello","c++"}; //等價于a3[] = {"hello","c++",""};
int a4[2] = {0,1,2}; //錯誤:初始值過多
字符數(shù)組的特殊性

字符數(shù)組有一種額外的初始化形式,我們可以使用字符串字面值對此類型的數(shù)組初始化。當(dāng)使用這種方式時,一定要注意字符串字面值的結(jié)尾處還有一個空字符,這個空字符也會像字符串的其他字符一樣被拷貝到字符數(shù)組中去:

char c1[] = {'c','+','+'}; //列表初始化,沒有空字符
char c2[] = {'c','+','+','\0'}; //列表初始化,含有顯示空字符
char c3[] = "c++"; //自動添加表示字符串結(jié)束的空字符
char c4[6] = "Daniel"; //錯誤:沒有空間存放空字符
不允許拷貝和賦值

不能將數(shù)組的內(nèi)容拷貝給其他的數(shù)組作為初始值,也不能用數(shù)組為其他數(shù)組賦值:

int a[] = {0,1,2}; //含有3個整數(shù)的數(shù)組
int a2[] = a; //錯誤:不允許使用一個數(shù)組初始化另一個數(shù)組
a2 = a; //錯誤:不允許把一個數(shù)組直接賦值給另外一個數(shù)組
理解數(shù)組的復(fù)雜聲明

和vector一樣,數(shù)組能存放大多數(shù)類型的對象。又因為數(shù)組本身是對象,所以允許定義數(shù)組的指針和引用。

int *ptrs[10]; //ptrs是含有10個整形指針的數(shù)組
int &refs[10] = /*?*/; //錯誤:不存在引用的數(shù)組
int (*parray)[10] = &arr; //parray指向一個含有10個整數(shù)的數(shù)組
int (&arrRef)[10] = arr; //arrRef引用一個含有10個整數(shù)的數(shù)組
int *(&arr)[10] = ptrs; //arr是數(shù)組的引用,該數(shù)組含有10個指針

==要想理解數(shù)組聲明的含義,最好的辦法是從數(shù)組的名字開始按照從內(nèi)向外的順序閱讀。==

5.2 訪問數(shù)組元素

數(shù)組的元素也能使用范圍for語句或下標(biāo)運算符來訪問。在使用數(shù)組下標(biāo)時,通常將其定義為size_t類型。

檢查下標(biāo)的值

與vector類似,使用下標(biāo)訪問時,需要檢查下標(biāo)的值。避免出現(xiàn)下標(biāo)越界。

5.3 指針和數(shù)組

在使用數(shù)組的時候編譯器一般會把它轉(zhuǎn)換成指針。對數(shù)組元素使用取地址符就能得到指向該元素的指針:

string nums[] = {"one","two","thress"};
string *p = &nums[2]; //p指向nums的第三個元素
string *p2 = nums; //等價于p2 = &nums[0];

==在大多數(shù)表達(dá)式中,使用數(shù)組類型的對象其實是使用一個指向該數(shù)組首元素的指針。==

指針也是迭代器

允許使用遞增運算將指向數(shù)組元素的指針向前移動到下一個位置:

string nums[] = {"one","two","three"};
string *e = &nums[nums->size()]; //指向尾元素的下一位置的指針
std::cout << *e << std::endl;
for (string *s = nums; s != e; s++) {
    std::cout << *s << std::endl;
}
std::cout << std::endl;
標(biāo)準(zhǔn)庫函數(shù)begin和end

C++新標(biāo)準(zhǔn)引入了兩個名為begin和end的函數(shù)。begin函數(shù)返回指向數(shù)組首元素的指針,end函數(shù)返回指向數(shù)組尾元素下一位置的指針。

int iarr[] = {1,2,3,4,5};
int *beg = begin(iarr); //指向iarr首元素的指針
int *end = end(iarr); //指向iarr尾后元素的指針
指針運算
constexpr size_t sz = 5;
int arr[sz] = {1,2,3,4,5};
int *ip = arr; //指向數(shù)組的首元素,等價于int *ip = &arr[0];
int *ip2 = ip + 4; //ip2指向arr的尾元素arr[4]
解引用和指針運算的交互

指針加上一個整數(shù)所得的結(jié)果還是一個指針。

int arr[] = {0,2,4,6,8};
int last = *(arr + 4); //正確,把last初始化成8,即arr[4]的值
下標(biāo)和指針
int ia[] = {1,2,3,4,5};
int i = ia[2]; //ia轉(zhuǎn)換成指向數(shù)組首元素的指針,ia[2]得到(ia+2)所指的元素
int *p = ia; //p指向ia的首元素
i = *(p+2); //等價于i = ia[2]

5.4 C 風(fēng)格字符串

字符串字面值是一種通用結(jié)構(gòu)的實例,這種結(jié)構(gòu)即是C++由C繼承而來的C風(fēng)格字符串。

C標(biāo)準(zhǔn)庫String函數(shù)
strlen(p); //返回p的長度,空字符不計算在內(nèi)
strcmp(p1,p2); //比較p1和p2的相等性,如果p1==p2,返回0;如果p1>p2,返回一個正值;如果p1<p2,返回一個負(fù)值
strcat(p1,p2); //將p2附加到p1之后,返回p1
strcpy(p1,p2); //將p2拷貝給p1,返回p1

5.5 與舊代碼接口

混用string對象和C風(fēng)格字符串
string s("hello world"); //s的內(nèi)容是hello world
char *str = s; //錯誤:不能用string對象初始化char*
const char *str = s.c_str(); //正確

==如果執(zhí)行完c_str()函數(shù)后程序想一直都能使用其返回的數(shù)組,最好將該數(shù)組重新拷貝一份。==

使用數(shù)組初始化vector對象

不允許使用一個數(shù)組為另一個內(nèi)置類型的數(shù)組賦初值,也不允許使用vector對象初始化數(shù)組。相反的,允許使用數(shù)組來初始化vector對象。

int int_arr[] = {0,1,2,3,4,5};
//ivec有6個元素,分別是int_arr中對應(yīng)元素的副本
vector<int> ivec(begin(int_arr),end(int_arr));

#6. 多維數(shù)組

多維數(shù)組即數(shù)組的數(shù)組。當(dāng)一個數(shù)組的元素仍然是數(shù)組時,通常使用兩個維度來定義它:一個維度表示數(shù)組本身的大小,另外一個維度表示其元素大?。?/p>

int ia[3][4]; //大小為3的數(shù)組,每個元素是含有4個整數(shù)的數(shù)組
多維數(shù)組初始化

允許使用花括號括起來的一組值初始化多維數(shù)組:

int ia[3][4] = { //3個元素,每個元素是大小為4的數(shù)組
    {0,1,2,3}, //第1行初始值
    {4,5,6,7}, //第2行初始值
    {8,9,10,11} //第3行初始值
};

其中內(nèi)層嵌套著的花括號并非必需的:

//沒有標(biāo)識每行的花括號,與之前的初始化語句是等價的
int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
多維數(shù)組的下標(biāo)引用

可以使用下標(biāo)運算符來訪問多維數(shù)組,此時數(shù)組的每個維度對應(yīng)一個下標(biāo)運算符。

constexpr size_t rowCnt = 3,colCnt = 4;
int ia[rowCnt][colCnt]; //12個未初始化的元素
//對于每一行
for(size_t i = 0;i != rowCnt;++i) {
    //對于行內(nèi)的每一列
    for(size_t j = 0;j != colCnt;j++) {
        //將元素的位置索引作為它的值
        ia[i][j] = i*colCnt + j;
    }
}
使用范圍for語句處理多維數(shù)組
size_t cnt = 0;
for(auto &row:ia) { //對于外層數(shù)組的每一個元素
    for(auto &col:row) { //對于內(nèi)存數(shù)組的每一個元素
        col = cnt; //將下一個值賦值給該元素
        ++cnt; //將cnt加1
    }
}
指針和多維數(shù)組

當(dāng)程序使用多維數(shù)組名時,編譯器會將其轉(zhuǎn)換成指向數(shù)組首元素的指針。

int arr[3][4];
int (*p)[4] = arr; //p指向arr的首元素,首元素為含有四個整數(shù)的數(shù)組(數(shù)組指針)
p = &arr[2]; //p指向ia的尾元素

==在上述聲明中,圓括號比不可少:==

int *ip[4]; //整形指針的數(shù)組
int (*ip)[4]; //指向含有4個整數(shù)的數(shù)組的指針
類型別名簡化多維數(shù)組的指針
using int_arr = int[4];
for(int_arr *p = ia;p != ia + 3; ++p) {
    for(int *q = *p;q != *p + 4; ++q) {
        cout << *q << '';
    }
    cout << endl;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容