我们用了多少内存之string

 

 

我们大家都知道,如果我们声明了一个变量或者对象,内存里面首先会有一个指针,指向具体的内容。比如说,我声明了一个字符串“hello”,那就有一个字符串指针指向这个字符串。在32位系统下,这个指针就是32位的,占了我4个字节的空间。字符是16位的,hello一共5个字母,又占了我10个字节。那是不是说,这个字符串就占了我14个字节呢?

 

我不知道。

动手找答案吧!

 

下面的讨论都是基于windows+.net环境,仅仅是因为我比较熟悉而已。Linux+c的话,肯定会有些不一样,但是道理应该是不变的。

 

就用大家最熟悉的hello world程序吧。

 

Static void main(stirng[] args)

{

string str = “hello”;

Console.WriteLine(str);

}

 

为了一探究竟,一个好办法就是反汇编。

Debug,然后打开disassembly窗口,

下面是一些代码和对应的反汇编代码:

源代码:string str = “hello”;

汇编:  mov eax , dword ptr ds:[02D52088h]

                   mov dword ptr [ebp-40h],eax

这两行汇编就是我要讨论的核心了。先解释一下。前面说过了,当我们声明一个变量的时候,系统就会创建一个指向这个变量的指针。现在,指向字符串”hello”的指针,就在地址02D52088h里面,我们把它取出来,然后保存到栈里面备用。具体在栈里面的位置,是从栈顶(ebp)向低位数40h的位置。

 

查看下寄存器,在地址02D52088h的位置,数出来32位,这个就是字符串的存放位置了哦。我这里是884dd101。好大一个地址啊。。。别被忽悠了,地址都是从低位到高位保存的,所以实际的地址是01d14d88,这个看起来正常多了。

 

查看内存(打开VS的内存窗口就可以了),跳转到地址01d14d88去看一下,果然找到了”hello”。不过,先别高兴太早,这里有点小问题。”hello”是找到了,可是并不在01d14d88这个位置,而是在它后面96位之后,也就是12个字节之后,才是hello。奇怪了,这96位共12个字节是什么东东呢?

 

试试找点线索来研究吧。

 

于是我又声明了几个string。记住string的内容要不一样哦,因为系统会进行优化,让具有相同内容的不同string变量指向同一个地方。再次查看,这回发现了很多有趣的东西。

 

先晒一下hello这个string的那12个字节的内容吧:

Ec 08 a4 66 06 00 00 00 05 00 00 00

再晒一下”aaaa”这个string的那12个字节的内容(这个”aaaa”是我声明的另外一个string):

Ec 08 a4 66 05 00 00 00 04 00 00 00

 

我想大家应该看出些眉目来了:

首先,前面的4个字节”ec 08 a4 66”是不变的。有理由认为这个就是string类型的标示符号。只要你是个string,最前面就要拿出4个字节来存这个数值。

然后是中间的4个字节和最后的4个字节。可以看出来,最后的4个字节,应该保存的是字符串里面字符的个数。比如hello5aaa就是4。那中间的4个字节呢?我怀疑也是跟个数有关系,而且看起来是跟字符个数是有关系的,至少这两个字符串都是字符个数加1

 

长度加1。。。嗯,让我想起了c里面字符串用’/0’结尾,长度最少要比字符的个数多一个。为了搞清楚这个问题,让我们来仔细的看一下这块内存:

Ec 08 a4 66 06 00 00 00   :前面4个字节是字符串标示,后面4位是个数

05 00 00 00 68 00 65 00  :前面4个字节是字符个数,后面就是hello的内容,每个字符是16位的

6c 00 6c 00 6f 00 00 00    :后面的4个字节,前面的6f 00是字母‘o’,后面的00 00具体不详,看起来很像’/0’结尾哦

00 00 00 80 ec 08 a4 66   :前面有4个字节,不知道是什么,后面4个字节还是字符串标示,一个新的字符串开始了。

05 00 00 00 04 00 00 00

61 00 61 00 61 00 61 00

00 00 00 00 00 00 00 00

 

现在看来,一个字符串至少包括下面的内容:

4个字节的指针,4个字节的字符串标示,4个字节的字符个数,4个字节的跟字符相关的个数。

 

现在,再来关心下两个字符串之间的那个地带。再多声明几个字符串,对比后,终于看出了眉目。

那个”00 00 00 80”是个隔离带,用来分割两个字符串。然后就是还有一些不太规律的0。经过观察,发现每个字符串都会以00 00来结尾,这个应该就是传说中的’/0’了吧。但是有的地方可是00 00 00 00啊!多了40呢。

我猜想是这样的:系统要对齐代码,这样效率才高。假如我们有奇数个字符,算上最后的’/0’

正好偶数个,每个又是16位的,正好是32位的整数倍。可是如果有偶数个字符呢,就无法实现32位对齐了,因此就要在00 00后面再补上00 00

 

于是,我们又有了下面的结论:

在两个字符串之间,有4个字节的隔离标示。然后呢,每个字符串后面都有2个字节的00 00。如果是偶数个字符的字符串,后面还要再多2个字节的00 00。另外刚才上面不太清楚的那个4个字节的字符个数,我现在怀疑是所谓的字符数组的长度,一般来说,就是字符个数加1了。

 

 

现在,可以小小的总结一下了:

假如我的程序里面有M个字符,那就要有:

4M个字节的指针,4M个字节的字符串标示,4M个字节的字符数组长度,4M个字节的字符个数,4(M-1)个字节的分隔符

再假设里面有一半的字符串长度是偶数,那就要多补2*M/2=M个字节的0,这样为了保存字符串,我们就要多用掉21M-4个字节。如果是100个字符串,就是大约2096B的额外内存。如果这些字符串的平均长度是100的话,那么我们的内存利用效率就是大约90%

 

除此之外,是否还有其他的损失,我暂时也不太清楚。另外,为什么要有那个” 00 00 00 80”的分隔符,我也不清楚。反正现在仅仅是知其然而不知其所以然。就先研究到这里吧。要想搞得更明白,就要去研究下.net的编译器了。这个,水平有限,暂且搁置吧。

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值