c++ day5 第四章 (三)动态结构,存储类型,数组替代品

用new分配动态结构

示例1 使用成员运算符和间接成员运算符访问结构成员

#include <iostream>
struct inflatable
{
    char name[20];
    float volume;
    double price;
};

int main()
{
    using namespace std;
    inflatable *ps = new inflatable;//分配刚好够存结构体的内存,一定要记得delete
    cout << "Enter name of inflatable item: ";
    cin.get(ps->name, 20);//用get()方法
    cout << "Enter volume in cubic feet: ";
    cin >> (*ps).volume;
    cout << "Enter price: $";
    cin >> ps->price;

    cout << "Name: " << (*ps).name << endl;
    cout << "Volume: " << ps->volume << endl;
    cout << "Price: " << ps->price << endl;
    delete ps;
    return 0;
}
Enter name of inflatable item: apple
Enter volume in cubic feet: 100
Enter price: $2.34
Name: apple
Volume: 100
Price: 2.34

但是如果输入多于20个字符,则get()方法只会拿19个字符,剩下的留在队列中,于是后面的输入就不让输入了,出错

Enter name of inflatable item: aaaaabbbbbcccccdddddeeeee
Enter volume in cubic feet: Enter price: $Name: aaaaabbbbbcccccdddd
Volume: 0
Price: 8.15075e+276

如果不用get()方法输入字符串,输入超过数组长度也没事,程序只会把数组长度个字符读取到成员中,并且剩下的输入不会留在缓冲队列中影响后续输入

cin >> ps->name;
Enter name of inflatable item: aaaaabbbbbcccccdddddeeeee
Enter volume in cubic feet: 123
Enter price: $1.22
Name: aaaaabbbbbcccccddddd
Volume: 123
Price: 1.22

示例2 动态分配内存,以存储多个字符串,只需一个数组作为输入缓冲

如果要键入很多字符串(中间没有被空白隔开的),比如1000个,其中最长字符串有79个字符,那么如果用数组存储,一共要1000个长度为80的数组,需要8万字节,其中很多字节全是浪费的;

可以只用一个长80字节的数组作为输入缓冲,输入的字符串先放在这个数组,然后用new分配适当内存,再把字符串从数组中拷贝到分配的内存处,然后把这块内存的指针存起来,一共只需要1000个指针,则一共只需要1080个字节,省了80倍!!!

#include <iostream>
#include <cstring>
using namespace std;
char * getname(void);//返回一个char指针

int main()
{
    char *name;

    name = getname();
    cout << name << " at " << (int *)name << "\n";//写'\n'也可以
    delete [] name;//这样才会释放整个字符串数组,而不是只释放第一个字符
    return 0;
}

char *getname(void)
{
    char temp[80];//一个存储键入字符串的大型(得长于键入的最长字符串)临时数组
    cout << "Enter last name: ";
    cin >> temp;
    char * pn = new char[strlen(temp) + 1];
    strcpy(pn, temp);//这时候不用担心内存越界,因为分配的长度刚好

    return pn;
}

这个程序在getname()函数中分配内存,却在main()函数中释放内存,其实这样(把new和delete放在不同函数里)不是好习惯,因为很容易忘记delete!!自己写程序尽量避免这么做!这里只是举例子

Enter last name: mary
mary at 0xf31990

数据的四种存储方式

C++中,各种数据在内存中有不同的存储方式,或说是存储类型,区别主要是变量的寿命和作用域。

C里面也说过变量的存储类型,作用域,链接等,都是一样的,只是这里多了一个动态存储需要加以注意。他很重要。

自动存储

自动存储的变量也叫做自动变量automatic variable,比如自动数组,自动结构···

他们的特点是:在函数内部定义,所属函数被调用时他们会自动产生,在函数调用结束后,控制权回到主调函数时,他们会被自动释放。而且他们是以栈的方式存储,即FIFO,进入所属函数时先创建的变量最后释放。

函数中的块中也可以定义自动变量,比如for循环块,这种变量在for循环块结束就消亡了,所以栈这种内存在程序执行的整个生命周期里不断在增大又缩小,增大,缩小····不断在变化。

而且栈的内存是连续的,即所有自动变量被分配的内存不会隔开

