CSDN首页 空间 新闻 论坛 Blog 下载 读书 网摘 搜索 .NET Java 视频 接项目 求职 在线学习 买书 程序员 通知
可用分押宝游戏火热进行中... 专题改版:Java Web 专题
CSDN社区
搜索 收藏 打印 关闭
CSDN社区 >  C/C++ >  C++ 语言

关于内存布局

楼主femalelover(楼主, 请把用不着的可用分捐给我1/3 :()2006-11-11 00:20:34 在 C/C++ / C++ 语言 提问

请运行下面的代码:  
  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

相关问题

关键词

得分解答快速导航

  • 帖主:femalelover
  • A_B_C_ABC
  • jixingzhong

相关链接

  • C/C++ Blog
  • C/C++类图书
  • C/C++类源码下载

广告也精彩

反馈

请通过下述方式给我们反馈
反馈
提问
网站简介|广告服务|VIP资费标准|银行汇款帐号|网站地图|帮助|联系方式|诚聘英才|English|问题报告
世纪乐知(北京)网络技术有限公司 版权所有, 京 ICP 证 020026 号
北京创新乐知广告有限公司 提供技术支持
Copyright © 2000-2007, CSDN.NET, All Rights Reserved
GongshangLogo