八、指针和const限定符
指向const对象的指针
const double *cptr; // cptr may point to a double that is const
const限定了cptr指针所指向的对象类型,而并非cptr本身。也就是说,cptr本身并不是const。
允许通过给cptr赋值,使其指向一个const对象,但不能通过cptr修改其所指向的对象的值。
不能使用void*指针保存const对象的地址,而必须使用const void*类型的指针保存const对象的地址:
const int universe = 42;
const void *cpv = &universe; // ok: cpv is const
void *pv = &universe; // error: universe is const
不能使用指向const对象的指针修改基础对象,然而如果该指针指向的是一个非const对象,可用其他方法修改其所指的对象。
九、const指针
C++语言还提供了const指针——本身的值不能修改:
int errNumb = 0;
int *const curErr = &errNumb; // curErr is a constant pointer
curErr = curErr; // error: curErr is a constant pointer
指向const对象的const指针,既不能修改所指对象的值,也不允许修改指针的指向。
假设给出以下语句:
typedef string *pstring;
const pstring cstr;
请问cstr变量是什么类型:
const string *pstring; // wrong interpretation of const pstring cstr
string *const cstr; // equivalent to const pstring cstr,等价于这句话
C风格字符串
C风格字符串的标准库函数(要使用这些标准库函数,必须包含相应的C头文件:cstring)
strlen(s) strcmp(s1, s2) strcat(s1, s2) strcpy(s1, s2) strncat(s1, s2, n) strncpy(s1, s2, n)
注意:这些标准库函数不会检查其字符串参数
永远不要忘记字符串结束符null,调用者必须确保目标字符串具有足够的大小
如果必须使用C风格字符串,则使用标准库函数strncat和strncpy比strcat和strcpy函数更安全
char largeStr[16 + 18 + 2]; // to hold cp1 a space and cp2
strncpy(largeStr, cp1, 17); // size to copy includes the null
strncat(largeStr, " ", 2); // pedantic, but a good habit
strncat(largeStr, cp2, 19); // adds at most 18 characters, plus a null
对大部分的应用而言,使用标准库类型string,除了增强安全性外,效率也提高了,因此应该尽量避免使用C风格字符串。
创建动态数组
动态数组的定义
int *pia = new int[10]; // array of 10 uninitialized ints
new表达式返回指向新分配数组的第一个元素的指针
初始化动态分配的数组
可使用跟在数组长度后面的一对空圆括号,对数组元素做值初始化:
int *pia2 = new int[10](); // array of 10 uninitialized ints
对于动态分配的数组,其元素只能初始化为元素类型的默认值,而不能像数组变量一样,用初始化列表为数组元素提供各不相同的初值。
const对象的态数组
// error: uninitialized const array
const int *pci_bad = new const int[100];
// ok: value-initialized const array
const string *pci_ok = new const int[100]();
允许动态分配空数组
char arr[0]; // error: cannot define zero-length array
char *cp = new char[0]; // ok: but cp can't be dereferenced
用new动态创建长度为0的数组时,new返回有效的非零指针。该指针与new返回的其他指针不同,不能进行解引用操作,因为它毕竟没有指向任何元素。而允许的操作包括:比较运算,因此该指针能在循环中使用;在该指针上加(减)0;或者减去本身,得0值。
动态空间的释放
动态分配的内存最后必须进行释放,否则,内存最终将会逐渐耗尽。
使用数组初始化vector对象
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> ivcec(int_arr, int_arr + arr_size);
多维数组
严格地说,C++中没有多维数组,通常所指的多维数组其实就是数组的数组。
代码::
#include <stdio.h>
main()
{
static int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; /* 定义一个3行4列的二维数组 */
int *p;
printf ("%d %d/n",a,*a);
printf ("%d %d/n",a[0],*(a+0));
printf ("%d %d/n",&a[0],&a[0][0]);
printf ("%d %d/n",a[0][0],*(*(a+0)+0));
for (p=a[0];p<a[0]+12;p++) /* 把a[0]的地址赋予指针变量p,条件判断p<a[0]+12表示的是小于最后一个元素的地址; */
{
if ((p-a[0])%4==0) /* 利用整数指针变量p减去当前地址判断出是不是已经显示出了四个值,换行回车 */
{
printf ("/n");
}
printf ("%4d",*p); /* 打印出元素的值 */
}
printf ("/n");
}
/*
第 5行中的a和*a打印出来的值,就会让人弄不明白我们知道数组传递的地址那么a表示这个数组的其实地址为什么*a却不是实际值呢?原因是在多维数组中 a+0表示的是第0行的首地址,a+1表示是第一行的首地址,那么*a其实就是*(a+0),那么第一个元素其实是a[0][0]
而*(a+0) 仅仅是把一个3行4列的二维数组拆为了三个一维数组,*(a+0)显然表示的不是物理位置也就不可能得到第一个元素的值了,它仅仅是一个内存地址也就是第 0行的启始地址,再看8行中的*(*(a+0)+0),它表示的则是第0行第0列个元素的值也就是a[0][0],再次强调*(a+0)表示的是数组第一行的首地址,也就是第二行的*(a+1),而元素值要得到显然就是*(*(a+0)+0)了如果是第0行第1个也就是*(*(a+0)+1)。
*/