Así Hace poco estaba pensando en strcpy y de nuevo a K & R donde muestran la aplicación como¿por qué vs C++ 2010 compilador producir un código de montaje diferente para función similar
while (*dst++ = *src++) ;
Sin embargo por error me transcribí como:
while (*dst = *src)
{
src++; //technically could be ++src on these lines
dst++;
}
En cualquier caso, eso me hizo pensar si el compilador realmente produciría un código diferente para estos dos. Mi idea inicial es que deberían ser casi idénticos, ya que src y dst se están incrementando pero nunca se usaron. Pensé que el compilador sabría que no trataría de preservarlos como "variables" en el código máquina producido.
Usando Windows7 con VS 2010 C++ SP1 construyendo en el modo Release de 32 bit (/ O2), obtuve el código de ensamblaje para las dos encarnaciones anteriores. Para evitar que la función haga referencia a la entrada directamente y esté en línea hice una dll con cada una de las funciones. He omitido el prólogo y epílogo del ASM producido.
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
;
encima he anotado el código, esencialmente un solo bucle, sólo el 1 cheque de copia de memoria nula y 1.
Ahora veamos mi versión error:
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
}
En mi versión, veo que primero copia el carbón 0th al destino, a continuación, busca nula, y luego entra finalmente el bucle donde comprueba para null de nuevo. Entonces, el bucle sigue siendo básicamente el mismo, pero ahora maneja el 0 ° carácter antes del bucle. Esto, por supuesto, será subóptimo en comparación con el primer caso.
Me pregunto si alguien sabe por qué el compilador no puede hacer el mismo código (o casi el mismo) que el primer ejemplo. ¿Es este un problema específico del compilador de ms o posiblemente con mi configuración de compilador/enlazador?
aquí está el código completo, 2 archivos (1 función reemplaza a la otra).
// 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);
}
¿Podría publicar todas las piezas de código C con las que comenzó? Puede ser diferente debido a las declaraciones de src & dst, pero no podría saberlo. ¿Los preámbulos del ensamblador que eliminó son idénticos? No es necesario pegarlos si lo son. – gbulmer
En ambos casos, ese es el código completo, a excepción de epílogo y prólogo. La declaración de ambas funciones es __declspec (dllexport) void docopy (char * src, char * dst) – skimon
Este es un mal estilo de codificación de código, porque muchos lectores verán el '=' dentro de la expresión como un error tipográfico para "==". –