1. 程式人生 > >字串、向量和陣列(三)

字串、向量和陣列(三)

1.陣列

陣列是一種類似於標準庫型別vector一種資料結構,但是在效能和靈活度上和vector又有所差異。與vector不同的是,陣列的大小固定,不能動態地往裡面新增元素,在一些特殊的場合使用陣列,效能會好一些,但是相應地靈活度會差一些。

1.1定義和初始化陣列

陣列是一種符合型別,形式為:a[d],a為陣列名,d為維度,即陣列中元素的個數,它也是陣列型別的一部分,編譯的時候維度應該是已知的,所以維度應該是一個常量表達式。下面是一些例子:

unsigned num1=3; //不是常量表達式
constexpr unsigned num2=12;//常量表達式
int arr[10]; //正確 含有10個整數的陣列
int *parr[10]; //正確 含有10個整形指標的陣列 string str[num1]; //錯誤,num1不是常量表達式 string strs[get_size()]; //如果get_size返回的值是常量時候正確

預設情況下,陣列的元素會被預設初始化。定義陣列的時候必須要指定陣列的型別,不能使用auto去推測型別,同時和vector一樣,裡面的元素必須是物件,不存在引用的陣列。

顯示初始化陣列元素

可以對陣列的元素進行列表初始化,這個時候可以忽略陣列的維度,編譯器可以推測出陣列的維度;如果指定了陣列的維度,那麼初始值的總量就不能大於維度,否則出錯;如果初始值的數量小於維度,那麼剩下的元素將被初始化成預設值。下面看一些例子:

const unsigned int size=3;
int arr1[size]={0,1,2};
int arr2[]={0,1,2} //arr2的維度為3
int arr3[5]={0,1,2}; //元素為0,1,2,0,0
string str1[3]={"hello","world"};//元素為:hello world ""
int arr4[2]={1,2,3};//初始值的數量大於維度,錯誤

字元陣列的特殊性

字元陣列有一種額外的初始方式,就是使用字串字面值進行初始化。當使用這種方式的時候需要注意,在字面值的結尾處還有一個空白符,這個空字元也會被拷貝到字元陣列中去。下面看一下舉例:

char
ch1[]={'h','e'}; //沒有空字元 char ch2[]={'h','e','\0'}; //含有一個顯示的空字元 char ch3[]="C++"; //自動新增表示字串結束的空字元 char ch4[5]="apple"; //錯誤 沒有空間存放空字元

不允許拷貝和賦值

不能將陣列的內容拷貝給其他的陣列作為其初始值,也不能用陣列賦值給其他陣列。例如:

int a[]={1,2,3};
int b[]=a; //錯誤,不能將陣列的內容拷貝給其他陣列作為其初始值
b=a; //錯誤,不能將陣列賦值給其他陣列

理解複雜的陣列宣告

和vector一樣,陣列能存放大多數的型別。外面可以定義存放指標的陣列,因為陣列也是物件,所以我們還可以定義陣列的指標和陣列的引用,後兩種在定義上比較複雜。例子:

int *ptrs[10]; //ptrs是含有10個整型指標的陣列
int &refs[10]; //不存在引用的陣列
int (*Parray)[10]=&arr; //陣列的指標,Parray指向一個含有10個整數的 陣列
int (&arrRef)[10]=arr; //陣列的引用 arrRef引用一個含有10個整數的陣列

預設情況下,型別修飾符從右往左依次繫結。如果有括號,就是按照由內到外的順序,例如上述的Parray和arrRef。對於修飾符的 數量,並沒有具體限制,看一個例子:

int *(&arr)[10]=ptrs;

按照由內而外的順序閱讀,首先arr是一個引用,然後往右看知道arr引用的物件是一個大小為10的陣列,最後觀察左邊發現 ,陣列的元素型別是指向int的指標。分析得到,arr是一個含有10個int型指標的陣列的引用。

2.訪問陣列元素

