第4章 复合类型-2(指针)

第4章

4.7 指针和自由存储空间

计算机存储数据时必须跟踪的3种基本属性:
 信息存储在何处
 存储的值为多少
 存储的信息类型
指针是一个变量,其存储的是值的地址,而非值本身。
对一个常规变量通过地址运算符(取址运算符)&即可获得它的位置,例如home变量,&home即是它的地址;*运算符被称为间接值或解除引用运算符,用于指针可得到该地址存储的值,例如manly指针,*manly即是manly地址处存储的值。

4.7.1 声明和初始化指针

int *ptr;    //C习惯此种,强调*ptr是一个int类型的值
int* ptr;    //C++习惯此种,强调int*是一种类型——指向int的指针
int*ptr;     //valid,三种方式都可以
int* p1, p2;  //创建了一个指针p1,一个int变量p2 

一个指针变量名对应一个*
不同类型的指针指向的数据类型长度不同,但两个指针变量本身长度通常是相同的。比如:double* p1,int* p2,p1和p2本身占用内存空间相同。

4.7.2 指针的危险

C++中创建指针时,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向的数据的内存。

long * fellow;           //没有将地址赋给fellow
* fellow = 22333;        //22333不知道放在哪里

上述代码很危险,可能会导致一些最隐匿难以跟踪的BUG。
warning:一定要在对指针解除引用运算符(*)之前,将指针初始化为一个确定适当的地址,此为使用指针的金科玉律。

4.7.3 指针和数字

指针为地址,非整型(及时地址通常被当作整数来处理),不能简单地将整数赋给指针。

int* pt;
pt = 0xB80000000;      //type mismatch

//通过强制类型转换:
int* pt;
pt = (int*)0xB80000000;  //type now match

4.7.4 使用new分配内存

上述内容都将指针初始化为变量的地址。指针真正用武之地——在运行阶段分配未命名的内存以存储值。这种情况下,只能通过指针来访问内存。
语法格式:

typeName * pointer_name = new typeName;

需要在两个地方指定数据类型:用来指定需要什么样的内存和用来声明合适的指针。
例如:

int * pt = new int;

new运算符根据类型来确定需要多少字节的内存,再返回其地址赋给pn,pn是被声明为指向int的指针。pn是地址,*pn为该地址存储的值。

4.7.5 使用delete释放内存

为避免内存泄露(memory leak),delete和new配对使用。
例如:

int * pt = new int;delete pt;      //并不会删除pt值,只会删除pt指向的内存空间

delete会释放pt指向的内存,并不会删除pt值,只会删除pt指向的内存空间。此时,还可将pt重新指向另一个新分配的内存块。
warning:
 不要尝试释放已经释放的内存块
 不能用delete来释放声明变量所获得的内存(只适用于new)

int jugs = 5;
int* pi = &jugs;
delete pi;       //not allowed, memory not allocated by new

类似房子本身就不是自己的,无法卖掉;但如果房子是new来的,自己创建的,买来的,才可以delete。
 不要创建两个指向同一个内存块的指针

4.7.6 使用new创建动态数组

对于大型数据应使用new正是new的用武之地。
静态联编:在编译时给数组分配内存。这意味着数组是在编译时加入到程序的。
使用new可在运行阶段按需创建数组,不需要时则不创建,还可以在运行时选择数组的长度,此为动态联编。这意味着数组是在程序运行时创建的。这种数组称为动态数组。
使用静态联编时必须在编写程序时指定数组长度;使用动态联编将在运行时确定数组长度。

  1. 使用new创建动态数组
int * psome = new int [10];    //get a block of 10 int

new运算符返回第一个元素的地址赋给指针psome。
delete释放指针:

delete [] psome

使用new时,不带[]则使用delete时也不带[];反之,则也应带。
使用new和delete遵守规则:
 不要使用delete释放不是new分配的内存
 不要使用delete释放同一个内存两次
 如果使用new[]为数组分配内存,则应使用delete[]释放
 如果使用new为一个实体分配内存,则使用delete(无[])来释放
 对空指针应用delete时安全的
2. 使用动态数组
指针和数组基本等价。可以通过索引下标访问数组元素。

double* pt = new double [3];
pt[0] = 0.2;
pt[1] = 0.5;
pt[2] = 0.8;

double a = pt[1];
pt = pt + 1;
double b = pt[0];

delete [] pt;

此时,a = b。
相邻的int地址通常相差2或4个字节,而将pt+1后,它将指向下一个元素的地址,这表明指针算数有一些特别的地方。

4.8 指针、数组和指针算术

指针和数组基本等价,可以用相同的方式使用指针和数组名,可以使用数组方括号表示法,也可以使用解除引用运算符(*)。
算术,将整数变量加1,其值将增加1;指针变量加1,增加的量等于它指向的类型的字节数,也就是当前地址的下一个地址。
指针和数组的区别:
 指针的值可以修改,而数组名是常量。

