Feedback
2009-08-20 22:34 |
(S)(*(sa+100*sizeof(S))).a();
这个表达式有2个小问题, 其他都到位。
由此, 可以给他解释另一个更经典的……
static_cast<S*>(0)->a();
-_-; ...
回复 更多评论
2009-08-20 22:57 |
不管怎么说,这个都是错误的。而且很多时候都是致命的。
回复 更多评论
2009-08-20 23:21 |
@胡友蒙
代码中只出现了无效地址, 但并没有解引用。
>而且很多时候都是致命的
这样做至少在i386上是100%不会出现任何问题, 所以, 不用说得这么吓人。
回复 更多评论
2009-08-21 01:24 |
瞎猜是没用的,直接反汇编就一目了然了。这是编译器优化的结果。静态变量就直接访问了。
回复 更多评论
2009-08-21 02:05 |
这种只读内存的代码不会有什么问题。而且你这个代码访问的是静态内存sum。
回复 更多评论
2009-08-21 10:20 |
在某编译器下,我见过这样的怪事。声明了某类的指针p,但并没有将其指向任何对象,却可以调用这个类里的非静态方法(如p->func( );)。当然,这个方法里边有访问成员变量的地方,在那里,程序崩溃了。
回复 更多评论
2009-08-21 10:23 |
int main()
{
s *ss = new s();
ss->a();
ss = NULL;
ss->a();
return 0;
}
可以试下这个测试函数,原理是一样的,在于this指针.....
回复 更多评论
2009-08-21 10:59 |
原因是你没有对无效的地址进行写操作。你弄个虚函数试试
回复 更多评论
2009-08-21 11:25 |
对于错误代码的处理,不同的编译器会使用不同的手法, 所以这样的代码还是少写
回复 更多评论
2009-08-21 11:40 |
我看不懂~~~
回复 更多评论
2009-08-21 17:18 |
是否该大方过
回复 更多评论
2009-08-23 09:29 |
void a()
{
cout<<sum<<endl;
//因为这段代码没有访问this指针指向的内存
}
回复 更多评论
2009-08-23 16:55 |
学习啦...
回复 更多评论
2009-09-01 21:23 |
代码中出现无效地址,那通过无效地址去访问有效地址按理说也是错的哟~~
如果不解引用就不出错的话,但是博主的解释中就有解引用了。
还是不会汇编啊,哪天抽空看看,看看优化的代码
回复 更多评论
2009-09-01 21:31 |
@莫失莫忘
你是从那句解释中看出有解引用一说的?
回复 更多评论
2009-09-01 21:37 |
@莫失莫忘
静态成员本来就可以通过
1. 类名
S::sum
2. 实例
S s;
s.sum;
两种形式访问, 毫无区别, 推荐第1种形式。
this->sum 依然是 S::sum, 而不会是s的非静态成员。
它根本就没有非静态的sum, 难道编译器还因为this->sum专门给它再加这么一个非静态sum么?
回复 更多评论
2009-09-01 21:49 |
在那里解引用?自己去看看博主的解释吧!
-----------------------------------
OwnWaterloo误解了,至少我的编译器没那么智能——第二个回复就不解释了,这只是测试下非静态成员函数对于静态成员变量的访问。
回复 更多评论
2009-09-01 22:05 |
@莫失莫忘
> 自己去看看博主的解释吧!
我又看了一遍。
只有对S::sum有语意上以及实现上的解引用。
对 sa[100].a(); 有语意上的解引用。
Chuck 这文章正是想得出 —— s[100].a(); 只存在语意上的解引用, 实际上没有进行解引用, 所以才不会出错 —— 的观点。
> 代码中出现无效地址,那通过无效地址去访问有效地址按理说也是错的哟~~
> 如果不解引用就不出错的话,但是博主的解释中就有解引用了。
依然不明白你在说什么……
回复 更多评论
2009-09-01 22:12 |
C++中对于数组越界之类的问题是不会在编译时进行检查的,如果代码中越界了,那编译不会出错。但是如果运行就会报错:访问未初始化的数据之类的。而那句:
sa[100].a();
因为无论如何一个函数在内存中只有一个,所以这句解释为:
S:a();
更具体点就是这样了:
S::a(&S[100]);
而S[100]是一个无效地址,但是也没关系。
cout<<sum<<endl;
这句打印的是全局变量,这就不涉及越界问题了。
------------------------我是纯洁的分割线大人----------------------
问一下:
OwnWaterloo,你是不是一直等在CPP BLOG上?
回复 更多评论
2009-09-01 22:14 |
不明白无所谓,我的测试代码改了不少,重要的是:
我觉得现在我的解释应该是正解滴了
回复 更多评论
2009-09-01 22:22 |
语意上的解引用?实际上的解应用?
真心话哦:这个解释确实是让我涨见识了。这我同意!
我的原话话:代码中出现无效地址,那通过无效地址去访问有效地址按理说也是错的哟。
是因为:在JAVA中,通过无效的地址去访问是绝对会出错的。当然了,在CPP论坛咱不说JAVA,这只是常人的一个思维:通过不存在的去访问当然会出错,所以博主的同学才会有那个问题。
回复 更多评论
2009-09-01 22:42 |
@莫失莫忘
> 如果代码中越界了,那编译不会出错。但是如果运行就会报错
对数组越界, C++标准中的描述是“未定义”。
据说有一些平台(我只用过i386和ppc), 确实会检查无效地址——甚至只是获取,而没有进行操作, 例如:
int a[12];
int* p = &a[13]; // 就会引发错误。
而在i386下, 后一句代码只会产生一个无效(无效的准确含义是越界)地址, 不会引发错误 —— 甚至对无效地址的访问(读写)是否会引发错误, 都要看运气。
首先, &a[13]肯定不是nullptr。 nullptr检测区就不会起作用。
其次, vc可能会在a[12]附近安插一些guard。
int read = *p; // 无法检测
*p = 1212; // 检测出写, 如果guard的恰好是1212,越界写都判断不出
guard的值好像是0xcc。
三, vc的release配置不会安插guard。
四, 还有一种能检测到错误的情况。
p 指向的地址还没有被保留或提交。
因为i386 下windows的栈通常是向下增长,
p = &a[ /* 这里需要一个很大的正数 */ ]; 才能越过栈底, 达到保护区。
但
p = &a[ /* 如果这里是负数 */ ] ; 越过以提交的栈顶, 达到保护区就容易许多。
如果p误指的不仅仅是栈上变量, 那还可以增加一些错误检测的机会。
没有被保留与提交的页, 读写都会错。
已提交的页, 还得看页的保护属性。
说了这么多, 就是想说所谓的“运行就会报错”, 是不靠谱的。
如你所见, 上面已经列举出许多运行时不会报错的情形——这种情形还不少。
所以, 大家写代码还是要按照规范来……
为什么这种不合乎规范的代码不会出现错误, 只能作为茶余饭后的谈资……
回复 更多评论
2009-09-01 22:58 |
@莫失莫忘
> 语意上的解引用?实际上的解应用?
这真的不是搞笑。
S sa[12];
int ia[12]
那么sa[100]; ia[26]; 这2
个表达式, 按C++的说法, 其结果类型就是 S& 和 i&。
如果给一些C程序员看, 他们还会告诉你,
sa[100]; 等效于 *(sa+100);
ia[26]; 等效于 *(ia+26);
所以, 我说这里有一个语意上的解引用。
但实际在, 在i386下, msvc和gcc编译器, 都不会理睬
sa[100]; ia[26]; 这么一个孤单的表达式。
只有当
int i = ia[26]; /* int i = *(ia+26); */ 读
ia[26] = 1212; /* *(ia+26) = 1212 */ 写
的时候, 才会真正产生"解引用"的代码 —— mov指令
并且, 只有 int* p = ia + 26; p的地址没有相应权限(将未提交也理解为缺陷缺失的话), 才会引发错误。
对自定义类型:
sa[100].a(); 调用处都不会产生解引用代码。
只有在
S::a() { // 定义处
才有可能产生解引用代码—— 如果操作非静态数据成员的话。
}
按C++标准来说, 表达式:
sa[100]; ia[26];
已经是未定义行为了。 只是在i386上, 通常不会造成什么问题。
又因为S::a()没有操作非静态数据成员, 就不会产生实际的mov指令, 也不会有问题。
-----------------------------------------
sum是S的静态数据成员, sum本来就是在静态存储区中的。
即使是按C++标准, 无论是
S::sum
this->sum
都是对S::sum——静态存储区中的一个变量——的操作。
没有问题是自然的。
for (int i=0;i<1212;++i) sa[i].sum
只要 sa[i]表达式不出问题, sa[i].sum 依然是访问的S::sum , 没有问题还是显然的。
回复 更多评论
2009-09-01 23:11 |
@莫失莫忘
> OwnWaterloo,你是不是一直等在CPP BLOG上?
有个"有回复时邮件通知我"。
我通常都开着邮箱, so ……
> 在JAVA中,通过无效的地址去访问是绝对会出错的
java中不能随意操作指针。 除了null, 如何能产生一个无效地址?
array 都是一个所谓的"对象", 记录着大小, 随时可以检查边界。
JNI可以吗?
> 这只是常人的一个思维:通过不存在的去访问当然会出错
这是javaer的思维。
现在使用的计算机中, 冯诺依曼架构是主流。
所以, 当cpu真正进行运算的时候, 一切都是地址。
硬件会提供一些手段, 区分一些地址是否有效。
但通常是软件, 通过"指令流"而产生"控制流", 来判断某地址是否有效。
C/C++不提供这些保姆功能。
这个例子充分说明了,正因为没有这些保姆功能, C/C++中, 程序员要尤其小心, 不能依赖于"编译器、OS、CPU"提供的"功能有限、可有可无(标准未定义)"的保护手段, 而要自己注意越界问题。
回复 更多评论