Comportamento di printf durante la stampa di% d senza fornire il nome della variabile

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

  •  22-07-2019
  •  | 
  •  

Domanda

Ho appena riscontrato un problema strano, sto provando a stampare una variabile intera, ma ho dimenticato di specificare il nome della variabile, ad esempio

printf("%d");

anziché

printf("%d", integerName);

Sorprendentemente il programma si compila, c'è un output e non è casuale. In effetti, sembra essere il numero intero che volevo stampare in primo luogo, che sembra essere m-1.

L'istruzione printf errata restituirà costantemente m-1 per tutto il tempo in cui il programma continua a funzionare ... In altre parole, si comporta esattamente come se l'istruzione leggesse

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

Qualcuno sa il motivo di questo comportamento? Sto usando g ++ senza alcuna opzione da riga di 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;
}
È stato utile?

Soluzione

Dici che "sorprendentemente il programma compila". In realtà, non è affatto sorprendente. C & amp; C ++ consente alle funzioni di avere elenchi di argomenti variabili. La definizione di printf è qualcosa del genere:

int printf(char*, ...);

Il " ... " significa che ci sono zero o più argomenti opzionali per la funzione. In effetti, uno dei motivi principali per cui C ha argomenti facoltativi è supportare printf & amp; famiglia di funzioni scanf.

C non ha una conoscenza specifica della funzione printf. Nel tuo esempio:

printf("%d");

Il compilatore non analizza la stringa di formato e determina che manca un argomento intero. Questo è un codice C perfettamente legale. Il fatto che manchi un argomento è un problema semantico che appare solo in fase di esecuzione. La funzione printf supporrà di aver fornito l'argomento e di cercarlo nello stack. Raccoglierà qualunque cosa accada. Accade semplicemente che nel tuo caso speciale stia stampando la cosa giusta, ma questa è un'eccezione. In generale, otterrai dati spazzatura. Questo comportamento varia da compilatore a compilatore e cambia anche in base alle opzioni di compilazione utilizzate; se attivi l'ottimizzazione del compilatore probabilmente otterrai risultati diversi.

Come sottolineato in uno dei commenti alla mia risposta, alcuni compilatori hanno " lint " come capacità che possono effettivamente rilevare chiamate printf / scanf errate. Ciò comporta che il compilatore analizzi la stringa di formato e determini il numero di argomenti extra previsti. Questo è un comportamento del compilatore molto speciale e non rileverà errori nel caso generale. vale a dire se scrivi il tuo " printf_better " funzione che ha la stessa firma di printf, il compilatore non rileverà se mancano argomenti.

Altri suggerimenti

Quello che succede è così.

printf("%d", m);

Sulla maggior parte dei sistemi l'indirizzo della stringa verrà inserito nello stack, quindi 'm' come numero intero (supponendo che sia un int / short / char). Non vi è alcun avvertimento perché printf è sostanzialmente dichiarato come 'int printf (const char *, ...);' - il ... significato che 'va bene'.

Quindi, dal momento che 'tutto va bene', accadono alcune cose strane quando metti lì variabili. Qualsiasi tipo integrale più piccolo di un int va come un int - cose del genere. Anche l'invio di nulla è ok.

Nell'implementazione di printf (o almeno un'implementazione 'semplice') troverai l'uso di va_list e va_arg (i nomi a volte differiscono leggermente in base alla conformità). Questi sono gli usi di un'implementazione per aggirare la parte "..." dell'elenco degli argomenti. Il problema qui è che NON esiste un controllo del tipo. Poiché non esiste un controllo del tipo, printf estrarrà dati casuali dallo stack di esecuzione quando esamina la stringa di formato ("% d ") e pensa che dovrebbe essere un 'int' successivo.

Scatto casuale nel buio direbbe che la chiamata di funzione che hai fatto poco prima di printf ha probabilmente superato 'm-1' come è il secondo parm? Questa è una delle tante possibilità, ma sarebbe interessante se ciò accadesse. :)

Buona fortuna.

A proposito, la maggior parte dei compilatori moderni (GCC credo?) hanno avvertimenti che possono essere abilitati per rilevare questo problema. Anche Lint lo credo. Sfortunatamente penso che con VC sia necessario usare il flag / analizza invece di ottenere gratuitamente.

Stai sbirciando nello stack. Cambia i valori dell'ottimizzatore e questo può cambiare. Cambia l'ordine delle dichiarazioni delle tue variabili (in particolare) m . Rendi m una variabile di registro. Rendi m una variabile globale.

Vedrai alcune variazioni in ciò che accade.

Questo è simile ai famosi hack di sovraccarico del buffer che si ottengono quando si esegue un I / O semplicistico.

Anche se dubito fortemente che ciò comporterebbe una violazione della memoria, l'intero che si ottiene è immondizia non definita.

Hai trovato un comportamento . Potrebbe essere stato qualsiasi altro comportamento, incluso un accesso alla memoria non valido.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top