第四章 数组和指针
4.1 数组
数组的长度是固定 的,而无法直接知道一个给定数组的长度。数组一经创建,就不允许添加新的元素 。 没有所有元素都是引用的数组 ,即数组元素不能是引用。在函数体外定义的内置数组 ,其元素均初始化为 0 。在函数体内定义的内置数组 ,其元素无初始化 。不管数组在哪里定义,如果其元素为类类型 ,则自动调用该类的默认构造函数进行初始化 ;如果该类没有默认构造函数,则必须为该数组的元素提供显式初始化。 不能用一个数组初始化另一个数组。
int a[2] = {1,2};
int b[2];
b = a; //非法操作
4.2 指针
& 取地址操作符只能用于左值。 指针只能指向同类型的对象。 如果需要在一个声明语句中定义两个指针,必须在每个变量标识符前再加符号 * 声明 :
int a = 3;
int *p1, *p2; //定义了两个int类型的指针
int *p3, p4; //定义了一个int型的指针p3和一个int变量p4
p1 = &a; //p1指针指向a变量
std::cout << *p1 <<endl; //输出p所指向变量的值
void 类型的指针可以 指向任意类型的变量 *,该类型指针不能操纵他所指向的对象 。仅支持以下操作: 1.与另一个指针进行比较. 2.作为参数 或者函数的返回值 . 3.给另一个 void* 指针赋值指针和引用的区别: 1.引用在定义时必须初始化,且它不能修改 。 2.给引用赋值是修改该引用关联对象的值,给指针赋值是使指针指向新的对象,原指向对象的值不改变。
int a = 1, b = 2;
int *p1 = &a, *p2 = &b;
p1 = p2; //现在p1指向了b变量
int &r1 = a, &r2 = b;
r1 = r2; //此时b的值被赋给a
在指针上加上(或减去)一个整型数值 n 等效于获得一个新指针, 该新指针指向指针原来指向的元素之后(或之前)的第 n 个元素。 允许在指针上加减0,使指针保持不变。更有趣的是,如果一指针具有 0 值(空指针),则在该指针上加 0 仍然是合法的,结果得到另一个值为 0 的指针。 也可以对两个空指针做减法操作,得到的结果仍是 0。 - 用指针遍历数组
const int N = 5;
int a[N] = {0, 1, 2, 3, 4};
//p1指向数组第一个元素,p2指向最后一个元素之后的那个位置。
for(int *p1 = a, *p2 = a + N; p1 != p2; ++p1)
cout << *p1 <<" ";
指向const对象的指针:const限定的是指针cp所指向的对象类型,而不是指针本身,可以修改使其指向另一个const对象,但不允许修改其所指对象的值。 不能使用“void*”指针保存const对象的地址,必须使用“const void*” 指针。
const int a = 1;
int b = 2;
const int c = 3;
const int *cp; //这里指针cp指向一个int类型的const对象,也就是说该指针只能指向const的对象
cp = &a; //合法
cp = &b; //非法,该指针只能指向const对象
cp = &c; //合法
int *p2;
p2 = &a; //非法,不能将一个const对象的地址赋给一个非const对象的指针。
const指针:该指针不可修改,但是该指针指向对象的值是可以修改的const指针也必须在定义时初始化。任何试图给const指针赋值的行为都不合法(及时用它自己给自己赋值)。
int a = 1, b = 2;
int *const p = &a;
*p = 3; //合法
p = &b; //非法
指向const对象的const指针:既不能修改该指针,又不能修改该指针所指向的对象的值 。
typedef string *pstring;
const pstring cstr;
上例不能理解为 const string *cstr;而应该理解为 string *const cstr;
4.3 C风格字符串
C++ 语言通过(const)char*类型的指针来操纵 C 风格字符串。 常用C风格字符串操作函数(需包含头文件 #include ):
函数 描述 strlen(s) 返回s的长度,不包括字符串结束符。 strcmp(s1, s2) 若s1==s2,返回0;s1>s2,返回正数;否则返回负数。 stract(s1, s2) 将s2连接到s1末尾,返回s1 strcpy(s1, s2) 将s2赋值给s1 strncat(s1, s2, n) 将s2前n个字符连接到s1后面,返回s1 strncpy(s1, s2, n) 将 s2 的前 n 个字符复制给 s1,并返回 s1
strlen总是假定其参数字符串以null字符结束 ,若不是则会出错。
char ca[] = {'C', '+', '+'}; //没有null结束符
cout << strlen(ca) << endl; //出错,不能这样使用
每一个程序在执行时都占用一块可用的内存空间,用于存放动态分配的对象,此内存空间称为程序的自由存储区或堆 。在自由存储区中创建的数组对象是没有名字的,只能通过地址 访问。
int *pia = new int[10]; //分配了一个含有10元素的数组,并将数组首指针返回给pia
动态分配数组时,如果数组元素具有类类型 ,将使用该类的默认构造函数实现初始化 ;如果数组元素是内置类型,则无初始化 。也可使用跟在数组长度后面的一对空圆括号 ,对数组元素做值初始化。对于动态分配的数组,其元素只能初始化为元素类型的默认值 ,而不能像数组变量一样,用初始化列表为数组元素提供各不相同的初值。如果我们在自由存储区中创建的数组存储了内置类型的const对象 ,则必须为这个数组提供初始化 。
int *pia2 = new int[10] (); //初始化为0
// 错误,未初始化const对象
const int *pci_bad = new const int[100];
// 正确
const int *pci_ok = new const int[100]();
C++ 允许定义类类型的const数组,但该类类型必须提供默认构造函数。 C++ 虽然不允许定义长度为0的数组变量,但调用 new 动态创建长度为0的数组是合法的。new动态创建长度为0的数组时,new返回有效的非零指针 。该指针与new 返回的其他指针不同,不能进行解引用操作 ,因为它毕竟没有指向任何元素,可以进行比较,加0,减去自身得到0。 C++ 语言为指针提供delete[]表达式释放指针所指向的数组空间,如果遗漏方框会产生内存泄漏,发生严重错误。
int *p = new int[n]();
delete [] p;
string 类型的加法操作需要两个操作数,可以使用 C 风格字符串作为其中的一个操作数 ,也允许将C风格字符串用作复合赋值操作的右操作数 。反之:在要求C 风格字符串的地方不可直接使用标准库 string 类型对象。如无法使用string对象初始化字符指针 :
string s2("he");
char *str = st2; //错误
char *str = st2.c_str(); //使用c_str()函数可以完成赋值,该函数返回指向字符数组首地址的指针
const size_t arr_size = 6;
int int_arr[arr_size] = {0, 1, 2, 3, 4, 5};
// ivec has 6 elements: each a copy of the corresponding element in int_arr
vector<int> ivec(int_arr, int_arr + arr_size);
vector<int> ivec(int_arr + 1, int_arr + 4);
4.4多维数组
int ia[3][4] = {
{0,1,2, 3},
{4,5,6,7},
{4,5,6,7}
}; //声明并且初始化一个三行四列的二维数组
int ib[3][4] = {0, 3, 6, 9}; //该声明初始化了第一行的元素,其余元素都被初始化为 0。