陣列的元素可以使用範圍for語句或者是下標運算子來訪問,陣列元素的索引是從0開始的。在使用陣列下標的時候,通常將其的型別定義為size_t。size_t在標頭檔案cstddef定義,是一種機器相關的 無符號型別,它被設計得足夠大以便能表示記憶體中任意物件的大小。除了大小固定這一特點外,其他用法與vector物件相似。下面是一個使用陣列統計各個分數段成績的例子:

unsigned score[11]={};
unsigned grade;
while(cin>>grade)
{
    ++score[grade/10]; //以每10分為一段
}

與vector和string物件一樣,陣列可以使用範圍for語句遍歷陣列中所有的元素,下面使用範圍for遍歷輸出陣列score中所有的元素:

for(auto s:score)
{
   cout<<s<<" ";
}
cout<<endl;

因為維度是陣列的一部分,編譯器知道陣列中有多少元素,所以使用範圍for可以減少人為控制遍歷過程的負擔。

檢查下標的值

和string和vector物件一樣,陣列需要檢測下標是否合法,下標必須大於等於0小於陣列長度。如果陣列越界,它在編譯的時候可能順利執行通過,所以需要謹慎處理細節才能避免一些致命的錯誤。常見的越界訪問非法記憶體帶來的問題就是緩衝區溢位錯誤。

1.3指標和陣列

C++語言中,陣列和指標有著緊密的聯絡。使用陣列的時候,編譯器一般會將陣列轉化為指標。取地址符適用於任何物件,同樣也適合陣列,可以使用取地址符得到指向該元素的指標,例如:

string num[]={"hello","world","haha"};
string *p=&num[0]; //指標p指向陣列num的第一個元素

陣列還有一個特性,就是在很多使用到陣列的地方,編譯器會自動將其轉化為指向陣列第一個元素的指標,例如:

string num[]={"hello","world","haha"};
string *p=num; //指標p指向num的第一個元素

由此 可以感知,有些情況下,對陣列的操作其實就是對指標的操作。這裡面有很深的意思,其中一層的意思是使用陣列作為auto變數的一個初始值時候,推斷出來的型別為指標而不是陣列。例如:

int a[]={1,2,3};
auto b(a); //b的型別是int* 等價於auto b(&a[0]);

需要注意的是,如果使用decltype關鍵字時,上述的轉化將不會發生。看下面的例子:

int a[10]={};//a有10個元素,初始值為0
decltype(a) b; //decltype(a)的返回值是有10個元素的陣列
b=p; //不能將指標賦值給陣列
a[5]=5; //正確,將5賦值給陣列中的第六個元素

指標也是迭代器

指向陣列元素的指標具有更多功能,string和vector的迭代器所支援的運算,陣列的指標全部支援。例如可以使用遞增運算子是指向陣列元素的指標向前移動一個位置:

int ptr[10]={1,2,3,4,5};
int *p=ptr; //指標p指向第一個元素
p++; //指標p指向ptr[1]

使用迭代器可以遍歷vector物件中的元素,同樣使用指標也可以遍歷陣列中的元素,前提是獲得指向陣列首元素的指標和指向陣列尾元素下一個位置的指標。首指標通過陣列名或者是或者陣列中首元素的地址獲得,尾元素則要使用到陣列的一個特殊性質:外面可是設法獲取尾元素後面那個並不 存在的元素的地址。例如:

int a[10]={};
int *end=&a[10]; //end指向尾元素的下一位置,不指向具體的元素

尾後後指標不指向具體的元素,因此不允許尾指標解引用和遞增操作。下面是一個使用指標遍歷陣列所有元素的例子:

int a[10]={};
int *end=&a[10];

for(int *start=a;start!=e;++start)
{
   cout<<*start<<" ";
}
cout<<endl;

標準庫函式begin和end

為了解決上述尋找尾後指標容易出錯的情況,C++11提供了標準庫函式begin和end用於獲取首指標和尾後指標,函式傳入的引數為陣列。下面是一個 用指標遍歷陣列,尋找出第一個負數的例子;

int a[10]={1,2,3,4,5,-9,10,89,76,80};
int *begin=begin(a);
int *end=end(a);
while(begin!=end && *begin>=0) //遇到負數,退出迴圈
{
    ++begin;
}

