第四章 数组和指针
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风格字符串是以空字符结束的字符数组。指针和迭代器都能访问所指向的对象。