C++自修入門實境秀、C++ Primer 5版研讀秀 18/ ~陣列、Pointers Are Iterators 指標是迭代器-201907...
3.5. Arrays 陣列
An array is a data structure that is similar to the library vector type (§ 3.3 , p. 96 ) but offers a different trade-off between performance and flexibility.
陣列(array)是類似程式庫vector型別(§ 3.3 ),但在效能和彈性之間提供了不同的取捨。
陣列(數組一組由索引數字操作的值)是一種資料結構
vector則是程式庫型別,乃較高階、抽象的
unnamed objects
不具名的叫object,具名的叫變數veriable
除了動態陣列,一般的陣列是不能改變大小的
tip
If you don’t know exactly how many elements you need, use a vector .
如果不知要用上多少元素,就選用vector,不要用array
16:00
3.5.1. Defining and Initializing Built-in Arrays
Arrays are a compound type (§ 2.3 , p. 50 ).
這樣就更明確何謂「compound type」了
是用/依/藉由別的型別——尤其是基礎型別——來定義自己的型
30:30複合型別(compound type)就是以其他型別來定義的一種型別。(中文版頁50)
複合型別是以其他型別來定義的一種型別。
compound type (複合型別)以其他型別定義的一個型別。(頁78)
如是,翻成「衍生型別」還比較適合。
由複合型別(compound type)來找本書前文,可見它是和「基礎型別」離不開關係的
40:00
The number of elements in an array is part of the array’s type. As a result, the dimension must be known at compile time, which means that the dimension must be a constant expression(參見頁65).
這就有複合型別(compound type)「複合」的意義了
47:00 constexpr變數
51:00陣列中的元素都會被預設初始化。
1:2:18陣列不能用auto,其型別必須有明確的指定
1:3:00 和vector一樣,array只承載實體的物件對象object,因此不會參考(reference)的array
頁114
Explicitly Initializing Array Elements
必須明確地初始化陣列內的元素
1:10:00
元素值的初始化,若給定的不夠用,則會從頭一個元素開始用給定的初始器來初始化,剩下不夠的則其值會被預設值初始化value initialized
1:17:20
Character Arrays Are Special
字元陣列可以用字串字面值去初始化它的元素
When we use this form of initialization, it is important to remember that string literals end with a null character.
當我們使用字串字面值去初始化一個字元陣列時,須要記住字串的尾端永遠掛了一個null的字元。
this form of initialization
這 樣 的 初始化
中文版漏了一個「use」:
當我們用這種形式的初始化,很重要的是要記得字串字面值(string literals)會以一個null字元結尾。
終止符(terminator) null='\0' 1:33:00
char a2[] = {'C', '+', '+', '\0'}; // list initialization, explicit null
中文是一個字有2個char
No Copy or Assignment
陣列是沒有所謂的拷貝或指定(值)的
不能用其他陣列來拷貝初始化一個陣列。
Understanding Complicated Array Declarations
瞭解一下(比較)複雜/繁複的陣列宣告
→2:29:00 瞭解一下(比較)複雜/繁複的含有/涉及陣列的宣告;就是一個宣告,含有陣列,但其宣告,可能是宣告一個指標或參考。
1:49:00
就像vector(向量),陣列也可以存放幾乎任何型別的物件對象object(就是不能有「參考」,參考並非「物件」,而是對物件的「參考」,或物件對象的影子、或其別名而已)
1:56:00
int (*Parray)[10] = &arr; // Parray points to an array of ten int s
如果不加圓括號(),則預設的情況下,「*」(型別修飾符 type modifiers)會優先修飾前面的型別int。所以要加圓括號,才能優先修飾後面的[](在宣告或定義中的[],就是陣列型別)所以在這裡是說這是一個指向陣列的指標指針而不是一個指向int的指標指針。這裡的int是指陣列元素的型別
頁115 2:5:00
By default, type modifiers bind right to left. Reading the definition of ptrs from right to left (§ 2.3.3, p. 58) is easy: We see that we’re defining an array of size 10, named ptrs, that holds pointers to int.
這裡談閱讀的方向,其實就是九陽神功 的④倒序重組 嘛。要掌握正確的讀文方向,不是死板拘泥於一端一種的
int (*Parray) [10] = &arr; // Parray指向由十個 int 組成的一個陣列
int (&arrRef) [10] = arr; // arriRef 指涉十個 int 所成的一個陣列
2:14:30先讀圓括弧,在讀圓括弧右邊、再左邊,這就叫由內向外讀
int *(&arry)[10] = ptrs; // arry is a reference to an array of ten pointers
一樣,括起來的先讀解讀就對了:
1.(&arry)這是一個叫作「arry」的參考
2.讀右邊[10],可見這個參考是對大小為10的陣列的參考
3.再讀左邊:這個大小為10的陣列,元素為int*型別——指向int的指標型別。
練習3.27
unsigned buf_size = 1024;
int ia[buf_size];
Severity Code Description
Error (active) E0028 expression must have a constant value
constexpr unsigned buf_size = 1024;
int ia[buf_size];
const unsigned buf_size = 1024;
int ia[buf_size];
改成以上二種均可
constexpr是隱含的const
int ia[4*7-14];
OK,「4*7-14」是常數運算式
int txt_size() {
return 4*7-14;
}
int ia[txt_size()];
Severity Code Description
Error (active) E2404 function call must have a constant value in a constant expression
改成下面就好了:
constexpr int txt_size() {
return 4*7-14;
}
int ia[txt_size()];
(d)
char st [11 ] = "fundamental";//溢位
Severity Code Description
Error (active) E0144 a value of type "const char [12]" cannot be used to initialize an entity of type "char [11]"
所以「"fundamental"」編譯器是看成含有12個cont char元素的陣列,而不是一個string型別的字串
這樣可以理解為什麼之前用加號「+」時,必然有一邊要是string型別的變數,不能兩端都是字串字面值(string literal)的緣故了。因為編譯器會把兩邊的字串字面值讀成兩個字元陣列在相加。陣列都不可以指定與拷貝了,又怎麼可以相加呢?
練習3.28
3:1:00
string sa[10];//10個空字串
int ia[10];//10個0
int main() {
string sa2[10];//10個空字串
int ia2[10];//10undefined元素,因為在函式中並無預設初始化
}
這題主要是考你陣列的默認/預設初始化在函式內和函式外的差別
練習3.29
使用陣列(數組)必須知道其大小(元素多少),比較沒有靈活性
陣列好像也不能用iterator迭代器,就不能解參考(dereference)了。
頁116
3:11:00
3.5.2. Accessing the Elements of an Array
用 range for或 subscript下標來存取陣列中的元素,一如對vector、string型別一樣
size_t
When we use a variable to subscript an array, we normally should define that variable to have type size_t. size_t is a machine-specific unsigned type that is guaranteed to be large enough to hold the size of any object in memory. The size_t type is defined in the cstddef header, which is the C++ version of the stddef.h header from the C library.
除了陣列是固定大小這一點有點不同外,我們用陣列的方式其實是和用vector大同小異
3:24:00
unsigned scores[11] = {}; // 11 buckets, all value initialized to 0
{}這個表示串列初始化(list initialization),但不指定一個元素,所以剩下的都用預設的初始值來初始化(值初始化:value initialized);所以才會全部都是0,詳頁114:
如果尺寸大於初始器的數目,那些初始器會用於前面的元素,而剩餘的所有元素都是值初始化的(value initialized,§3.3.1)
在這裡,初始器的數目是0。
3:34:00
As in the case of string or vector, it is best to use a range for when we want to traverse the entire array.
就跟使用string或vector時一樣,要巡訪(traverse)整個陣列,最好使用一個範圍 for。
這句其實就是前面看不太懂那段話的註解。
traverse譯巡訪或巡覽都可以
3:44:00
為什麼陣列這裡用「traverse 、traversal」而vector、string乃至container那裡用迭代(iterate)呢?
Checking Subscript Values
3:47:10
3:51:40
Nothing stops a program from stepping across an array boundary except careful attention to detail and thorough testing of the code. It
只有透過小心留意細節而且通盤仔細地檢查整個程式碼才能防止應用程式在執行運算時超出一個陣列的邊界
3:58:00
緩衝區溢位(buffer overflow)
Warning
The most common source of security problems are buffer overflow bugs.Such bugs occur when a program fails to check a subscript and mistakenly uses memory outside the range of an array or similar data structure.
這種臭蟲會在程式沒有檢查下標而錯誤地使用到陣列或類似陣列的資料結構(它們)範圍外的記憶體時產生。
頁117
練習3.30
constexpr size_t array_size = 10;
int ia[array_size];
for (size_t ix = 1; ix <= array_size; ++ix)
ia[ix] = ix;
//if this is to traverse ,ix should be 0;
//ix must < array_size,never equal
when ix=10 then exit the loop then:
Run-Time Check Failure #2 - Stack around the variable 'ia' was corrupted. occurred
練習3.31
就是練習3.30的程式修改後就可以了
constexpr size_t array_size = 10;
int ia[array_size];
for (size_t ix = 0; ix < array_size; ++ix) {
ia[ix] = ix;
練習3.32
constexpr size_t array_size = 10;
int ia[array_size]; int iaCopy[array_size];
for (size_t ix = 0; ix < array_size; ++ix) {
ia[ix] = ix;
}
for (size_t ix = 0; ix < array_size; ++ix) {
iaCopy[ix] = ia[ix];
}
改用vector寫:
constexpr size_t array_size = 10;
vector< int> iv(10,1); vector<int>ivCopy;// (10, 0);
unsigned i = 0;
for (decltype(iv.begin()) ix = iv.begin(); ix != iv.end(); ++ix) {
*ix = i;
++i;
}
ivCopy = iv;//vector可以拷貝
if (iv==ivCopy)//vector可以作相等與關係運算(頁103,表3.5)
{
return;//iv與ivCopy are equal
}
練習3.33
unsigned scores[11];// = {}; // 11個叢集,所有的值都初始化為0
//沒有初始化,除非放到函式外可用預設初始值,否則計數器不會從0開始計算
unsigned grade;
while (cin >> grade) {
if (grade <= 100)
++scores[grade / 10]; //為目前的叢集遞增其計數器
3.5.3. Pointers and Arrays
在C++中指標和陣列的關係是非常密切的
編譯器通常都會將陣列轉換為指標
5:23:50
容器vector 有迭代器(iterator) 就像陣列有指標(pointer)
5:28:00
編譯器會直接將陣列取代為substitue指標且是指向該陣列第一個元素的指標
string *p2 = nums; // 等同於p2 = &nums[0]
看起來好像省略了(不必不用到)取址運算子(&)
Note
In most expressions, when we use an object of array type, we are really using a pointer to the first element in that array.
在大多數的情況下,我們好像用了一個陣列,實際上只是用了一個對該陣列第一個元素的指標
當然我們用一個auto型別指定符來定義一個變數而用一個陣列將之初始化,則auto讓我們得到的結果將會是指標,而不是陣列
頁118
5:41:00
auto ia2(&ia[0]); // now it's clear that ia2 has type int*
這裡的「&」是取址運算子,雖然在宣告與定義中,但並不是用來修飾auto 或變數名稱ia2的,所以不是「參考」型別的修飾詞。
auto ia2(&ia[0]);
就等於「auto ia2=&ia[0];」
在用decltype時,同樣的情形卻不會發生,編譯器並不會將陣列轉換substitue為指標,傳回的型別將會是具有某個大小的陣列
int i = 2;
int ia[] = { 0,1,2,3,4,5, 6,7,8,9 };
auto ia3(ia);
//auto ia3=ia ;和上式相等
*ia3 = i;
留言