指標運算

指向陣列元素的指標可以執行所有迭代器的運算,這些運算包括解引用、遞增、與整數相加、兩個指標相加減等,用在指標和迭代器上的意義完全一樣。
從一個指標加上(減去)一個整數值,結果仍然是指標。新指標指向的元素與原來的指標相比,向前移動了該整數個位置。

constexpr size_t sz=5;
int arr[5]={1,2,3,4,5};
int *p=arr; //p指向首元素
int *p2=p+4; //p4指向arr[4]

給指標加上一個整數,要麼是指向同一陣列的其他元素,要麼指向尾元素的下一位置。例如:

int *p=arr+sz; //p指向尾元素的下一位置
int *p2=arr+10; //錯誤,通過計算所得 的指標超出了上述範圍就將產生錯誤,但更為嚴重的是,編譯器發現不了這個錯誤

兩個指標相減的結果的型別是一種名為ptrdiff_t的標準庫型別,和size_t一樣,ptrdiff_t也是一種定義在cstddef標頭檔案中的機器相關的型別。因為差值可能為負值,所以ptrdiff_t是一種帶符號型別。
只要兩個指標指向的是同一個陣列的元素,或者是指向尾元素的下一位置,那麼就能使用關係運算符對它們進行比較;如果兩個指標指向不同的物件,則它們不能進行比較。上述的指標運算同樣適用於空指標和所指物件並非是陣列的指標。在後一種情況中,兩個指標所指的必須是同一個物件或者物件的下一位置。如果是空指標,允許加上或者減去一個值為0的常量表達式,兩個空指標允許相減,結果是0。

解引用和指標運算的互動

如果指標指向陣列中的一個元素,就可以解引用該指標,得到所指向的值。如果表示式中有解引用運算子,那麼就需要注意括號是否需要,例如:

int a[5]={1,2,3,4,5};
int last=*(a+4); //last為a[5]的值
int last=*a+4; //last的值為a[0]+4,注意和上面的區別

下標和指標

對陣列執行下標運算實際上就是對陣列元素的指標執行下標運算,只要指標指向的是陣列中的元素或者尾元素的下一位置,都可以進行下標運算,例如:

int *p=&a[2]; //p指向a[2];
int num=p[1]; //p[1]等價於*(p+1),即為a[3]
int k=p[-2]; //p[-2]等價於*(p-2),即為a[0]

在這邊我們可以看出,陣列的下標可以是負數,而string和vector雖然也能執行下標運算,但是下標必須是無符號型數。

4.C風格字串

C標準庫String函式

它們用於操作C語言風格的字串,放在cstring標頭檔案中。

傳入此函式的指標必須指向以空字元作為結束的陣列,例如:

char a[]={'a','b','c'};
cout<<strlen(a)<<endl; //錯誤,a並沒有以空字元結尾
char b[]={'a','b','c','\0'};
cout<<strlen(b)<<endl; //正確,b是以空字元為結尾

比較字串

比較標準庫string物件的 會後,用的是普通的關係運算符和相等性運算子;如果使用這些運算子用在兩個C風格字串上,實際上比較的將是指標而非字串本身。例如:

string s1="A String haha";
string s2="A String game";
if(s1>s2) //true:s1大於s2

const char ca1[]="A String a";
const char ca2[]="A String b";
if(ca1<ca2) //兩個都是const char*型別 未定義的:試圖比較兩個無關地址

對於C語言風格的字串比較,需要使用strcmp函式,此時比較的不再是指標。如果兩個字串相等,返回0,;如果前一個字串的字面值較大,返回一個正數;如果第二個字串的字面值較大,則返回一個負數。例如:

if(strcmp(ca1,ca2)) 

目標字串的大小由呼叫者決定

連線和拷貝C語言風格的字元應該使用strcat和strcpy函式。要使用這兩個函式,還需要提供一個存放字串的陣列,必須儲存該陣列的大小足夠放下結果字串和空字元,這種操作充滿風險,極易發生錯誤。例如:

