所以最近我考虑strcpy并回到K& R,他们将实现显示为
while (*dst++ = *src++) ;
但是我错误地把它转录为:
while (*dst = *src) { src++; //technically could be ++src on these lines dst++; }
在任何情况下,让我思考编译器是否真的会为这两个代码生成不同的代码.我最初的想法是它们应该几乎相同,因为src和dst正在递增但从未使用过我认为编译器会知道不会尝试将它们作为生成的机器代码中的“变量”进行实际保存.
使用windows7和VS 2010 C SP1构建在32位发布模式(/ O2)中,我获得了上述两种版本的解组合代码.为了防止函数本身直接引用输入并进行内联,我使用每个函数创建了一个dll.我省略了制作的ASM的序言和结语.
while (*dst++ = *src++) 6EBB1003 8B 55 08 mov edx,dword ptr [src] 6EBB1006 8B 45 0C mov eax,dword ptr [dst] 6EBB1009 2B D0 sub edx,eax //prepare edx so that edx + eax always points to src 6EBB100B EB 03 jmp docopy+10h (6EBB1010h) 6EBB100D 8D 49 00 lea ecx,[ecx] //looks like align padding,never hit this line 6EBB1010 8A 0C 02 mov cl,byte ptr [edx+eax] //ptr [edx+ eax] points to char in src :loop begin 6EBB1013 88 08 mov byte ptr [eax],cl //copy char to dst 6EBB1015 40 inc eax //inc src ptr 6EBB1016 84 C9 test cl,cl // check for 0 (null terminator) 6EBB1018 75 F6 jne docopy+10h (6EBB1010h) //if not goto :loop begin ;
上面我注释了代码,本质上是一个循环,只有1个检查null和1个内存副本.
现在让我们来看看我的错误版本:
while (*dst = *src) 6EBB1003 8B 55 08 mov edx,dword ptr [src] 6EBB1006 8A 0A mov cl,byte ptr [edx] 6EBB1008 8B 45 0C mov eax,dword ptr [dst] 6EBB100B 88 08 mov byte ptr [eax],cl //copy 0th char to dst 6EBB100D 84 C9 test cl,cl //check for 0 6EBB100F 74 0D je docopy+1Eh (6EBB101Eh) // return if we encounter null terminator 6EBB1011 2B D0 sub edx,eax 6EBB1013 8A 4C 02 01 mov cl,byte ptr [edx+eax+1] //get +1th char :loop begin { src++; dst++; 6EBB1017 40 inc eax 6EBB1018 88 08 mov byte ptr [eax],cl //copy above char to dst 6EBB101A 84 C9 test cl,cl //check for 0 6EBB101C 75 F5 jne docopy+13h (6EBB1013h) // if not goto :loop begin }
在我的版本中,我看到它首先将第0个字符复制到目标,然后检查null,然后最后进入循环,再次检查null.所以循环基本保持不变,但现在它处理循环前的第0个字符.与第一种情况相比,这当然是次优的.
我想知道是否有人知道为什么编译器被阻止与第一个例子制作相同(或接近相同)的代码.这是ms编译器特定问题还是可能与我的编译器/链接器设置有关?
// in first dll project __declspec(dllexport) void docopy(const char* src,char* dst) { while (*dst++ = *src++); } __declspec(dllexport) void docopy(const char* src,char* dst) { while (*dst = *src) { ++src; ++dst; } } //seprate main.cpp file calls docopy void docopy(const char* src,char* dst); char* source ="source"; char destination[100]; int main() { docopy(source,destination); }
解决方法
因为在第一个示例中,即使src开始指向空字符,也会始终发生后递增.在相同的起始情况下,第二个示例不会增加指针.