2011-01-01 691 views
10

Uno de mis amigos envía este código para mí, diciendo que no funciona como se esperaba:salida inesperada del programa BubbleSort con MSVC vs TCC

#include<stdio.h> 

void main() 
{ 
    int a [10] ={23, 100, 20, 30, 25, 45, 40, 55, 43, 42}; 
    int sizeOfInput = sizeof(a)/sizeof(int); 
    int b, outer, inner, c; 
    printf("Size is : %d \n", sizeOfInput); 

    printf("Values before bubble sort are : \n"); 
    for (b = 0; b &lt; sizeOfInput; b++) 
     printf("%d\n", a[b]); 

    printf("End of values before bubble sort... \n"); 

    for (outer = sizeOfInput; outer > 0; outer--) 
    { 
     for ( inner = 0 ; inner < outer ; inner++) 
     { 
     printf ("Comparing positions: %d and %d\n",inner,inner+1); 
     if (a[inner] > a[inner + 1]) 
     { 
       int tmp = a[inner]; 
       a[inner] = a [inner+1]; 
      a[inner+1] = tmp; 
      } 
     } 
     printf ("Bubble sort total array size after inner loop is %d :\n",sizeOfInput); 
     printf ("Bubble sort sizeOfInput after inner loop is %d :\n",sizeOfInput); 
    } 
    printf ("Bubble sort total array size at the end is %d :\n",sizeOfInput); 
    for (c = 0 ; c < sizeOfInput; c++) 
     printf("Element: %d\n", a[c]); 
} 

estoy usando Micosoft Estudio del sistema de Visual Line Tool para compilar esto en una máquina con Windows XP. cl /EHsc bubblesort01.c

Mi amigo obtiene la salida correcta en una máquina de dinosaurios (el código se compila utilizando TCC allí).
Mi salida es inesperada. La matriz crece misteriosamente de tamaño, en el medio.

Si cambia el código para que la variable sizeOfInput se cambie a , ¡obtendrá los resultados esperados!

Una búsqueda realizada en Microsoft Visual C++ Developer Center no da ningún resultado para "sizeOfInput".

No soy un experto en C/C++, y tengo curiosidad por saber por qué sucede esto, ¿algún experto en C/C++ que pueda "arrojar algo de luz" sobre esto?
Nota no relacionada: pensé seriamente en volver a escribir todo el código para usar el ordenamiento rápido o el tipo de fusión antes de publicarlo aquí. Pero, después de todo, no es el tipo de Stooge ...

Editar: Sé que el código no es correcto (se lee más allá del último elemento), pero tengo curiosidad de por qué el nombre de la variable hace la diferencia.

+0

Gracias por la edición, pero acabo de notar que en mal estado los (<>). > etiquetas y <. – crnlx

Respuesta

27

Como interjay's answer mentioned, una vez que se tiene un comportamiento indefinido, todas las apuestas están apagadas. Sin embargo, cuando dijiste que el mero cambio de nombre de la variable cambió el comportamiento del programa, tuve curiosidad sobre lo que estaba sucediendo (indefinido o no).

En primer lugar, no creía que el cambio de nombre de la variable cambiaría la salida del compilador (siendo todo lo demás igual), pero efectivamente, cuando lo probé, me sorprendió ver exactamente lo que describió.

Así que hice que el compilador volcara el ensamblado para el código que generaba para cada versión del archivo fuente y ejecuté una comparación.Esto es lo que he encontrado en la descripción de los compiladores de cómo se estaba diseñando las variables locales:

***** test.sizeOfInput.cod 
    _c$ = -56     ; size = 4 
    _b$ = -52     ; size = 4 
    _inner$ = -48    ; size = 4 
    _a$ = -44     ; size = 40 
>>> _sizeOfInput$ = -4   ; size = 4 
    _main PROC 
    ***** test.sizeOfInputt.cod 
    _c$ = -56     ; size = 4 
>>> _sizeOfInputt$ = -52   ; size = 4 
    _b$ = -48     ; size = 4 
    _inner$ = -44    ; size = 4 
    _a$ = -40     ; size = 40 
    _main PROC 
    ***** 

Lo que usted notará es que cuando la variable se llama sizeOfInput, él compilador lo coloca en una dirección más alta que la gama a (justo después del final de la matriz), y cuando la variable se llama sizeOfInputt, la coloca en una dirección inferior a la matriz a en lugar de pasar el final de la matriz. Eso significa que en la compilación que tiene la variable llamada sizeOfInput, el comportamiento indefinido que ocurre cuando modifica a[10] está cambiando el valor de sizeOfInput. En la construcción que usa el nombre sizeOfInputt, dado que esa variable no está al final de la matriz, la escritura en a[10] destroza algo más.

En cuanto a por qué el compilador establecería las variables de manera diferente cuando el nombre de uno cambia de una manera aparentemente insignificante, no tengo ni idea.

Pero este es un buen ejemplo de por qué no debe contar con el diseño de las variables locales (o casi todas las variables, aunque puede contar con el orden de disposición de los elementos struct), y por qué cuando se trata de indefinido el comportamiento, "funciona en mi máquina" no lo corta como prueba de que algo funciona.

