Comportamiento de printf al imprimir un% d sin proporcionar el nombre de la variable

StackOverflow https://stackoverflow.com/questions/437816

  •  22-07-2019
  •  | 
  •  

Pregunta

Acabo de encontrar un problema extraño, estoy tratando de imprimir una variable entera, pero olvidé especificar el nombre de la variable, es decir,

printf("%d");

en lugar de

printf("%d", integerName);

Sorprendentemente el programa compila, hay salida y no es aleatorio. De hecho, resulta ser el número entero que quería imprimir en primer lugar, que resulta ser m-1.

La instrucción errónea printf generará constantemente m-1 mientras el programa siga ejecutándose ... En otras palabras, se comporta exactamente como si la instrucción dijera

printf("%d", m-1);

¿Alguien sabe la razón detrás de este comportamiento? Estoy usando g ++ sin ninguna opción de línea de comando.

#include <iostream>
#define maxN 100
#define ON 1
#define OFF 0

using namespace std;

void clearArray(int* array, int n);
int fillArray(int* array, int m, int n);

int main()
{
    int n = -1, i, m;
    int array[maxN];
    int found;

    scanf("%d", &n);

    while(n!=0)
    {
        found=0;
        m = 1;
        while(found!=1)
        {
            if(m != 2 && m != 3 && m != 4 && m != 6 && m != 12)
            {
                clearArray(array, n);
                if(fillArray(array, m, n) == 0)
                {
                    found = 1;
                }
            }
            m++;
        }

        printf("%d\n");

        scanf("%d", &n);
    }

    return 0;
}

void clearArray(int* array, int n)
{
    for(int i = 1; i <= n; i++)
        array[i] = ON;
}

int fillArray(int* array, int m, int n)
{
    int i = 1, j, offCounter = 0, incrementCounter;

    while(offCounter != n)
    {
        if(*(array+i)==ON) 
        {
            *(array+i) = OFF;
            offCounter++;       
        }
        else 
        {
            j = 0;
            while((*array+i+j)==OFF)
            {
                j++;
            }
            *(array+i+j) = OFF;
            offCounter++;           
        }
        if(*(array+13) == OFF && offCounter != n) return 1;
        if(offCounter ==n) break;

        incrementCounter = 0;       
        while(incrementCounter != m)
        {
            i++;
            if(i > n) i = 1;
            if(*(array+i) == ON) incrementCounter++; 
        }       
    }

    return 0;
}
¿Fue útil?

Solución

Usted dice que "sorprendentemente, el programa compila". En realidad, no es sorprendente en absoluto. C & amp; C ++ permite que las funciones tengan listas de argumentos variables. La definición de printf es algo como esto:

int printf(char*, ...);

El " ... " significa que hay cero o más argumentos opcionales para la función. De hecho, una de las razones principales por las que C tiene argumentos opcionales es para admitir printf & amp; familia de funciones scanf.

C no tiene un conocimiento especial de la función printf. En tu ejemplo:

printf("%d");

El compilador no analiza la cadena de formato y determina que falta un argumento entero. Este es el código C perfectamente legal. El hecho de que te falte un argumento es un problema semántico que solo aparece en tiempo de ejecución. La función printf asumirá que ha proporcionado el argumento y lo buscará en la pila. Recogerá lo que sea que esté allí. Simplemente sucede que en su caso especial está imprimiendo lo correcto, pero esto es una excepción. En general, obtendrá datos basura. Este comportamiento variará de un compilador a otro y también cambiará según las opciones de compilación que utilice; si activa la optimización del compilador, probablemente obtendrá resultados diferentes.

Como se señaló en uno de los comentarios a mi respuesta, algunos compiladores tienen "pelusa" como capacidades que realmente pueden detectar llamadas erróneas de printf / scanf. Esto implica que el compilador analiza la cadena de formato y determina la cantidad de argumentos adicionales esperados. Este es un comportamiento de compilador muy especial y no detectará errores en el caso general. es decir, si escribe su propio " printf_better " función que tiene la misma firma que printf, el compilador no detectará si faltan argumentos.

Otros consejos

Lo que sucede se ve así.

printf("%d", m);

En la mayoría de los sistemas, la dirección de la cadena se insertará en la pila, y luego 'm' como un entero (suponiendo que sea un int / short / char). No hay advertencia porque printf se declara básicamente como 'int printf (const char *, ...);' - el ... que significa 'todo vale'.

Entonces, como 'todo vale', suceden algunas cosas extrañas cuando pones variables allí. Cualquier tipo integral más pequeño que un int va como un int, cosas así. No enviar nada está bien también.

En la implementación de printf (o al menos una implementación 'simple') encontrará el uso de va_list y va_arg (los nombres a veces difieren ligeramente según la conformidad). Esto es lo que utiliza una implementación para recorrer la parte '...' de la lista de argumentos. El problema aquí es que NO hay verificación de tipo. Como no hay verificación de tipo, printf extraerá datos aleatorios de la pila de ejecución cuando observe la cadena de formato ("% d ") y piense que se supone que debe ser un 'int' siguiente.

Disparo aleatorio en la oscuridad diría que la llamada a la función que realizó justo antes de printf posiblemente pasó 'm-1' como su segundo parámetro. Esa es una de las muchas posibilidades, pero sería interesante si este fuera el caso. :)

Buena suerte.

Por cierto, la mayoría de los compiladores modernos (¿CCG, creo?) tienen advertencias que se pueden habilitar para detectar este problema. Lint también lo hace, creo. Desafortunadamente, creo que con VC necesita usar el indicador / analyse en lugar de obtenerlo gratis.

Estás mirando hacia la pila. Cambie los valores del optimizador, y esto puede cambiar. Cambie el orden de las declaraciones de sus variables (particularmente) m . Convierta m en una variable de registro. Convierta m en una variable global.

Verás algunas variaciones en lo que sucede.

Esto es similar a los famosos hacks de desbordamiento de búfer que obtienes cuando haces E / S simplistas.

Aunque dudaría mucho de que esto resulte en una violación de memoria, el número entero que obtienes es basura indefinida.

Encontraste one comportamiento. Podría haber sido cualquier otro comportamiento, incluido un acceso no válido a la memoria.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top