自动变量都是局部变量,局部 一词描述的是作用域,即他们的作用域只是定义他们的块(块是花括号括起来的一段代码

示例

#include <iostream>
int main()
{
    using std::cout;
    using std::endl;

    int x = 10;
    {
        cout << x << endl;//旧x
        int x = 100;//在块中定义新的x,旧的被覆盖
        cout << x << endl;//新的
    }
    cout << x <<endl;//旧的
    return 0;
}
10
100
10

静态存储

静态 的意思是:在程序执行的整个生命周期,一直都存在,保持不动。

要想使得变量以静态方式存储,那就在函数外面声明变量(外部变量:具有外部链接的静态变量);或者虽然在函数内,但是使用static存储类别说明符(具有内部链接的静态变量)。

动态存储

自动存储和静态存储严格地限制了变量的寿命

要么程序运行期间一直存在,要么就只在函数执行时存在

很不灵活

我们可以更灵活的存储变量——动态存储,即变量的寿命不会限制于一个函数的执行期间或者整个程序执行期间,而是想出生就出生,想入土就入土(比如上面的示例,在getname()函数new分配内存,在main函数delete释放内存),因此程序员对内存的控制更大了,但是同时内存管理也就更加复杂了

动态存储使用的内存池叫做堆,heap,也叫做自由存储区,free store。这种内存池和自动变量以及静态变量使用的内存在物理上就是隔开了的!

堆不像栈,是连续的内存,堆毕竟是用的new,new不会保证给你分配连续的内存,一般都是不连续的分散的内存块

线程存储

以后再说,C也没仔细说过这个,是C++11新增的,估计是高级特性,后面再说

示例 类型组合(数组,结构,指针)

即数组里面放结构,数组里面放指针,结构里放数组,放指针,然后这四种都用指针去访问,总之可以弄得很晕

#include <iostream>
struct anta_year
{
    int year;
};

int main()
{
    anta_year s1, s2, s3;//三个结构变量
    s1.year = 1998;//直接用成员运算符访问成员
    anta_year *pa = &s2;//指向结构的指针,指向s2
    pa->year = 1999;//用间接成员运算符访问指针指向的结构的成员

    anta_year trio[3];//结构数组
    trio[0].year = 2002;
    std::cout << trio->year << std::endl;//trio是指针,指向第一个结构元素

    const anta_year *arp[3] = {&s1, &s2, &s3};//结构指针数组,每个指针都指向一个结构
    std::cout << arp[1]->year << std::endl;//arp[1]是指向结构s2的指针
    const anta_year **ppa = arp;//arp是数组名,本来就是指针,但arp又是指针数组的数组名,
    //所以arp是一个指向指针的指针,最终指向的是anta_year类型的结构
    auto ppb = arp;//使用auto,进行自动类型推断,automatic type deduction
    //等效于const anta_year **ppb = arp;
    std::cout << (*ppa)->year << std::endl;//*ppa是&s1
    std::cout << (*(ppb+1))->year << std::endl;//*(ppb+1)是&s2

    return 0;
}
2002
1999
1998
1999

数组的替代品(两个模板类)

vector类(动态数组的替代品)

string类是一种动态数组,vector类实际上也是动态数组,它是用new创建动态数组的替代品,但是实际上它也使用new和delete来管理内存。

vector类也在名称空间std中。

#include <vector> // 必须包含vector类头文件
using namespace std;
vector<int> vi;//vi是一个vector<int>对象
vector<double> vd;//vd是一个vector<double>对象
int n;
cin >> n;
vector<float> vf(n);//圆括号!中的值可以是常量,也可以是变量!!表示创建一个数组

array类 (定长数组的替代品)

array类也在名称空间std中

array类对象的长度也是固定的,,像数组那样

#include <array>//头文件
using namespace std;
array<int, 5> ai;//ai是一个有5个int类型数字的array对象
array<double 4> ad = {1.2, 1.3, 1.4, 1.5};//可以用列表初始化

在这里插入图片描述

示例(数组, vector类,array类)

#include <iostream>
#include <vector>
#include <array>
int main()
{
    using namespace std;
    double a1[4] = {1.2, 1.3, 1.4, 1.5};//数组
    //vector类不能列表初始化
    //vector<double> a2(4) = {1.0/3.0, 1.0/5.0, 1.0/7.0, 1.0/9.0};//vector对象
    vector<double> a2(4);
    a2[0] = 1.0/3.0;
    a2[1] = 1.0/5.0;
    a2[2] = 1.0/7.0;
    a2[3] = 1.0/9.0;

    array<double, 4> a3 = {3.1, 3.2, 3.3, 3.4};
    array<double, 4> a4;
    a4 = a3;//array对象之间可以直接赋值!!
    cout << "a1[2]: " << a1[2] << " at " << &a1[2] << endl;
    cout << "a2[2]: " << a2[2] << " at " << &a2[2] << endl;
    cout << "a3[2]: " << a3[2] << " at " << &a3[2] << endl;
    cout << "a4[2]: " << a4[2] << " at " << &a4[2] << endl;

    //misdeed错误操作
    a1[-2] = 20.2;//这句代码会被转化为*(a1-2),C和C++都不检查数组的越界下标引用
    cout << "a1[-2]: " << a1[-2] << " at " << &a1[-2] << endl;
    return 0;
}
a1[2]: 1.4 at 0x61ff08
a2[2]: 0.142857 at 0x1d19a0
a3[2]: 3.3 at 0x61fed8
a4[2]: 3.3 at 0x61feb8
a1[-2]: 20.2 at 0x61fee8

a1[2]的地址是61ff08,所以a1中存储的地址是61ff08减去16个字节,即61fef8(十六进制减法竟然让我挠了会儿头···!!!),再减去16 字节,即61fee8,所以a1[-2]的地址是0x61fee8

我尝试vector和array类的对象可不可以越界赋值,答案是可以!但是vector类的那两句如果不注释程序就不能打印这三句代码,注释掉后可以正常打印a1和a3

//misdeed错误操作
a1[-2] = 20.2;//这句代码会被转化为*(a1-2),C和C++都不检查数组的越界下标引用
//a2[-2] = 3.19;
a3[-2] = 7.9;
cout << "a1[-2]: " << a1[-2] << " at " << &a1[-2] << endl;
//cout << "a2[-2]: " << a2[-2] << " at " << &a2[-2] << endl;
cout << "a3[-2]: " << a3[-2] << " at " << &a3[-2] << endl;
a1[-2]: 20.2 at 0x61fee8
a3[-2]: 7.9 at 0x61feb8

vector类和array类的at()方法可以检查越界下标,代价:程序运行时间更长

不会报错终止编译,但是会引发运行时异常

这种方法检查了下标,很安全,代价是程序运行时间更长,C++让你自己选安全还是运行时间长

a2.at(-2) = 4.56;
terminate called after throwing an instance of 'std::out_of_range'
  what():  vector::_M_range_check: __n (which is 4294967294) >= this->size() (which is 4161207503)
a3.at(-2) = 4.3;
terminate called after throwing an instance of 'std::out_of_range'
  what():  array::at: __n (which is 4294967294) >= _Nm (which is 4)

练习

在这里插入图片描述

打印存储这个字符串的地址呗

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值