Verhalten von printf beim Drucken eines% d ohne Variablennamen zu Liefern

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

  •  22-07-2019
  •  | 
  •  

Frage

Ich habe begegnet nur ein seltsames Problem, ich versuche, einen Integer-Variable printf, aber ich habe vergessen, die Variablennamen angeben, d.

printf("%d");

statt

printf("%d", integerName);

Überraschenderweise das Programm kompiliert, gibt es Ausgang, und es ist nicht zufällig. In der Tat kommt es vor, die sehr integer sein, die ich in erster Linie drucken wollte, die m-1.

sein geschieht

Die errorneous printf Anweisung wird konsequent Ausgang m-1, solange das Programm läuft weiter ... Mit anderen Worten, es verhält sich genau so, wie wenn die Anweisung liest

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

Jeder kennt den Grund für dieses Verhalten? Ich bin mit g ++ ohne Befehlszeilenoptionen an.

#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;
}
War es hilfreich?

Lösung

Sie sagen, dass „überraschend das Programm kompiliert“. Eigentlich ist es überhaupt nicht überraschend. C & C ++ ermöglichen Funktionen variable Argumentlisten zu haben. Die Definition für printf ist so etwas wie folgt aus:

int printf(char*, ...);

Die „...“ bedeutet, dass es keine oder mehr optionalen Argumente an die Funktion. In der Tat ist einer der Hauptgründe, C optionale Argumente hat, ist die printf & Scanf Familie von Funktionen zu unterstützen.

C hat keine spezielle Kenntnis der printf-Funktion. In Ihrem Beispiel:

printf("%d");

Der Compiler analysiert nicht den Format-String und bestimmt, dass ein Integer-Argument fehlt. Dies ist völlig legal C-Code. Die Tatsache, dass Sie ein Argument fehlen, ist ein semantisches Problem, das nur zur Laufzeit angezeigt wird. Die Funktion printf davon aus, dass Sie das Argument geliefert haben, und machen Sie es sich auf dem Stapel suchen. Es wird abholen, was dort geschieht, zu sein. Es passiert einfach, dass in Ihrem speziellen Fall ist es das Richtige ist, den Druck, aber dies ist eine Ausnahme. In der Regel werden Sie Müll Daten. Dieses Verhalten wird von Compiler variieren zu Compiler und ändert sich auch je nachdem, was kompilieren Optionen, die Sie verwenden; wenn Sie auf Compiler-Optimierung wechseln werden Sie wahrscheinlich unterschiedliche Ergebnisse erhalten.

Wie in einer der Kommentare auf meine Antwort darauf hingewiesen, einige Compiler haben „Fussel“ wie Funktionen, die tatsächlich erkennen kann fehlerhafte printf / scanf Anrufe. Dies beinhaltet die Compiler die Format-String und die Bestimmung der Anzahl der zusätzlichen Argumente Parsen erwartet. Dies ist sehr spezielles Compiler Verhalten und nicht Fehler im allgemeinen Fall erkennen. das heißt, wenn Sie Ihre eigene „printf_better“ Funktion schreiben, die die gleiche Signatur wie printf hat, erkennt der Compiler nicht, ob irgendwelche Argumente fehlen.

Andere Tipps

Was passiert, sieht wie folgt aus.

printf("%d", m);

Bei den meisten Systemen die Adresse des Strings auf den Stapel geschoben werden, und dann 'm' als Integer (vorausgesetzt, es ist ein int / short / char). Es gibt keine Warnung, weil printf grundsätzlich als 'int printf(const char *, ...);' deklariert wird -. Der ... bedeutet ‚alles geht‘

So, da ‚anything goes‘ ein paar seltsame Dinge passieren, wenn Sie Variablen dort setzen. All integraler Typ kleiner als ein int geht als int - solche Dinge. garnichts Senden ok ist als gut.

In der printf-Implementierung (oder zumindest eine ‚einfache‘ Implementierung) Sie Nutzung von va_list und va_arg finden (manchmal Namen unterscheiden sich geringfügig basierend auf Konformität). Diese sind, was eine Implementierung zu Fuß benutzt um den ‚...‘ Teil der Argumentliste. Problem hier ist, dass es keine Typüberprüfung ist. Da es keine Typprüfung ist, wird printf Zufallsdaten aus Stapel der Ausführung ziehen, wenn es an der ("%d") Format-String sieht und denkt, dass es soll neben einer 'int' sein.

Random Schuss im Dunkel würde sagen, dass die Funktion rufen Sie kurz vor printf möglicherweise weitergegeben 'm-1' gemacht, wie es zweiter Parm ist? Das ist eine von vielen Möglichkeiten - aber es wäre interessant, wenn dies geschehen ist der Fall zu sein. :)

Viel Glück.

Übrigens - (? GCC glaube ich) die meisten modernen Compiler haben Warnungen, die aktiviert werden können, um dieses Problem zu erkennen. Lint tut auch, glaube ich. Leider denke ich mit VC Sie die / analysieren Flagge anstatt sich kostenlos verwenden müssen.

Sie spähte in den Stapel. Ändern Sie die Optimierungsprogramm Werte, und dies kann sich ändern. Ändern Sie die Reihenfolge der Erklärungen Ihre Variablen (insbesondere) m. Machen m eine Registervariable. Machen m eine globale Variable.

Sie werden einige Variationen in sehen, was passiert.

Dies ist vergleichbar mit dem bekannten Buffer Overrun Hacks, die Sie erhalten, wenn Sie stark vereinfachen I / O tun.

Während ich würde dies sehr bezweifeln in einer Speicherverletzung führen würde, die ganze Zahl Sie bekommen, ist nicht definiert Müll.

Sie gefunden ein Verhalten. Es könnte gewesen jeder andere Verhalten, einschließlich einem ungültigen Speicherzugriff.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top