关于内存布局
请运行下面的代码:
char *str0 = "10";
cout<< *(int*)(&str0) <<endl;
char str1[] = "10"; //内存布局为栈里的01, 0在上1在下.
cout<< *(int*)(&str1) <<endl;
char *str2 = "1000";
cout<< *(int*)(&str2) <<endl;
char str3[] ="1000";
cout<< *(int*)(&str3) <<endl;
int a;
int b;
cout<< &a <<endl;
cout<< &b <<endl; //得到a的地址比a小4, 在栈区, 应该是a比b大才对呀.
//栈底地址号大嘛.
int d[2];
cout<< &d[0] <<endl;
cout<< &d[1] <<endl; //得到d[0]的地址比d[1]小4, 莫非分配数组空间时, 从最后
//一个元素开始?
请谈谈您对得到的结果看法.
问题点数:100、回复次数:24Top
1 楼lujun5100(笑笑)回复于 2006-11-11 00:42:03 得分 0
这个应该跟编译器有关
就像使用位段时,有的是从高位排,有的从低位Top
2 楼39457760(人间一日,网上一年◎分要多多的给,贴要慢慢的结)回复于 2006-11-11 00:48:46 得分 0
和编译器有关,也和优化有关
用VC6编译的结果:
debug版本的内存分配
_str0$ = -4
_str1$ = -8
_str2$ = -12
_str3$ = -20
_a$ = -24
_b$ = -28
_d$ = -36
Release版本
_str1$ = -24
_str3$ = -24
_a$ = -16
_b$ = -12
_d$ = -8Top
3 楼lei001(太极)回复于 2006-11-11 00:57:45 得分 0
markTop
4 楼A_B_C_ABC(黄瓜@YouCanDoIt)回复于 2006-11-11 08:34:00 得分 50
数组的地址排列是一定的
而两个变量的地址排列由编译器决定。
Top
5 楼guochun(yingc)回复于 2006-11-11 09:00:12 得分 0
栈只是一个概念模型,与物理地址无关Top
6 楼femalelover(楼主, 请把用不着的可用分捐给我1/3 :()回复于 2006-11-11 09:39:24 得分 0
不知道楼上各位说的对不对.
怎么没人觉得
char *str0 = "10";
cout<< *(int*)(&str0) <<endl;
char str1[] = "10";//内存布局为栈里的01, 0在上1在下.
cout<< *(int*)(&str1) <<endl;
char *str2 = "1000";
cout<< *(int*)(&str2) <<endl;
char str3[] ="1000";
cout<< *(int*)(&str3) <<endl;
这段很有意思吗Top
7 楼A_B_C_ABC(黄瓜@YouCanDoIt)回复于 2006-11-11 11:26:40 得分 0
感觉有意思,那是初看觉得得输出应该一样,但为什么不一样呢?细想也很简单.
char *str2 = "1000";//这里的str2指针在栈上,而"1000"在常量区
cout<< *(int*)(&str2) <<endl;//所以这里取指针取到的是指针的地址,并没有取到"1000"的第一个字符的地址
char str3[] ="1000";//这里的"1000"初始化给数组,即在栈一有"1000"的实体。
cout<< *(int*)(&str3) <<endl;//所以这里取指针取到的是"1000"的首地址
内容不一样,两个输出自然不一样了。Top
8 楼femalelover(楼主, 请把用不着的可用分捐给我1/3 :()回复于 2006-11-11 23:59:27 得分 0
嗯,黄瓜兄说得有些道理. 不过还是有很多问题,比如:
char str1[] = "10"; 换为10进制时得到12237, 可见这两个字节是 00110000 00110001即
str1[1]在str1[0]的上面.
然而,
int d[2];
cout<< &d[0] <<endl;
cout<< &d[1] <<endl;
却得到 d[0]在d[1]的上面.., 两个都是数组,怎么会有区别?Top
9 楼A_B_C_ABC(黄瓜@YouCanDoIt)回复于 2006-11-12 00:29:11 得分 0
char str1[] = "10";
=======================
假设str1[0]的地址是1000,保存'1',则str1[1]的地址是1001,保存'0','\0'的地址是1002。这跟int数组的保存是一样的啊。Top
10 楼A_B_C_ABC(黄瓜@YouCanDoIt)回复于 2006-11-12 01:00:05 得分 0
我想你疑惑的int数据的4个字节的顺序:
假设有整数int i=123456789
则它的2进制是
00000111 01011011 11001101 00010101
一种字节排列方式是
00000111
01011011
11001101
00010101 <---这里是i的地址 地址从上往下减小
另一种字节排列方式是
00010101
11001101
01011011
00000111 <---这里是i的地址 地址从上往下减小
一种称为大尾,一种称为小尾,是由cpu决定的。通用pc 的cpu是前一种排列方式。
char str1[]="10";它在内存中的排列是:
'\0'
'0' 00110000
'1' 00110001 <---这里是&str1 地址从上往下减小
*(int*)(&str0) 输出整数如果是 12237,说明你的cpu对一个整数字节是按第一种方式排列的。 而且字串后面那个字节也为00000000。
但是字符数组和整数数组的排列顺序并没有什么不同。
int ar[]={1,2,3,4};仍然是
4 sizeof(int) 个字节
3 sizeof(int) 个字节
2 sizeof(int) 个字节
1 sizeof(int) 个字节 <---这里是&ar 地址从上往下减小
Top
11 楼jixingzhong(瞌睡虫·星辰)回复于 2006-11-12 08:12:31 得分 50
这样的结果是编译器相关的,
一般的说, 在宏观上, 堆栈数据存放是从 底部 开始的,
也就是 大地址 部分。
但是具体的操作是怎么样的,
各个编译器可能有所不同,
楼主你可以用 别的编译器 看看结果, 比如 Dev C++,
a 就是很预期的一致, 比 b 大了4个字节的地址值 ......Top
12 楼jixingzhong(瞌睡虫·星辰)回复于 2006-11-12 08:14:57 得分 0
恩, 这个还有一个 字节序 的问题 !!
char 是单字节的,
不需要考虑,
int 是4字节的, 就需要考虑 字节序的问题了 ~~Top
13 楼jixingzhong(瞌睡虫·星辰)回复于 2006-11-12 08:15:15 得分 0
Endianism
Endianism 是指用来存储数据的方法,它定义了整数和浮点数据类型中是如何对字节进行寻址的。
Little-endian 是将低位字节存储在内存的低地址中,将高位字节存储在内存的高地址中。
Big-endian 是将高位字节存储在内存的低地址中,将低位字节存储在内存的高地址中。
表 3 给出了一个 64 位长整数的布局示例。
表 3. 64 位 long int 类型的布局
低地址 高地址
Little endian Byte 0 Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 Byte 7
Big endian Byte 7 Byte 6 Byte 5 Byte 4 Byte 3 Byte 2 Byte 1 Byte 0
例如,32 位的字 0x12345678 在 big endian 机器上的布局如下:
表 4. 0x12345678 在 big-endian 系统上的布局
内存偏移量 0 1 2 3
内存内容 0x12 0x34 0x56 0x78
如果将 0x12345678 当作两个半字来看待,分别是 0x1234 和 0x5678,那么就会看到在 big endian 机器上是下面的情况:
表 5. 0x12345678 在 big-endian 系统上当作两个半字来看待的情况
内存偏移量 0 2
内存内容 0x1234 0x5678
然而,在 little endian 机器上,字 0x12345678 的布局如下所示:
表 6. 0x12345678 在 little-endian 系统上的布局
内存偏移量 0 1 2 3
内存内容 0x78 0x56 0x34 0x12
类似地,两个半字 0x1234 和 0x5678 如下所示:
表 7. 0x12345678 在 little-endian 系统上作为两个半字看到的情况
内存偏移量 0 2
内存内容 0x3412 0x7856
下面这个例子解释了 big endian 和 little endian 机器上字节顺序之间的区别。
下面的 C 程序在一台 big endian 机器上进行编译和运行时会打印 “Big endian”,在一台 little endian 机器上进行编译和运行时会打印 “Little endian”。
清单 2. big endian 与 little endian
#include <stdio.h>
main () {
int i = 0x12345678;
if (*(char *)&i == 0x12)
printf ("Big endian\n");
else if (*(char *)&i == 0x78)
printf ("Little endian\n");
}
Endianism 在以下情况中非常重要:
使用位掩码时
对象的间接指针地址部分
在 C 和 C++ 中有位域来帮助处理 endian 的问题。我建议使用位域,而不要使用掩码域或 16 进制的常量。有几个函数可以用来将 16 位和 32 位数据从 “主机字节顺序” 转换成 “网络字节顺序”。例如,htonl (3)、ntohl (3) 用来转换 32 位整数。类似地,htons (3)、ntohs (3) 用来转换 16 位整数。然而,对于 64 位整数来说,并没有标准的函数集。但是在 big endian 和 little endian 系统上,Linux 都提供了下面的几个宏:
bswap_16
bswap_32
bswap_64
Top
14 楼OOPhaisky(异化$渴望成功~~)回复于 2006-11-12 10:40:58 得分 0
char *str0 = "10";
cout<< *(int*)(&str0) <<endl;
---------------------------------------------------------------------------------
楼主讨论str0的内存布局没有任何意义,因为“10”这个字符串字面常量的内存布局对用户不透明,即编译器可能进行优化,比如内存重叠(overlap)处理等等。Top
15 楼OOPhaisky(异化$渴望成功~~)回复于 2006-11-12 10:44:40 得分 0
楼主的其他问题跟编译器的具体实现有关,比如
int a;
int b;
cout<< &a <<endl;
cout<< &b <<endl;
编译器不一定先将a压栈,然后再将b压栈,也可能反过来,反正标准也没有说明阿(我没看到^_^)。Top
16 楼femalelover(楼主, 请把用不着的可用分捐给我1/3 :()回复于 2006-11-12 18:08:43 得分 0
int a;
int b;
cout<< &a <<endl;
cout<< &b <<endl;
编译器不一定先将a压栈,然后再将b压栈,也可能反过来,反正标准也没有说明阿
------------
这个嘛, 就算没有也只能是先压a, 后压b了, 一个好的编译器得遵循通用的编译原理吧.
编译原理中,一条语句可能自右向左, 但肯定是自上向下的.Top
17 楼nothingner(真是不懂啊)回复于 2006-11-12 18:13:44 得分 0
路过 。。。。。。。。。。Top
18 楼femalelover(楼主, 请把用不着的可用分捐给我1/3 :()回复于 2006-11-12 20:21:35 得分 0
还有个问题问瞌睡虫:
对 int i = 0x12345678, 0x12 是 i 的低字节还是高字节?
内存0x0001, 0x0002中, 哪个是高址?
我的内存是这种情况:
| 0X12 | 0X03
| 0X34 | 0X02
| 0X56 | 0X01
| 0X78 | 0X00 0x00是i的首地址
照你上面的判断方法是 little endianTop
19 楼blue_zyb()回复于 2006-11-13 12:35:50 得分 0
char *str0 = "10";
cout<< *(int*)(&str0) <<endl;
char str1[] = "10";//内存布局为栈里的01, 0在上1在下.
cout<< *(int*)(&str1) <<endl;
char *str2 = "1000";
cout<< *(int*)(&str2) <<endl;
char str3[] ="1000";
cout<< *(int*)(&str3) <<endl;
--------------------------------
这一段输出中只有最后一个的值是可以确定的,其他的都是不可确定的。
对于最后一个,&str3的类型是char (*)[5],但值是str3数组的首地址,把该地址里面的内容强制解释为int,那么对应的二进制序列是0x31303030,该值在大端机器上就是0x31303030对应的十进制值,在小端机器上则是0x30303031对应的十进制值。例如,我在Vc6下Intel机(小端)上运行的结果就是808464433(0x30303031对应的十进制值)
与之形式类似的是第二个:
char str1[] = "10";
cout<< *(int*)(&str1) <<endl;
但由于只初始化了两个字符,外加结尾的\0,并不构成解释一个int的四个字节,所以得到的值取决于十六进制序列0x31,0x30,0x00后面的那个字节,无法确定
至于str0和str2,是属于同一类的,处理的是指针变量。例如,*(int*)(&str2)是把str2的内容解释为int,也就是把str2里面保存的地址值解释为int,这也是不确定的。
但是可以确定的是,
cout<< *(int*)(&str0) <<endl;和cout<< *(int*)(&str2) <<endl;输出的分别是常量字符串"10" 和 "1000"的地址。
PS:如果把
char *str2 = "1000";
cout<< *(int*)(&str2) <<endl;
改成
char *str2 = "1000";
cout<< *(int*)(str2) <<endl; // &str2 to str2
那么得到的就是和str3一样的结果,是二进制序列0x31303030对应的十进制值(大小端相关)Top
20 楼femalelover(楼主, 请把用不着的可用分捐给我1/3 :()回复于 2006-11-13 13:33:49 得分 0
对 int i = 0x12345678, 0x12 是 i 的低字节还是高字节?
对内存地址. 0X00000000是高地址还是低地址?Top
21 楼taodm((不能收CSDN社区短信息,请莫浪费精力))回复于 2006-11-13 13:37:15 得分 0
“int a;
int b;
cout<< &a <<endl;
cout<< &b <<endl;
编译器不一定先将a压栈,然后再将b压栈,也可能反过来,反正标准也没有说明阿
------------
这个嘛, 就算没有也只能是先压a, 后压b了, 一个好的编译器得遵循通用的编译原理吧.
编译原理中,一条语句可能自右向左, 但肯定是自上向下的.
”
为楼主狂汗!因为2种压栈顺序的环境我都遇过。标准没有明确规定的东西就绝对不要随便揣测。
Top
22 楼blue_zyb()回复于 2006-11-13 13:50:47 得分 0
对 int i = 0x12345678, 0x12 是 i 的低字节还是高字节?
对内存地址. 0X00000000是高地址还是低地址?
------------------------------------------
如果i被分配在1000号地址,那么有:
字节地址 little endian big endian
1000 0x78 0x12
1001 0x56 0x34
1002 0x34 0x56
1003 0x12 0x78
至于0X00000000是高地址还是低地址?,不太明白你什么意思,但上面这个例子可能会给你一些思路:对于大小端机,变量i的地址都是1000,不同的在于接下来四个字节的排列
Top
23 楼blue_zyb()回复于 2006-11-13 13:52:10 得分 0
对了,下面的代码段可以用来查看字节编码:
void show_bytes(unsigned char *p, int len)
{
for (int j = 0; j < len; j++)
{
for (int i = 7; i >=0; i--)
cout << ((p[j] & (1 << i)) != 0) ;
cout << endl;
}
}
int main()
{
int i = 0x12345678;
show_bytes((unsigned char*)&i, sizeof(i));
return 0;
}Top
24 楼sjjf(水晶剑锋)回复于 2006-11-13 14:19:12 得分 0
markTop