+0

Michael, ¡esto ayuda! Esto es lo que estaba buscando. Aceptando tu respuesta. Gracias por tomarse el tiempo para investigar esto. – crnlx

+8

Probablemente todas las variables se pongan en alguna forma de mapa hash y los valores hash de 'sizeOfInput' y' sizeOfInputt' son simplemente diferentes. –

6

Su código se lee más allá del final de la matriz. El valor máximo de outer es 10, y el valor máximo de inner es 9, por lo que a[inner+1] leerá a[10]. Esto le dará undefined behavior, lo que explica por qué los diferentes compiladores dan resultados diferentes.

En cuanto al nombre de la variable que hace la diferencia: Probablemente no. Es posible que si ejecuta el mismo código dos veces (usando el mismo nombre de variable), obtendrá diferentes resultados. Básicamente, al invocar un comportamiento indefinido, no puede estar seguro de cualquier cosa que haga su programa, por lo que es mejor no tratar de encontrar el significado en cosas como nombres de variables.

También existe la posibilidad de que el nombre de la variable marque la diferencia. Eso depende de la implementación del compilador: por ejemplo, el uso de un nombre de variable diferente podría hacer que el compilador organice la memoria de forma diferente de alguna manera, lo que podría hacer que el programa se comportara de manera diferente. Creo que la mayoría de los compiladores generarían el mismo código si cambiases los nombres de las variables, así que probablemente fue solo cuestión de suerte.

+0

interjay: Gracias, actualicé mi publicación. Sé que el código lee un elemento más: es obvio desde la salida. Estoy desconcertado sobre el nombre de la variable que hace una gran diferencia. – crnlx

+0

@interjay: Vi su actualización: no, si nombro la variable como algo más, compila y me da la respuesta correcta desde la primera vez. Además, recompilar con "sizeOfInput" no hizo ninguna diferencia. – crnlx

+0

@Sujith: incluso si eso es cierto, es casi imposible saber con certeza por qué sucede sin examinar el código fuente del compilador. Hice otra edición para dar un ejemplo. – interjay

1

Michael Burr's reply expone un comportamiento interesante del compilador. Pero todavía dudo que el nombre de la variable local pueda afectar su disposición en la pila para el registro activo de la función.

Estoy utilizando VC++ 2013 con la línea de comando anterior (cl.exe/EHsc progam.cpp) y no puedo reprogramar - cambiar el nombre de la variable no cambia el comportamiento del programa, en cambio, muestra un bloqueo aleatorio estable (algunas vueltas vuelven los resultados son buenos y algunas ejecuciones fallan).

La razón de la caída aleatoria anterior se __security_cookie se almacena directamente encima (dirección más grande) la matriz a, y como se define como matriz de enteros chamuscado, el resultado dependerá de la bit de signo (mis-interpretados) de la valor de __security_cookie. Si es un entero positivo mayor que 100, sigue siendo el valor más grande en la matriz a, por lo que la ordenación no lo cambiará a otras posiciones, entonces la comprobación (__security_check_cookie) al final de la función estará bien. Si es menor que 100 o un entero negativo, se cambiará al elemento inferior en la matriz después de ordenar, por lo que __security_check_cookie informa de un error. Esto significa que el comportamiento del programa depende del valor generado aleatoriamente para __security_cookie.

Cambié el programa de prueba original a continuación para facilitar las pruebas. También amplié el resultado para incluir el elemento de uno por uno (arrayLen + 1), y pudimos predecir el comportamiento basado en el valor original en el elemento después de la matriz definida a.

#include<stdio.h> 
#define arrayLen sizeOfInputt 

void main() 
{ 
    int a [10] ={23, 100, 20, 30, 25, 45, 40, 55, 43, 42}; 
    int arrayLen = sizeof(a)/sizeof(int); 
    int b, outer, inner, c; 
    printf("Size is : %d \n", arrayLen); 

    printf("Values before bubble sort are : \n"); 
    for (b = 0; b < arrayLen + 1; b++) 
     printf("%d\n", a[b]); 

    printf("End of values before bubble sort... \n"); 

    for (outer = arrayLen; outer > 0; outer--) 
    { 
     for ( inner = 0 ; inner < outer ; inner++) 
     { 
     printf ("Comparing positions: %d and %d\n",inner,inner+1); 
     if (a[inner] > a[inner + 1]) 
     { 
       int tmp = a[inner]; 
       a[inner] = a [inner+1]; 
      a[inner+1] = tmp; 
      } 
     } 
     printf ("Bubble sort total array size after inner loop is %d :\n",arrayLen); 
     printf ("Bubble sort arrayLen after inner loop is %d :\n",arrayLen); 
    } 
    printf ("Bubble sort total array size at the end is %d :\n",arrayLen); 
    for (c = 0 ; c < arrayLen; c++) 
     printf("Element: %d\n", a[c]); 
} 
Cuestiones relacionadas