第四章 數組和指針
1、類似於vector和iterator高級符合類型,數組和指針是低級符合類型。數組不允許添加元素,指針則可以像迭代器遍歷和檢查數組中的元素;
數組無法進行size、push_back等操作
數組是由類型名、標識符和尾數組成的複合數據類型
2、vector 使用 vector::size_type 作爲下標的類型,而數組下標的正確類型則是 size_t(size_t爲bitset.size()返回二進制位的個數,)
數組的維數必須用值大於等於1的常量表達式定義:整型字面值常量、枚舉常量、常量表達式初始化的整型const對象(非const變量以及到運行階段才知道其值的const變量都是非法的)
內置類型初始化爲0,若是類類型則調用該類的默認構造函數進行初始化。
字符數組的兩種表達方式:
char ca1[] = {'C', '+', '+'}; // no null維數3
char ca2[] = {'C', '+', '+', '\0'}; // explicit null維數4
char ca3[] = "C++"; // null terminator added automatically 維數4
// null作爲空字符用於結束字符串
3、數組的長度是固定的,區別於vector類型,數組不提供push_back或者其他的操作在數組中添加新元素。管理好內存很關鍵。
導致安全問題的最常見原因是所謂“緩衝區溢出(buffer overflow)”錯誤。當我們在編程時沒有檢查下標,並且引用了越出數組或其他類似數據結構邊界的元素時,就會導致這類錯誤。
與使用標準 vector 類型的程序相比,依賴於內置數組的程序更容易出錯而且難於調試。
4、4.2指針的引入
vector的遍歷可使用下標、迭代器,數組的遍歷使用下標、指針。
指針是指向某種類型對象的複合數據類型,是用於數組的迭代器:指向數組中的一個元素。
解引用操作符 * dereference operator 自增操作符 ++ increment operator
指針用於指向對象。類似於迭代器,指針提供對其所指對象的間接訪問。指針用於指向單個對象,而迭代器只能用於訪問容器內的元素。
指針保存的爲另一個對象的地址,只能初始化或賦值爲同類型的變量地址或另一指針。void *可以保存任何類型對象的地址。
指針聲明語句時,請從右向左閱讀。
除了使用數值0或在編譯時值爲 0 的 const 量外,還可以使用 C++ 語言從 C 語言中繼承下來的預處理器變量 NULL,該變量在 cstdlib 頭文件中定義,其值爲 0。如果在代碼中使用了這個預處理器變量,則編譯時會自動被數值 0 替換。
給指針賦值:對左操作數進行解引用,修改的是指針所指對象的值。
通過指針進行賦值:沒有使用解引用操作,修改的是指針本身的值。
引用必須初始化,始終指向同一個特定對象。
指針本身也可用指針指向的內存對象。指針佔用內存空間存放其值,因此指針的存儲地址可存放在指針中。
**操作符指派一個指針指向另一指針。
兩個指針的減法操作結果是標準庫類型ptrdiff_t的數據。ptrdiff_t類似於size_t類型,機器相關的類型,在cstddef頭文件中定義。size_t 是 unsigned類型,而 ptrdiff_t 是 signed類型。
lsat = *(ia + 4) 指的是ia[0]後的第四個元素
計算數組的超出末端指針
for循環的性質:只要定義的多個變量具有相同的類型,就可以在for循環的初始化與劇中定義他們。
for (int *pbegin = int_arr, *pend = int_arr + arr_size; pbegin != pendl; ++ pbegin)
類似於其他內置類型,數組也沒有成員函數。因而,數組不提供begin和end操作。
const size_t arr_sz = 5;
int int_arr[arr_sz] = { 0, 1, 2, 3, 4 };
// pbegin points to first element, pend points just after the last
for (int *pbegin = int_arr, *pend = int_arr + arr_sz;
pbegin != pend; ++pbegin)
cout << *pbegin << ' '; // print the current element
// equivalent loop using iterators to reset all the elements in ivec to 0
for (vector<int>::iterator iter = ivec.begin();
iter != ivec.end(); ++iter)
*iter = 0; // set element to which iter refers to 0
內置數組類型具有標準庫容器的性質,與數組聯合使用的指標本身是迭代器。
如果指針所指的對象是const類型,那麼指針也必須是const類型。const int *noddle,約定的是prt而不是noddle。
const類型的變量,必須要求對應的指針也是const類型。指向const的指針指向的對象可以不是const類型。
void *指針也同樣要求const void *纔可以保存const變量。
int errNumb = 0;
int *const curErr = &errNumb; // curErr is a constant pointer const指針的表達方式
curErr 是指向 int 型對象的 const 指針
const double *const pi_ptr = π
指針和typedef
typedef string *pstring;
const pstring cstr;
cstr的類型是const pstring類型,更進一步的是指向string類型對象的const指針。
等價於string *const cstr;
const int ic = 1; 定義了一個int型const對象ic,並用int型對象對其進行初始化
const int *p = ⁣ 定義指向int型const對象的指針p,並用ic的地址對其進行初始化
int *const p = ⁣ 定義int型對象的const對象,但是初始化的是const int型對象ic的地址
const int *const p = ⁣ 定義指向int型const對象的const指針p,並用ic的地址對其進行初始化。
5、4.3 C-style 的字符串
C風格字符串是以空字符null結束的字符數組
字符串字面值的類型就是const char類型的數組。
C++語言通過 const char* 類型的指針來操作C風格字符串,碰到null終止運行。使用指針的算術操作來遍歷C風格字符串:
const char *cp = “some value”;
while (*cp){
++cp;
}
操縱C風格字符串的標準庫函數(必須要有null結束),必須包含相應的C頭文件:#include<cstring>
strlen(s)返回s長度,不包括字符串結束符null
strcmp(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
6、4.3.1動態數組
每一個程序在執行時都佔用一塊可用的內存空間,用於存放動態分配的對象,內存空間稱爲程序的自由存儲區或者堆。
int *pia = new int[10] (); new和delete語句進行內存空間的劃定,類型+數組維數。
const int *pia = new const int[100];
允許動態分配空數組:
size_t n = get_size();
int *p = new int[n];
for(int *q = p; q != p +n; ++q)
釋放空間 delete [ ] pia;
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> ivec;
int numbers;
//讀入元素數據並建立vector
cout << "Enter nubmers:" << endl;
while(cin>> numbers)
ivec.push_back(numbers);
//創建一個與vector相同的數組
size_t n = getsize(ivec);
int *p = new int[n];
int *tp = p;
for(vector<int>::iterator iter = ivec.begin(); iter != ivec.end(); ++iter,++tp)
*tp = *iter;
deleter [] p;
return 0;
}
標準庫string與C風格的轉換:char *str = st2.c_str();
c_str函數返回C風格字符串,即返回指向字符數組首地址的指針,以null結尾。
int ia[3][4] = {
(0,1,2,3),
(4,5,6,7),
(8.9.10.11) };
多維數組的指針:typedef
typedef int int_array[4];
int_array *p = ia;
for (int_array *p = ia; p != ia + 3; ++p)
for (int *q = *p; q != *p + 4; ++q)
cout << *q << endl;
標準輸入設備讀入字符串,並存放到字符數組。處理可變長的輸入
#include<iostream>
#include<string>
#include<cstring> //c++中的c字符串
int main()
{
string s1;
const size_t n = 10;
char s2[n+1];
cout << "Enter a string(<=" << n << "characters):" << endl;
cin >> s1;
//比較len與n的關係,進而判斷
size_t len = strlen(s1.c_str());
if(len > n){
len = n;
cout << "String is longer than" << n << "characters and is stored only"
<< n << "characters!" << endl;
}
//複製元素並實現添加
strcpy(s2, s1, len);
s2[len+1] = "\0"; 末尾添加空字符
return 0;
}
用int型數組初始化vector對象:vector 從int數組中選擇
把int型vector複製給int型數組: int *p = new int[ivec.size()] delete [ ] p;
4.34例題:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main()
{
//讀入string類型的數據,並存到vector中
string st1;
vector<string> ivec;
cout << "please input a string:" << endl;
while(cin >> st1)
ivec.push_back(st1);
//複製到字符指針數組,爲元素創建數組
char **p = new char*[ivec.size()]; //創建的“字符指針數組”需要加兩個※
//爲每一個vector元素創建一個字符數組
size_t n = 0;
for(vector<string>::iterator iter = ivec.begin(); iter != ivec.end(); ++iter,++n){
char *pat = new char[(*iter).size()+1];
//複製vector元素的數據到字符數組
strcpy(pat, (*iter).c_str());
//將指向該字符數組的指針插入到字符指針數組
p[n] = pat;
}
//釋放各個字符數組
for(ix = 0; ix != ivec.size();++ix)
delete [] p[ix];
//釋放字符指針數組
delete [] p;
return 0;
}
用typedef簡化指向多維數組的指針
typedef int int_array[4];
int_array *p = ia;
for(int_array *p = ia; p != ia+3; ++p)
for(int *q = *p; q !=*p+4;++q)
cout<< *q <<endl;
int (*p)[4]是指向4個元素的數組的指針
int *p[4]是指向int型的數組指針
vector爲更靈活更容易管理的數組
string是C風格字符串的改進類型,而C風格字符串是以空字符結束的字符數組。指針和迭代器都能訪問所指向的對象。