指针和数组基本等价的原因在于:指针算数(pointer arithmetic)和C++内部处理数组的方式。首先看算数,整数加1后值增加1,指针加1后增加的量等于指向的类型的字节数。以下示例还说明了C++将数组名解释为地址:

  1. #include <iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. double list1[3] = {1.0, 2.0, 3.0};
  6. short list2[3] = {1, 2, 3};
  7. //两种取数组地址方式
  8. double *p1 = list1; //数组名等同首元素地址
  9. short *p2 = &list2[0]; //首元素地址
  10. cout << "p1 = " << p1 << ", *p1 = " << *p1 << endl; //输出:p1 = 0x28ccf0, *p1 = 1.0
  11. p1 += 1;
  12. cout << "p1 = " << p1 << ", *p1 = " << *p1 << endl; //输出:p1 = 0x28ccf8, *p1 = 2.0
  13. return 0;
  14. }

上述代码中,list1[1] 等价于 *(list1+1),即先计算数组第2个元素的地址,然后找到存储在那里的值。

从数字上说,list2 和 &list2 这两个地址相同;从概念上说,&list2[0]是一个2字节内存的地址,而 &list2 是一个6字节内存的地址。

4.8.3 指针和字符串

数组和指针的关系可以扩展到 C 风格字符串。

  1. char flower[10] = "rose";
  2. cout << flower << endl;

4.8.4 使用 new 创建动态结构

以下是使用 new、delete 存储通过键盘输入的字符串的示例。getname() 函数返回一个指向输入字符串的指针。该函数将输入读入到一个大型的临时数组中,然后使用 new[] 创建一个刚好能够存储改字符串的内存块,并返回指向该内存块的指针。这种方法可以节省大量内存。

  1. #include <iostream>
  2. #include <cstring>
  3. using namespace std;
  4. char* getname()
  5. {
  6. //临时存储输入
  7. char temp[80];
  8. cout << "Enter last name: ";
  9. cin >> temp;
  10. //存入大小合适的内存块
  11. char p = new char[strlen(temp) + 1];
  12. strcpy(p, tmp);
  13. return p;
  14. }
  15. int main()
  16. {
  17. char *name = getname();
  18. cout << name << endl;
  19. delete [] name;
  20. //重新使用
  21. name = getname();
  22. delete [] name;
  23. return 0;
  24. }

4.8.5 自动存储、静态存储、动态存储

这是C++的3中管理数据内存的方式。C++11新增线程存储。

  1. 自动存储

在函数内部定义的常规变量使用自动存储空间,称为自动变量(automatic variable)。调用函数时自动产生,调用结束后消亡。自动变量通常存储在栈中。这意味着执行代码块时,其中的变量将依次加入到栈中,而在离开代码块时,将按相反的顺序释放这些变量,这被称为后进先出(LIFO)。因此,在程序执行过程中栈将不断地增大和缩小。

  1. 静态存储

静态存是整个程序执行期间都存在的存储方式。使变量成为静态的方式有两种:一种是在函数外定义它;另一种是在声明变量时使用关键字static。

  1. 动态存储

new、delete 运算符提供了一种比自动变量和静态变量更灵活的方法。它们管理了一个内存池,这在C++中被称为自由存储空间(free store)或堆(heap)。上述代码表明 new、delete 让您能够在一个函数中分配内存,而在另一个函数中释放它。因此,数据的生命周期不完全受程序或函数的生存时间控制。虽然有更大的控制权,但是内存管理也更复杂了。在栈中,自动添加删除机制使得占用的内存总是连续的;但 new、delete 的相互影响可能导致占用的自由存储区不连续,这使得跟踪新分配内存的位置更困难。