//在 這邊必須保證largeStr的大小足夠大
strcpy(largeStr,cal); //將cal拷貝給largeStr
strcat(largeStr," "); //在largeStr的尾部加上一個空格
strcat(largeStr,ca2); //把ca2連線到largeStr的後面

1.5與舊程式碼的介面

C++提供了一組功能簡化這工作。

混用string物件和C風格字串

我們經常會使用字串字面值初始化string物件,更一般的情況是,任何出現字串字面值的地方都可以用以空字元結束的字元陣列來替代。
如果程式需要使用C語言風格字串,無法使用string物件來替代它。我們 不能用string物件初始化一個指向C語言風格字串的指標,但是我們可以使用string類的c_str成員函式來返回一個指向一個以空字元為結尾的字元陣列,這個陣列所存的資料和string物件的一樣。例如:

string s("hello");
const char *pstr=s.c_str(); //指標的型別是const char*,陣列中的所有元素不能內修改

使用陣列初始化vector物件

C++規定不允許使用陣列給內建型別的陣列賦值,也不允許使用vector物件給陣列賦初值,但是允許陣列給vector物件賦初值。要實現這個目的,我們只需提供陣列的首地址和尾後地址就可以了,例如:

int a[]={1,2,3,4,5};
vector<int> ivec(begin(a),end(a)); //ivec將包含6個元素,順序和值 都和陣列一樣

用於初始化vector物件的值也可以僅僅是陣列的一部分,例如:

vector<int> ivec(a+1,a+4); //ivec含有3個元素,分別為a[1] a[2] a[3]

現代C++程式要求儘量使用標準庫型別而不是陣列。

2.多維陣列

嚴格意義上講,C++沒有定義多維陣列,通常的多維陣列其實是陣列的陣列。對於二維陣列而言,通常把第一個維度 稱為行,第二個維度稱為列。

相關推薦

字串向量陣列

1.陣列 陣列是一種類似於標準庫型別vector一種資料結構,但是在效能和靈活度上和vector又有所差異。與vector不同的是,陣列的大小固定,不能動態地往裡面新增元素,在一些特殊的場合使用陣列,效能會好一些,但是相應地靈活度會差一些。 1.1定義和初始

C++primer筆記-第3章 字串變數陣列標準庫型別string

 標準庫string、vector都是對內建陣列型別的抽象,string支援變長的字串,vector表示可變長的集合。迭代器是string和vector的配套型別,常用於訪問string或者vector中的元素。 string作為標準庫的一部分,定義在名稱空間s

字串向量陣列

一、標頭檔案中不應該包含using namespace XXX;的宣告。 二、標準庫 string(標頭檔案為<string>)         string與”test“字串字面值不是同種型別的。 1、初始化 #include <string&

c++ primer 第五版學習筆記-第字串向量陣列

本文為轉載,出處:https://blog.csdn.net/libin1105/article/details/48210261                               https://blog.csdn.net/fnoi2014xtx/article/

Android 基於Netty的訊息推送方案之字串的接收傳送

在上一篇文章中《Android 基於Netty的訊息推送方案之概念和工作原理(二)》 ,我們介紹過一些關於Netty的概念和工作原理的內容,今天我們先來介紹一個叫做ChannelBuffer的東東。 ChannelBuffer  Netty中的訊息傳遞,都必須以位元

整合學習之boosting,AdaboostGBDT xgboost

AdaBoost演算法的解釋——前向分步法與提升樹(GBDT) 可以認為AdaBoost演算法是模型為加法模型,損失函式為指數函式、學習演算法為前向分步演算法時的二類分類學習方法。 前向分步演算法: 考慮加法模型

c++字串向量陣列

前一段時間忙著趕專案進度,現在就接著之前學習的內容繼續學習。 1、c++的內建型別(數字和字元等),體現了大多數計算機硬體本身具備的能力。 2、位於標頭檔案中的程式碼一般不用using進行宣告,因為標頭檔案的內容會拷貝到所有引用它的檔案中去。 3、關於直接初始化和拷貝初

【C++筆記】字串向量陣列

