指针和数组基本等价的原因在于:指针算数(pointer arithmetic)和C++内部处理数组的方式。首先看算数,整数加1后值增加1,指针加1后增加的量等于指向的类型的字节数。以下示例还说明了C++将数组名解释为地址:
#include <iostream>
using namespace std;
int main()
{
double list1[3] = {1.0, 2.0, 3.0};
short list2[3] = {1, 2, 3};
//两种取数组地址方式
double *p1 = list1; //数组名等同首元素地址
short *p2 = &list2[0]; //首元素地址
cout << "p1 = " << p1 << ", *p1 = " << *p1 << endl; //输出:p1 = 0x28ccf0, *p1 = 1.0
p1 += 1;
cout << "p1 = " << p1 << ", *p1 = " << *p1 << endl; //输出:p1 = 0x28ccf8, *p1 = 2.0
return 0;
}
上述代码中,list1[1] 等价于 *(list1+1),即先计算数组第2个元素的地址,然后找到存储在那里的值。
从数字上说,list2 和 &list2 这两个地址相同;从概念上说,&list2[0]是一个2字节内存的地址,而 &list2 是一个6字节内存的地址。
4.8.3 指针和字符串
数组和指针的关系可以扩展到 C 风格字符串。
char flower[10] = "rose";
cout << flower << endl;
4.8.4 使用 new 创建动态结构
以下是使用 new、delete 存储通过键盘输入的字符串的示例。getname() 函数返回一个指向输入字符串的指针。该函数将输入读入到一个大型的临时数组中,然后使用 new[] 创建一个刚好能够存储改字符串的内存块,并返回指向该内存块的指针。这种方法可以节省大量内存。
#include <iostream>
#include <cstring>
using namespace std;
char* getname()
{
//临时存储输入
char temp[80];
cout << "Enter last name: ";
cin >> temp;
//存入大小合适的内存块
char p = new char[strlen(temp) + 1];
strcpy(p, tmp);
return p;
}
int main()
{
char *name = getname();
cout << name << endl;
delete [] name;
//重新使用
name = getname();
delete [] name;
return 0;
}
4.8.5 自动存储、静态存储、动态存储
这是C++的3中管理数据内存的方式。C++11新增线程存储。
- 自动存储
在函数内部定义的常规变量使用自动存储空间,称为自动变量(automatic variable)。调用函数时自动产生,调用结束后消亡。自动变量通常存储在栈中。这意味着执行代码块时,其中的变量将依次加入到栈中,而在离开代码块时,将按相反的顺序释放这些变量,这被称为后进先出(LIFO)。因此,在程序执行过程中栈将不断地增大和缩小。
- 静态存储
静态存是整个程序执行期间都存在的存储方式。使变量成为静态的方式有两种:一种是在函数外定义它;另一种是在声明变量时使用关键字static。
- 动态存储
new、delete 运算符提供了一种比自动变量和静态变量更灵活的方法。它们管理了一个内存池,这在C++中被称为自由存储空间(free store)或堆(heap)。上述代码表明 new、delete 让您能够在一个函数中分配内存,而在另一个函数中释放它。因此,数据的生命周期不完全受程序或函数的生存时间控制。虽然有更大的控制权,但是内存管理也更复杂了。在栈中,自动添加删除机制使得占用的内存总是连续的;但 new、delete 的相互影响可能导致占用的自由存储区不连续,这使得跟踪新分配内存的位置更困难。