指针
-
指针和自由存储空间
-
指针是一个变量,存储的是值的地址,而不是值的本身
-
例子:找到常规变量的地址
#include <iostream> int main() { using namespace std; int donuts = 6; double cups = 4.5; cout << "甜甜圈的值是:" << donuts << endl; cout << "甜甜圈的地址是:" << &donuts << endl; // 使用地址运算符&可以获得变量的地址 cout << "小蛋糕的值是:" << cups << endl; cout << "小蛋糕的地址是:" << &cups << endl; return 0; }
-
新思路就是把地址视为指定的量,将值视为派生量,指针用于存储值的地址,所以指针名表示的是地址
-
*运算符被称为间接值或解除引用运算符,将这个运算符应用于指针,可以得到这个地址处存储的值
-
例子
#include <iostream> int main() { using namespace std; int updates = 6; // 声明一个变量 int * p_updates; // 声明一个int类型的指针 p_updates = &updates; // 给指针赋值(赋的是地址) cout << "updates的值是:" << updates<< endl; cout << "p_updates的值是:" << *p_updates<< endl; *p_updates = *p_updates +1; cout << "p_updates新的值是:"<< *p_updates << endl; return 0; }- 变量updates表示值,使用&运算符来获取地址
- 变量p_updates表示地址,使用*运算符来获得值
- 变量p_updates执行updates,所以*p_updates和updates完全等价,可以像使用int变量那样使用*p_updates
-
-
声明和初始化指针
-
声明指针的时候必须指定指针指向的数据类型
int * p_updates; // *两边的空格可选,但是对于每个指针变量名,都需要使用一个*- p_updates指向int类型
- p_updates的类型是指向int的指针
- 所以p_updates是指针地址,但是*p_updates是int,不是指针
-
例子:将指针初始化为一个地址
# include <iostream> int main() { using namespace std; int higgens = 5; int * pt = &higgens; cout << "higgens的值是:"<< higgens << endl; cout << "higgens的地址是:" << &higgens << endl; cout << "*pt的值是:" << *pt << endl; cout << "pt的值是:" << pt << endl; return 0; }- higgens和*pt的值一样
- higgens的地址值和pt的值一样
-
-
指针和数字
- 指针描述的是位置,将两个地址相乘没有意义,所以不能简单的将整数赋给指针
-
使用new来分配内存
在运行阶段,指针可以用来分配未命名的内存来存储值,这种情况下,只能通过指针来访问内存
-
使用new运算符来分配内存
int * pn = new int;- new int表示需要适合存储int的内容
- new运算符根据类型来确定需要多少字节的内存,然后找到满足要求的内存返回地址
- 当new运算符找到地址之后,这个地址会被赋给pn
- pn被声明为一个指针,指向int
- *pn是存储在那里的值
-
例子
# include <iostream> int main() { using namespace std; int abc = 1001; int * pointer = new int; *pointer = 1001; cout << "abc的值是:"; cout << abc << endl; cout << "abc的地址是:"; cout << &abc << endl; cout << "int的值是:"<< * pointer << endl; cout << "int的地址是;" << pointer << endl; double * pointer_double = new double; * pointer_double = 1001.1; cout << "double的值是:"<< * pointer_double << endl; cout << "double的地址是;" << pointer_double << endl; cout << "pointer的大小是:" << sizeof(pointer) << endl; cout << "*pointer的大小是:" << sizeof(*pointer) << endl; cout << "pointer_double" << sizeof(pointer_double) << endl; cout << "*pointer_double的大小是" << sizeof(*pointer_double) << endl; return 0; }
-
使用delete释放内存
delete可以在使用完内存之后,将内存归还到内存池
-
语法例子
int * ps = new int; delete ps;- 是否ps执行的内存,但是不会删除指针ps本身,此时如果需要,可以将它重新指向另一个新分配的内存块
- new和delete必须配对使用,不然会内存泄漏(memory leak),这样会导致被分配的内存不能再使用了
- 不要去尝试释放已经释放的内存块
- 不要创建两个指向同一个内存块的指针
-
使用new来创建动态数组
通过声明创建数组的时候,在程序被编译的时候就给数组分配了内存,不管这个数组有没有被使用,都在在那儿占着内存,这种在编译时给数组分配内存的操作叫做静态联编(static binding)
使用new 的时候,如果在运行阶段需要数组,就创建数组,如果不需要就不创建,也可以在程序运行的时候选择数组长度,这种操作叫做动态联编(dynamic binding)。这样创建的数组叫做动态数组(dynamic array)
-
使用new创建动态数组
int * pointer = new int[10]; // 创建一个包含10个int元素的数组 delete [] pointer; // 释放动态数组- new运算符返回第一个元素的地址
- 释放动态数组的时候,方括号告诉程序,应该释放整个数组
- 如果使用new 的时候没有方括号,那么delete的时候也不使用
-
new和delete的规则:
- 不要使用delete释放不是new分配的内存
- 不要使用delete释放同一个内存块两次
- 如果使用new[]为数组分配内存,那么应该使用delete[]释放内存
- 使用使用new[]给一个实体分配内存,那么使用delete释放(不要加方括号)
- 对空指针应用delete是安全的
-
使用动态数组
-
例子
# include <iostream> int main() { using namespace std; double * pointer = new double[3]; pointer[0] = 0.2; pointer[1] = 0.6; pointer[2] = 0.3; cout << "pointer[1]的值是:" << pointer[1] << endl; pointer = pointer +1; cout << "新的pointer[0]的值是:" << pointer[0] << endl; cout << "pointer[1]的值是:" << pointer[1] << endl; pointer = pointer -1; delete []pointer; return 0; }
-
-
-
指针,数组,指针算术
-
例子
#include <iostream> int main() { using namespace std; double a[3] = {100.1,202.5,303.3}; short b[3] = {3,2,1}; // 两种获取数组地址的方法 double *pa = a; // 将pa声明为指向double类型的指针,然后初始化为a数组中第一个元素的地址 short * pb = &b[0]; cout << "pa = " << pa <<", *pa = " << *pa << endl; pa = pa+1; cout << "pa指针加1\n"; cout << "pa = " << pa <<", *pa = " << *pa << endl; pa = pa+1; cout << "pa指针加1\n"; cout << "pa = " << pa <<", *pa = " << *pa << endl; cout << "pb = " << pb <<", *pb = " << *pb << endl; cout << "pb指针加1\n"; cout << "pb = " << pb <<", *pb = " << *pb << endl; cout << "a[0]的值是:" << a[0]<< endl; cout << "a[1]的值是:" << a[1]<< endl; cout << "*b的值是:" << *b << endl; cout << "*(b+1)的值是:" << *(b+1) << endl; cout << "a的数组长度是:" << sizeof(a)<< endl; cout << "pa的长度是:" << sizeof(pa) << endl; return 0; }- pa是地址,*pa是存储在这个地址上的值
-
使用new创建动态结构
和动态创建数组一样,内存是在运行时分配的
-
例子
// 创建一个未命名的inflatable类型,然后将地址赋给一个指针 inflatable * pointer = new inflatable -
例子:使用new创建一个未命名的结构体,并且使用两种方法访问结构体成员
# include <iostream> struct inflatable { char name[20]; float volume; double price; }; int main() { using namespace std; inflatable * pointer = new inflatable; // 给结构体分配内存 cout << "输入结构体中的name成员的值:"; cin.get(pointer->name,20); // 第一种访问成员的方法 cout << "输入结构体中的volume成员的值:"; cin >> (*pointer).volume; // 第二种访问成员的方法 cout << "输入结构体中的price成员的值:"; cin >> pointer->price; // 第二种方法的输出 cout << "name:"<< (*pointer).name << endl; // 第一种方法的输出 cout << "volume:" << pointer-> volume<< endl; cout << "price: $" << pointer-> price<< endl; delete pointer; return 0; }
-
自动存储,静态存储,动态存储
- 自动存储
- 在函数内部定义的常规变量使用自动存储空间,被称为自动变量(automatic variable)
- 这些变量在函数被调用的时候自动产生,函数结束时消亡
- 自动变量是局部变量,作用域是包含它的代码块
- 静态存储
- 整个程序执行期间都会存在的存储方式,使变量变成静态的方式有两种:在函数外面定义;在声明的时候使用static关键字
- 动态存储
- new和delete运算符提供的方法
- new和delete管理一个内存池,这个内存池中,用于静态变量和动态变量的内存是分开的,这样数据的生命周期就不完全受程序或函数的生存时间控制
- 自动存储
-
数组的替代品
-
模板类vector
类似于string类,也是一种动态数组
可以在运行阶段设置vector对象的长度,可以在末尾附加新数据,也可以在中间插入新数据
这个类也是使用new和delete来管理内存
-
例子
#include <vector> // 创建vector对象需要包含头文件vector using namespace std; vector<int> vi; // 创建一个长度为0的int数组 int n; cin >> n; vector<double> vd(n); // 创建一个长度为n的double数组
-
模板类array
c++11新增的模板类,也位于名称空间std中
array对象的长度也是固定的,使用静态内存分配
效率和数组相同,但是更方便,更安全
-
例子
#include <array> // 创建array对象需要包含头文件array using namespace std; array<int,5> ai; // 创建一个包含五个对象的int数组 array<double,4> ad = {1.2,2.3,4.5,6.7};
-
比较数组,vector对象和array对象
无论是数组,vector对象和array对象,都可以使用标准数组的表示方法来访问各个元素
array对象和数组存储在相同的内存区域中(栈)
vector对象存储在另一个区域中(自由存储区或堆)
array对象可以被赋值给另一个array对象
对于数组,必须逐元素去赋值数据