string(C++Primer 5th) string & vector   string表示可變長的字元序列,vector存放的是某種給定型別物件的可變長序列。 using宣告   標頭檔案不應包含using宣告,否則標頭檔案的內容會

第二章:字串向量陣列

字串、向量和陣列 名稱空間的using宣告 略過… 標準庫型別string 標準庫型別string表示可變長的字元序列,使用string型別必須首先包含string標頭檔案。 定義和初始化string物件 string s0;

cocos2d-x動作特效動畫扭曲動作

    CCActionInterval *actionTo = CCSkewTo::create(2, 70.0f, 0.0f);       CCActionInterval *actionToBack = CCSkewTo::create(2, 0, 0);  

C++——字串向量陣列

標準庫型別vectorvector物件(以及string物件)的下標運算子可用於訪問已存在的元素,而不能用於新增元素。只能對確知已存在的元素執行下標操作。迭代器所有標準庫容器都可以使用迭代器,string物件不屬於容器型別,但是支援很多與容器型別類似的操作。const vec

2. 字串向量陣列

第三章 字串、向量和陣列 標頭檔案中不應該使用using的宣告,因為標頭檔案的內容會拷貝到所有引用它的檔案中去,若標頭檔案中使用了using,則每個引用該標頭檔案的檔案都會有這個宣告。 std::string std::string的初始化方式:

執行緒程序協程GIL

上一篇文章介紹了:建立縣城的兩種方式、Event物件判斷執行緒是否啟動、利用訊號量控制執行緒併發。 部落格連結:執行緒、程序、協程和GIL(二) 這一篇來說說執行緒間通訊的那些事兒:    一個執行緒向另一個執行緒傳送資料最安全的方式就是使用queue庫中的隊列了,通過建立一個供多個執行緒共享

Go語言入門——陣列切片對映

上篇主要介紹了Go語言裡面常見的複合資料型別的宣告和初始化。 這篇主要針對陣列、切片和對映這些複合資料型別從其他幾個方面介紹比較下。 1、遍歷   不管是陣列、切片還是對映結構,都是一種集合型別,要從這些集合取出元素就要查詢或者遍歷。   對於從其他語言轉到Go語言,在遍歷這邊還是有稍稍不同的。 陣

Oracle學習筆記—Db_nameDb_domainGlobal_nameService_nameInstance_nameOracle_SID轉載

安全 文件中 分布 好處 避免 名稱 detail 數據庫安全 自動 轉載自: Oracle中DB_NAME,SID,DB_DOMAIN,SERVICE_NAME等之間的區別 Db_name:對一個數據庫(Oracle database)的唯一標識。這種表示對於單個數據

SVN服務器搭建使用

進行 文本框 logs 版本 強制寫 移動 對話 刪除文件 對話框 接下來,試試用TortoiseSVN修改文件,添加文件,刪除文件,以及如何解決沖突等. 添加文件 在檢出的工作副本中添加一個Readme.txt文本文件,這時候這個文本文件會顯示為沒有版本控制的狀態,如圖

轉載【項目管理構建】——Maven下載安裝配置

文檔 port 目標 軟件項目管理 strong mar temp mave work 原文鏈接: http://blog.csdn.net/jiuqiyuliang/article/details/41076215 在現實

多線程編程學習筆記——asyncawait

result ask aps nta cti ise 線程編程 學習筆記 top 接上文 多線程編程學習筆記——async和await(一) 接上文 多線程編程學習筆記——async和await(二) 五、 處理異步操

Saltstack數據系統GrainsPillar

centos服務器 items har zabb ini highstate fqdn clas deb Saltstack數據系統 分為Grains和Pillar SaltStack 數據系統 Grains (谷粒) Pillar (柱子)

Java並發編程:CountDownLatchCyclicBarrierSemaphore 總結

資源 bsp 狀態 java並發 其他 相等 blog 重用 www    下面對上面說的三個輔助類進行一個總結:   1)CountDownLatch和CyclicBarrier都能夠實現線程之間的等待,只不過它們側重點不同:     CountDownLa