KK's question 2(for beginner)

我啃 2007-12-01 05:00:51
接上一题
http://topic.csdn.net/u/20071118/17/e65a1684-88ae-4cce-959f-408b4ff5bb54.html

#include <iostream>
using namespace std;
int main()
{
int const c1=4;
int const c2[4] = {2, 4, 6, 8};
*(int *)&c1 = 5;
*(int *)&c2[2] = 9;
cout<<c1+c2[2]<<endl;
return 0;
}

这次:

#include <iostream>
using namespace std;
int const c1=4;
int const c2[4] = {2, 4, 6, 8};
int main()
{
*(int *)&c1 = 5;
*(int *)&c2[2] = 9;
cout<<c1+c2[2]<<endl;
return 0;
}

what may happen ?why? and what can i do to fulfill my humble wills?
会发生什么?为什么? 我要做什么才能完成我小小的心愿?
...全文
171 27 打赏 收藏 转发到动态 举报
写回复
用AI写文章
27 条回复
切换为时间正序
请发表友善的回复…
发表回复
ms_ms 2007-12-07
  • 打赏
  • 举报
回复
mark
我啃 2007-12-07
  • 打赏
  • 举报
回复
前两题可以说是比较完美地解决了,于是我又能思索新的更加变态的题目了~呵呵~
questions for beginners
problems for those who pretend to be expert
我啃 2007-12-07
  • 打赏
  • 举报
回复
blue_zyb解释得非常不错,呵呵
对于question 1,的确没有什么可以说的了,大家参见blue_zyb的,“const”或者更贴切说其实是一种加了修改限制的变量,优化的时候根据是否需要分配空间的条件(例如是否使用到了其地址等等),可以决定是否真正给它分配空间。此外实现真正的数值常量数组,可以使用boost::mpl::vector_c,使用起来可能有些麻烦。当然吹毛求疵是没有意义的。
对于question 2,也的确这个是未定义行为,但是在标准未定义外,编译器制造商却出奇地统一(现在只说windows架构)
对于执行栈上的const,只要被分配了空间,几乎都可以加以修改(只要用强转绕过编译器的安全检查)
但是全局上的const则不同。以前玩溢出攻击的时候就看到过,不同的系统架构和编译器提供给了全局常量特殊的保护
通常,在win32 PE文件下,系统提供了不同段访问限制(参见PE文件介绍),又通常,全局const是被放置在.rdata段内(MS的链接器),rdata是定义为read-only data section由系统来保全指针访问的“安全性”,所以擅自改动系统当然会嚎叫。

既然这样很容易想到流氓的手段-修改那个段的访问属性,具体就是用lordPE修改编译后的程序的.rdata段访问为writeable,运行即可。
另一种事实上不可行的自修改手段相比大家不感兴趣就不加以赘述了。逼人愚钝,没有更好的淫技献上。

“所以,站在标准的角度来说,lz的2个小程序都是undefined behavior,没有统一可解释的唯一结果。”

呵呵,事实上我早知道是undefined,可是基于我对于标准undefined但是编译器制造者通用的实现手段窥探的嗜好,以加强同仁们对于真实的了解。

标准的确伟大,无容置疑,但是为了可移植性,标准有时针对任何可能有争议的地方都加以undefined,中庸之道啊。
但现实比标准更现实。

最后,十分感谢blue_zyb的解释以及各位参与我无聊的讨论,谢谢。
blue_zyb 2007-12-05
  • 打赏
  • 举报
回复
当然,我上面说的那一段都是windows下VC(某一特定系统)的情形,但是标准对此种情形的裁决是:undefined behavior

