理解代码的一个不错的方法是重构它。
首先,创建一个测试函数和几个测试用例。然后重写函数。它可以看起来像这样。它非常简单,对于更大的重构,我会让它更复杂一点。
bool test(uint param_1, uint param_2)
{
return (FUN_80059070(param_1, param_2) == my_func(param_1, param2));
}
int main()
{
uint test_cases[3][2] = { {0,0}, {8, 12}, {12, 14}};
for(int i=0; i<3; i++) {
if(! test(test_cases[i][0], test_cases[i][1])) {
printf("Case %d with values %d and %d failed\n",
i, test_cases[i][0], test_cases[i][1]);
exit(EXIT_FAILURE);
}
}
printf("All tests passed\n");
}
由于您已经知道参数的条件,请考虑编写一个为您创建测试用例的片段。创建大量测试用例,但要注意溢出的风险。
之后,您可以开始重构过程。首先复制 to 的整个主体,FUN_80059070
然后my_func
我们将替换行和代码块。
例如,首先通过1 << (param_2 & 0x1f);
谷歌搜索和测试不同的值来调查实际做了什么。当您了解它的作用时,您就创建了一个函数。
uint describing_name(uint x) { return (x & 0x1f); }
并将行初始化更改uVar3
为
uVar3 = 1 << describing_name(param_2);
然后迈出一小步。例如,uVar3 << 2
等价于uVar * 4
,但后者更易于阅读。在更一般的情况下,x << y
与 相同x * pow(2,y)
。请注意,pow
具有签名double pow(double, double)
,因此要么强制转换,要么编写您自己的整数变体。
然后迭代地处理代码并每次运行测试。如果代码的某些部分特别棘手,您可以或当然可以为该功能创建一个带有适当测试用例的单独测试。
请注意,替换<<
为pow
不一定总是有意义的。有时它们用于位操作,有时也用于更快的乘法。对于带有坏或没有优化器的编译器,它可以产生巨大的性能差异。在这些情况下,将它们替换为有意义,pow
但在其他情况下<<
可用于删除最高有效位。
例如,我不知道uint
您的系统上有多少位,但x & 0x1f
如果您设置除 15 个最低有效位之外的所有位,我将返回您获得的数字。大端或小端在这里可能很重要。我不这么认为,但我不确定。如果我是正确的,那么与模运算x & 0x1f
相同。x % 32
这也是常见的优化。位移比乘法和取模快得多。所以我们可以将函数重命名describing_name
为modulo32
.
这if((int)uVar4 < 0)
基本上是一种“聪明”的方式来检查是否设置了最高有效位,或者是否uVar4
包含比signed int
可以表示的更大的数字。两种解释是等价的。
现在它看起来像这样:
uint modulo32(uint x) { return (x & 0x1f); }
bool larger_than_INT_MAX(uint x) { return (int)x<0; }
uint my_func(uint param_1, uint param_2)
{
uint uVar1, uVar2, uVar3, uVar4, uVar5, uVar6;
uVar5 = 0;
uVar4 = 1;
uVar6 = 0;
uVar3 = powi(2, modulo32(param_2));
while ((uVar3 < param_1 && (uVar3 * 4 != 0))) {
uVar4 = uVar4 + 1;
uVar3 = uVar3 * 4;
}
uVar1 = powi(2, uVar4 + (modulo32(param_2-1)));
while (uVar3 != 0) {
uVar2 = uVar5 * powi(2, modulo32(uVar4));
if (larger_than_INT_MAX(uVar4)) {
uVar2 = uVar5 / powi(2, -uVar4);
}
uVar2 = uVar2 + uVar6 + uVar3;
if (uVar2 <= param_1) {
uVar5 = uVar5 + uVar1;
uVar6 = uVar2;
}
uVar1 = uVar1 / 2;
uVar3 = uVar3 / 4;
uVar4 = uVar4 - 1;
}
return uVar5;
}
powi
是我写的一个简单的整数幂函数。上面的代码仍然不是很容易理解,但至少怪异的位操作和“聪明”的代码部分不碍事。
现在我注意到一些事情。uVar3 * 4 != 0
作为数学运算并没有真正意义,因为这仅适用于uVar3 == 0
. 但这所做的是检查除了两个最重要的位之外的所有位是否为零。所以你可以用这个函数替换它:
bool fourteen_least_significant_bits_are_not_zero(uint x) {
return x << 2 != 0;
}
当您更了解代码的实际含义时,替换函数名称或使用注释。当您知道它们的作用时uVar1
,还要替换漂亮的匿名名称等。uVar2
在此之后,我建议尝试重命名此函数:
void describing_name(uint *uVar3p, uint *uVar4p, uint param_1)
// These are declared so that you can just copy paste the code
uint uVar3 = *uVar3p;
uint uVar4 = *uVar4p;
// Copy paste with no modifications
while ((uVar3 < param_1 &&
fourteen_least_significant_bits_are_not_zero(uVar3)) {
uVar4 = uVar4 + 1;
uVar3 = uVar3 * 4;
}
// And write back the values
*uVar3p = uVar3;
*uVar4p = uVar4;
}
将 while 循环替换为:
describing_name(&uVar3, &uVar4, param_1);
重构代码通常是理解它的最佳方式。请记住,重构时测试至关重要。