.Net 6 CLR Card_Table跨代引用标记之数组位差
net吧
全部回复
仅看楼主
level 3
这段时间研究.Net CLR里面的跨带引用标记Card_tabele的时候在一个问题上阻隔了很久,蛋疼的是居然看不出任何破绽,如果不去实验下。(如果喜欢请关注一下公众号)
具体问题是什么呢?
card = (last_card_word - &card_table[0]) * card_word_width + bit_position;
上面这段代码是通过card_word的索引*Card_word的长度再加上余数的位移获取Card的一个公式计算。
问题就在于last_card_word - &card_table[0] 这个计算,last_card_word是 last_card_word = &card_table [card_word (card)],非常的明显这是从某个Card_table元素的地址值减去Card_table首元素的地址值。那么它的结果一定是从当前元素地址到首元素地址的位数。毫无疑问的,如果只是看代码的话。
那么问题来了?这个位数能作为card_word的索引吗?明显不能的,因为四个位才为一个索引。
所以实际上的索引代码应该是:
(last_card_word - &card_table[0])/4
但是很明显上面的那个公式
card = (last_card_word - &card_table[0]) * card_word_width + bit_position;
它没有除以4,那么问题出在哪儿呢?是CLR BUG?这种情况几乎不太可能,当然问题还得从自己身上找。
由于兜兜转转了,翻来覆去的看了代码。依旧没有结果,不如动手试下。
于是有了下面C程序:
#include<stdio.h>int main(void){ int shuzu[] = { 1,2,3,4,5,6 }; int* three = &shuzu[2]; int* one = &shuzu[0]; int n = three - one; getchar(); return 0;}
仔细看,元素3的地址值减去元素1的地址值,实际上是之间相差的位数,也就是8,为啥?因为元素3到元素1,之间隔了两个元素,一个元素是4位,那么两个自然是8位了。
但是这段程序在 Vs2019上编译的时候,它并不是等于8,而是等2.奇怪吧
它的汇编:
00007FF7F1FF402B mov rax,qword ptr [one] 00007FF7F1FF402F mov rcx,qword ptr [three] 00007FF7F1FF4033 sub rcx,rax 00007FF7F1FF4036 mov rax,rcx 00007FF7F1FF4039 sar rax,2 00007FF7F1FF403D mov dword ptr [n],eax
其实再明白不过了,sar rax,2 这段它会把位数差向右挪动2位。8右移2位,实际上就是2了。也就是元素3的索引值。
那么结果是对上号了。
为什么会有这种情况,实际上求地址值差的时候,它会以4个字节的结果反馈回来。其实你可以自己饰演int n = three - one;你把 int n改为char n 或者byte n 。结果都是一样的。
2021年11月21日 17点11分 1
1