pointname = pointname + 1;   //valid
arrayname = arrayname + 1;   //not allowed

 sizeof运算符,数组应用返回是数组的长度,指针应用返回是指针的长度。
数组的地址:数组名被解释为其第一个元素的地址,而对数组名应用地址运算符时,返回是整个数组的地址。

4.8.2 指针小结

  1. 声明指针
typeName * pointerName;
  1. 给指针赋值
    应将内存地址赋给指针,可对变量名应用&运算符来获得被命名的内存的地址或是new运算符返回未命名的内存的地址。
  2. 对指针解除引用
    即获得指针指向的值,一种方式是通过()来解除引用,另一种是使用数组表示法[]。比如pn[0]和pn是一样的。
    注意:绝不要对未被初始化为适当地址的指针解除引用
  3. 区分指针和指针所指向
    如果pt是指向int的指针,*pt不是指向int的指针,而是一个完全等同于int类型的变量。

4.8.3 指针和字符串

C风格字符串:

char flower[10] = “rose”;
cout << flower << “s are red\n”; 

数组名是第一个元素的地址,因此cout中的flower是包含字符r的char元素的地址。cout对象认为char的地址是字符串的地址,因为它打印该地址处的字符,然后继续打印知道遇到空字符{\n}为止;而cout中的“s are red\n”也是和数组名一样,传递的也是第一个元素的地址,打印改地址处的字符,然后继续打印知道遇到空字符{\n}为止。
总而言之,如果给cout提供一个字符的地址,则会从该字符开始打印直到遇到空字符为止。

4.8.4 使用new创建动态结构

可以使用new运算符在程序运行时为结构分配所需的空间。
例如,创建一个未命名的inflatable类型,并将其地址赋给一个指针:

inflatable * ps = new inflatable;   //将足以存储inflatable结构的一块可用内存地址赋给ps

访问结构体成员方法:

  1. 创建动态结构,不能将成员运算符(.)用于结构名,代替是箭头成员运算符(->)来访问结构体成员,如上述ps->price,即为结构体的price成员。
  2. *ps就是结构本身,也就是inflatable,则可以使用(*ps).price访问结构体的price成员。
    对于一个函数返回值为一个大型的临时数组时使用new[]创建一个刚好能够存储其内容的内存块,并返回一个指向该内存块的指针,可以节省大量内存( )。

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

根据用于分配内存的方法,C++的3种管理数据内存的方式。

  1. 自动存储
    函数内部定义的常规变量使用自动存储空间,称为自动变量,常见temp,存在于一个代码块{}里的变量。姑且是局部变量。
  2. 静态存储
    整个程序运行期间都存在的存储方式,全局变量。
  3. 动态存储
    比上述两种都更为灵活。new和delete使得可以在一个函数中分配内存,而在另一个函数中释放它。务必同时使用new和delete。

4.9 类型组合

可以通过各种方式组合数组、结构和指针。

4.10 数据的替代品

4.10.1 模板类 vector

vector类似string类,动态数组,可在运行阶段设置vector对象的长度,也可在末尾追加或中间插入新的数据。
使用须知:
 首先,必须包含头文件vector
 Vector包含在名称空间std中,可使用using编译指令,using声明或std::vector
 模板使用不同的语法指出其存储的数据类型
 Vector类使用不同的语法来指定元素数
声明模板:

vector<typeName> vt(n_elem);

创建一个命为vt的vector对象,可存储n_elem个typeName的元素,n_elem可为整形常量也可为整形变量。
例如:

# include <vector>using namespace std;
vector<int> vi;       // create z zero-size array of int
int n = 3;
vector<double> vd(n);    //create an array of n doubles

4.10.2 模板类array(C++11)

vector类功能比数组强大,但效率略低,适用于长度固定的数组,代价是不太方便和安全。
鉴于此,C++11新增模板类array,也位于名称空间std中。
array对象的长度固定,适用栈(静态内存分配),而非自由存储区,因此效率与数组相同,但更方便安全。
使用时也需包含头文件array。
声明模板:

array<typeName, n_elem> arr;

创建一个命为arr的array对象,包含n_elem个typeName类型的元素,n_elem不能是变量。
例如:

# include<array>
…
Using namespace std;
array<int, 5> a;
array<double, 4> b = {1.2, 1.3, 1.4, 1.5};

4.10.3 数组、vector对象和array对象的区别

共同点
都可以使用标准数组表示法(数组下标索引)访问各个元素。
不同点
array对象和数组存储在相同的内存区域(栈)中,vector对象存储在自由存储区或堆中;
array对象可以直接赋给另一个array对象,数组必须逐个元素复制。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我宿孤栈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值