在讲到const_cast的时候,标准在第7条说到:
5.2.11 Const cast
7. [Note: Depending on the type of the object, a write operation through the pointer, lvalue or pointer to data member resulting from a const_cast that casts away a const-qualifier may produce undefined behavior(7.1.5.1).

然后在7.1.5.1中的例子中有:
const int* ciq = new const int (3); // initialized as required
int* iq = const_cast<int*>(ciq); // cast required
*iq = 4; // undefined: modifies a const object

所以,站在标准的角度来说,lz的2个小程序都是undefined behavior,没有统一可解释的唯一结果。

healer_kx 2007-12-05
  • 打赏
  • 举报
回复
这次只接分。。。
blue_zyb 2007-12-05
  • 打赏
  • 举报
回复
case 1:
int main()
{
int const c1=4;
int const c2[4] = {2, 4, 6, 8};
*(int *)&c1 = 5;
*(int *)&c2[2] = 9;
cout<<c1+c2[2]<<endl;
return 0;
}
--------------------------------
此处c1被修改,c2[2]则没有,是因为const的普通变量c1和数组数据c2[2]不能被编译器一视同仁。
对于c1来说(单个变量),被声明为const后,编译器就可以在符号表里为其添加一个记录,以后引用到c1的语句,就可以通过查符号表中c1的值来进行常量折叠。
而此处编译器正是这样做的,编译时就把表达式c1 + c2[2]中的c1的值替换为了4,而不是语句*(int *)&c1 = 5;执行后,c1内存处对应的5。
但是对c2来说(数组),编译器却不能这样做。原因在于,如果你声明一个1000000个元素的数组,想必谁也不会傻到在符号表中添加1000000个记录,来分别记下c2[0]到c2[999999]的值,以方便来进行常量折叠吧。所以,此处c2数组元素的访问与一般数组元素访问无异,也就是会根据c2对应的数组首地址,加上偏移,再到内存中去取值。当然,c2[2]取到的是前一句已被改变了的值。

case 2:
vc中会crash。原因不外乎:global的const数据,可以将其放在可执行文件中的const段,加上只读保护,那么程序试图对该地址进行写操作的时候,就会crash。但是local的const变量用指针改写不会crash,是因为local的const变量分配在栈上,而据我所知(记得CS:app上说过),栈上的数据是不能加只读保护的,都是可读可写。

下面是vc /FA得到的asm中找到的c1,c2所在数据段的位置
CONST SEGMENT
_c1 DD 04H
_c2 DD 02H
DD 04H
DD 06H
DD 08H
CONST ENDS
eatta 2007-12-04
  • 打赏
  • 举报
回复
楼主公布答案吧
我啃 2007-12-02
  • 打赏
  • 举报
回复
的确正常的编译器都不应该通过,但是为什么不能通过,如何做些小动作完成我小小的心愿?
loops 2007-12-01
  • 打赏
  • 举报
回复

*(int *)&c1 = 5;
004113FE mov dword ptr [c1 (4156FCh)],5
*(int *)&c2[2] = 9;
00411408 mov dword ptr [c2+8 (415708h)],9
cout<<c1+c2[2]<<endl;
00411412 mov esi,esp
00411414 mov eax,dword ptr [__imp_std::endl (4182A0h)]
00411419 push eax
0041141A mov ecx,dword ptr [c2+8 (415708h)]
00411420 add ecx,4
00411423 mov edi,esp
00411425 push ecx
00411426 mov ecx,dword ptr [__imp_std::cout (418294h)]
0041142C call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (418298h)]
00411432 cmp edi,esp
00411434 call @ILT+315(__RTC_CheckEsp) (411140h)
00411439 mov ecx,eax
0041143B call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (41829Ch)]
loops 2007-12-01
  • 打赏
  • 举报
回复
第二个问题,输出是4+9=13,看汇编看出来的。
在linux, vc6, vc8, dev c++统统没运行出来,只好看vc8的汇编得出结论。
oo_v_oo 2007-12-01
  • 打赏
  • 举报
回复
上一个问题就没有答对,所以不指望这次

对于常类型的全局变量(包含常量数组),编译器一般会将其放在程序的代码段,
缺省情况下,代码段具有只读属性,所以,这个程序的结果就是:

当运行到更改常量地址内容时,系统提示,不能修改只读区

没有编译测试,不知解释的合适否.
eatta 2007-12-01
  • 打赏
  • 举报
回复
我也只是通过测试做出了如上结论而已,到底为何修改全局常量的值会出错,我就想不到了.书上也没说过.
楼主应该知道答案的吧,呵呵,等待此问题的解答
yuyunliuhen 2007-12-01
  • 打赏
  • 举报
回复
等待解说ING
我啃 2007-12-01
  • 打赏
  • 举报
回复
前12楼应该另开门户~
eatta继续~
eatta 2007-12-01
  • 打赏
  • 举报
回复
试图通过指针修改全局常量的值会在运行时刻出错.
这个问题我以前看书时曾记录下来过,不过我也没弄清原理是什么,书上也没介绍过.
或许是因为全局对象是放在全局数据区有关吧.
mu_yang 2007-12-01
  • 打赏
  • 举报
回复

抱歉,我不怎么看书
JobSeeker 2007-12-01
  • 打赏
  • 举报
回复
参考c++标准,array to pointer standard convertion
mu_yang 2007-12-01
  • 打赏
  • 举报
回复

请教依据?
JobSeeker 2007-12-01
  • 打赏
  • 举报
回复
"数组名是指针常量"---这个是比较经典的误解
mu_yang 2007-12-01
  • 打赏
  • 举报
回复

呵呵
你这话让我想起了1年前
这里有人用某本著名的书上怎么说
来反驳我说"数组名是指针常量"的事情了

加载更多回复(7)

64,676

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