C++存储方式

 

今天,莫名其妙地“奋奋”。

看着一本C++的时候,突然想到,是不是可以用数组越界来探讨一下,C++的存储方式呢?由于书页上的内容太多。就简单地写写吧。

int main()

{

    const int SIZE = 4;

    float a[SIZE] = {33.3, 44.4, 55.5, 66.6};

 

    for (int i=0; i<7; i++)

    {

        cout << "\ta[" << i << "] = " << a[i] << endl;

    }

    system("pause");

    return 0;

}

//这里不报错吧?很犀利。就是要用这个不报错的bug(C++的硬伤)来深入一下这个强大的东西。想知道他是怎么存储的。

有一则例子,应该是很早以前的编译器写的。那个时候C++的确实会像我写那样输出。

int main()

{

    float a[] = {22.2, 44.4, 66.6};

    float x = 11.1;

   

    cout << "x = " << x << endl;

    a[3] = 88.8;

    cout << "x = " << x << endl;

 

    system("puase");

    return 0;

}

输出是什么呢?

x = 11.1

x = 88.8

 

此时很惊讶。a[3]的非法使用,本身是不报错的,可是它修改了另一个变量的数据x!

要是在大型程序,几千万行代码中,怎么办?这种把头皮擦破都想不出来。因为它能通过编译。

不过,有经验的人都会主动去查野指针、数组越界。这个是很好的经验,确实能够修复bug,也能够让测试人员顺利地验证通过。

原因就会表明:数组越界、野指针。具体操作和内存的使用并不太清楚。这个也是我为了想知道,并且在知道后兴奋地写博客的原因。

 

我相信,现在兴奋的时候,会主动地写,以后没了这个激情,见怪不怪就不会写了。为了保存这一刻青涩的激动。并且抛弃上面的老古董。

来看看新的代码。和上面的类似。不过上面的代码在当前很多规范的编译器已经克服掉了。那么就看看它怎么克服的。

上面有一条规律总结:a[3]的内存地址就是x的内存地址。即a[3]就是x。

 

#include <iostream>
using namespace std;

int main()
{
    float x = 11.1;
    float a[] = {22.2, 44.4, 66.6};
   
   
    cout << "x = " << x << endl;
    a[7] = 88.8;
    cout << "x = " << x << endl;
   
    cout << "a的地址 " << &a << endl;
    cout << "x的地址 " << &x << endl;
    cout << "float的大小 " << sizeof(float) << endl;
   
    system("pause");
    return 0;
}

//这里a[7]就是x!

输出:

x = 11.1

x = 88.8

a的地址 0x22ff50

x的地址 0x22ff6c

float的大小 4

 

这个时候,应该很激动地来计算&a与&x的公式了: &x = &a + 2*3*sizeof(float) + 1。

这个公式经典了!

也就是说,当前的编译器是怎么在防止数组越界的。在开辟的首地址做始点,然后2倍的数组空间长,这就是数组的内存空间。

那么下一个变量是在哪里呢?很明显,是这个数组内存空间后的第一位。

 

这个问题清楚了。激动了。那么就来不激动的了:

我的概念里,有静态存储方式,这个要区别static。因为这个是内存角度,或者说类似读计算机组成原理来的概念,被我自己的语言总结成的。

这一点先说明好了。贴下面的体会就好理解了。

1 这里揭示了一个很重要的信息:C++是如何保存数值的。从这里测试例子来看很明显,可以很直观地解释:固定的变量是可以确定空间大小的,包括各种内置类型、类类型等的对象,他们按作用域被封装在一块内存区域,这个内存区域中,各个变量的空间是连续挨着的,这里通过数组可以越界来测试这种存储方式。这里的变量都是静态存储。

2 这个测试要注意:只能测试静态存储方式。因为这种方式的类型是确定的。容易把握后边的数据怎么翻译的问题。如果方式不确定,不能确定翻译需要的空间定长,或者不匹配,都会读出不同结果的值。这个值对自己的测试目的和把握来说,是不可控的。

3 通过数组可以越界这一特点,来测试C++的内存管理。可以确定静态存储的有两种变量:局部变量和全局变量。static和auto。static是另外一片连续的存储空间,按声明顺序来分配空间。这篇区域叫静态存储区。这个静态和存储方式的静态在这里是必须要区别的(不做解释,理解提升了就懂了我的意思了,这里强调是方式,是跟物理内存有关的实现,而不是上层的概念)。另外关于动态的,是用堆来实现。程序中无法在编译阶段确定大小的,需要等待运行时数据,在运行过程中开辟空间。而普通的能在编译阶段确定的也是按作用域来分段,按声明顺序来定义的静态存储方式。

4 经测试,我发现当前的做法是数据以2倍+1的空间分界,下一个变量以此界开始分配。

//这一段是我读的过程中写的笔记,虽然总结得不太清楚,也不大好理解。但是确实编译器是这样子的。要梳理总结比较麻烦,就贴这里吧。

至于那个所谓的堆,其实是数据结构里,我记得那段malloc的算法代码。严蔚敏那本书很枯涩,但是治学就是这么严谨了。对她来说,这已经她尽了很大努力把概念通俗了。

我说的堆,其实也就是那段代码的实现方式(毕竟我弄不出C++编译器的真正源代码)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值