与阴险的错误联合

Posted

技术标签:

【中文标题】与阴险的错误联合【英文标题】:Union with Insidious bug 【发布时间】:2017-06-29 07:56:11 【问题描述】:

当我们想要遍历整个数组并将数组的每个值与数组中存在的数字进行比较时遇到问题,比如 arr[0],为什么建议用 arr[0 初始化 int ],例如 int acomp =arr[0] 并将 acomp 与数组中存在的每个整数进行比较,而不是将数组中存在的每个整数与 arr[0] 进行比较? 例如,在下面的联合代码中,有人向我指出代码 2 比代码 1 更好,但我不太清楚为什么。

int unionarr(int p, int q)            //Code 1
    for(int i=0;i<size;i++)
        if(arr[i]==arr[p])
            arr[i]=arr[q];

int unionarr(int p, int q)            //Code 2
    int pid=arr[p];
    int qid=arr[q];
        for(int i=0;i<size;i++)
        if(arr[i]==pid)
            arr[i]=qid;

【问题讨论】:

union 是 C++ 中的保留关键字,如果不使用这两个变量,int pid=arr[p]; int qid=arr[q]; 的意义何在? 你的意思是if(arr[i]==pid)arr[i]=qid;,不是吗? 告诉你一个比另一个更好的人是在撒谎,因为both wont compile 通过优化构建两者,检查生成的程序集是否存在差异。 代码现在看起来好多了,但你的标题仍然很误导,因为代码中没有union,而且不清楚你指的是什么错误 【参考方案1】:

这是一个正确性问题。 for 循环内的赋值可以修改数组值。您可能会修改在分配的比较或右侧使用的元素。这就是为什么你必须在进入循环之前保存它们。

【讨论】:

谢谢!如果有人仍然无法理解这个答案,假设 arr[]=0,1,1,2,p 是 1,q 是 3。如果我们按照代码 1,我会得到的答案是 0,2,1,2 as arr[1] 会变为 2,当 i 在 for 循环中变为 2 时,它不会改变,因为 arr[2] 不等于 arr[p],因为 arr[ 1] 现在变成了 2。如果我们通过代码 1,我们会得到正确的答案,即 0,2,2,2。【参考方案2】:

制作本地副本 pid 和 qid 的值,否则必须在数组中重复查找,这是一种性能优化。 但是,如果任何现代编译器都无法理解这一点并隐式进行优化,我会感到惊讶。

【讨论】:

乱想:编译器是不是因为有别名的可能性&amp;arr[i] == &amp;arr[p] == &amp;arr[q]而无法在第一个代码中优化?【参考方案3】:

使用https://godbolt.org/ 可以比较两者。你关心的是循环内的指令。

使用 Clang 4.0 的程序集是:

代码 1

movsxd  rax, dword ptr [rbp - 16]
mov     ecx, dword ptr [4*rax + arr]
movsxd  rax, dword ptr [rbp - 8]
cmp     ecx, dword ptr [4*rax + arr]
jne     .LBB0_4
movsxd  rax, dword ptr [rbp - 12]
mov     ecx, dword ptr [4*rax + arr]
movsxd  rax, dword ptr [rbp - 16]
mov     dword ptr [4*rax + arr], ecx

代码 2

movsxd  rax, dword ptr [rbp - 24]
mov     ecx, dword ptr [4*rax + arr]
cmp     ecx, dword ptr [rbp - 16]
jne     .LBB0_4
mov     eax, dword ptr [rbp - 20]
movsxd  rcx, dword ptr [rbp - 24]
mov     dword ptr [4*rcx + arr], eax

【讨论】:

以上是关于与阴险的错误联合的主要内容,如果未能解决你的问题,请参考以下文章

与 LDAP 的 Keycloak 联合无法建立连接:错误!尝试连接到 LDAP 时出错。有关详细信息,请参阅 server.log

无法让嵌套类型保护与打字稿中的联合类型一起使用

你如何与多个 CTE 联合?

try-except语句与else子句联合使用处理可能出现的程序异常

在两个表上使用联合

联合运营商的Linq查询返回错误