2016-06-11 20 views
-1

Ich arbeite an den Übungen in K & Rs Buch, und ich habe einen seltsamen Bug beim Versuch, 04-06 zu erweitern Erlaube Variablen mit String-Namen. Ehrlich gesagt, habe ich es tatsächlich geschafft, den Fehler zu beheben (ziemlich einfach - unten erklärt), aber ich würde gerne wissen, warum der Fehler überhaupt aufgetreten ist.Das Übergeben einer nicht leeren Zeichenkette an snprintf bewirkt, dass ein nicht verwandtes char * -Array die Adressen ändert.

Für diejenigen, die mit dem Problem nicht vertraut sind, werden Sie grundsätzlich gebeten, einen Befehlszeilenrechner (in polnischer Notation) zu erstellen, der Variablen mit Charakternamen speichern und abrufen kann.

Hier ist der relevante Code, wo das Problem auftritt:

#define MAXOPLEN 1000 

int varCount = 1; 
char **keys; 
char **values; 
// changing the declaration to: 
// char strOps[][STROPCOUNT] = { ... }; 
// fixed the issue 
char *strOps[STROPCOUNT] = { "dupe", "swap", "del", "print", 
          "clr", "sin", "cos", "tan", 
          "exp", "pow", "ln", "log", 
          "mem", "re"}; 

main() { 
    keys = malloc(varCount * sizeof(char[MAXOPLEN])); 
    keys[0] = "ans"; 
    values = malloc(varCount * sizeof(char[MAXOPLEN])); 
    values[0] = "0.0"; 

    ... // Other stuff related to the program 
} 

// flag is unrelated to the problem I'm asking about. It just checks to see 
// if the variable name used to store value n is 'ans', which is where 
// the last returned value is stored automatically 
void memorize(char s[], double n, bool flag) { 
    ... // small conditional block for flag 

    for (i = 0; i < varCount; i++) { 
     if (equals(keys[i], s)) { 
      found = True; 
      // Next line is where the program actually breaks 
      snprintf(values[i], MAXOPLEN, "%f", n); 
      break; 
     } 
    } 

    if (!found) { 
     i = varCount; 
     varCount++; 

     keys = realloc(keys, varCount * sizeof(char*)); 
     keys[i] = malloc(sizeof(char[MAXOPLEN])); 
     keys[i] = s; 

     values = realloc(values, varCount * sizeof(char*)); 
     values[i] = malloc(sizeof(char[MAXOPLEN])); 
     snprintf(values[i], MAXOPLEN, "%f", n); 
    } 
} 

Nach dem Kompilieren und Ausführen, zum ersten Mal in einer Gleichung eingeben zu berechnen, scheint alles glatt zu laufen. Beim Debugging habe ich jedoch festgestellt, dass die ersten drei char * in strOps merkwürdig auf unterschiedliche Adressen verweisen. Wenn Sie versuchen, den Rückgabewert der Gleichung auf "ans" zu speichern, wird die for-Schleife in memorize() eingefügt, die versucht, zu sehen, ob die Zeichenfolge s bereits als Schlüsselname verwendet wurde. Es findet korrekt die Schlüssel [0], um auf eine Zeichenkette zu zeigen, die dem s-Wert ("ans") entspricht, und versucht dann, n in eine Zeichenkette umzuwandeln und sie in Werten [0] zu speichern.

während im Innern des snprintf() -Funktion, die ersten drei char * in strops gemacht wird an anderer Stelle in diesem Verfahren in corecrt_stdio_config.h Punkt:

_Check_return_ _Ret_notnull_ 
__declspec(noinline) __inline unsigned __int64* __CRTDECL __local_stdio_printf_options(void) 
{ 
    // Error occurs after this next line: 
    static unsigned __int64 _OptionsStorage; 
    return &_OptionsStorage; 
} 

Wie oben in dem Code kommentiert, so dass ein 2D strops Ein Array von Zeichen (anstatt eines Arrays von Zeichenzeigern) behob das Problem. Dies ist sinnvoll, da in Zeichenfeldern die Werte einzelner Zeichen nicht geändert werden können. Was ich jedoch nicht verstehe, ist, warum die Methode in corecrt_stdio_config.h die Werte dieser drei Zeiger an erster Stelle ändert.

Danke!

+0

Beginnen Sie mit der Verwendung korrekter Prototypfunktionsdeklaratoren. 'main()' ist veraltet. Und mit einem neueren Buch ist K & R längst überholt. Es lehrt kein modernes C! Und siehe [fragen], stellen Sie ein [mcve] zur Verfügung. Selbst wenn Ihr Code ohne Warnungen kompiliert wurde, ergibt das keinen Sinn. – Olaf

+0

Und prüfen Sie das Ergebnis der Funktionen, wenn sie für die weitere Ausführung relevant sind! 'Realloc' könnte fehlschlagen! – Olaf

Antwort

2

Ihre Initialisierungen sind falsch und verursachen die Änderung:

keys[0] = "ans"; 
values[0] = "0.0"; 

Beide "ans" und "0.0" sind Stringliterale und nicht verwendet werden kann, die Arrays zu initialisieren, müssen Sie strcpy verwenden, nachdem Sie zuordnen.

strcpy (keys, "ans"); 
strcpy (values, "0.0"); 

Die andere Option ist ein Zeichen in einer Zeit zuweisen:

size_t i; 
char *p = "ans"; 
for (i = 0; i < strlen (p); i++) 
    keys[i] = p[i];     /* copy to keys */ 
p[i] = 0;       /* nul-terminate */ 

Anmerkung: dies sind Beispiele für Ihre Fehler, Sie die gleiche Sache im gesamten Code zu tun.

+0

Könnten Sie uns ein wenig erklären, was passiert, wenn Sie versuchen, dies zu tun? keys [0] = "ans"; ? – velocirabbit

+0

'strOps' ist ein Array von Zeigern, initialisiert auf die Liste der Stringliterale. 'keys' ist ein * pointer-to-pointer-to-char *.Wenn Sie 'Schlüssel' zuweisen, erzeugen Sie einen Speicherblock' varCount * sizeof (char [MAXOPLEN]) '(das sind' 1000' Byte, genug für ~ 125 nicht initialisierte Zeiger). Wenn Sie 'keys [0] =" ans ";' zuweisen, weisen Sie die Adresse für "ans" (im schreibgeschützten Speicher) dem ersten Zeiger in 'keys' zu. Wenn Sie dann versuchen, auf 'keys' als * pointer-to-char * zuzugreifen, erscheint die Adresse für' keys' (weil die * Adresse eines Arrays * die * Adresse des ersten Elements * ist). –

+0

Das macht Sinn. Warum änderte später der Wert der Schlüssel [0] die ersten drei Zeiger in strOps? Soweit ich das beurteilen kann, sind sie in keiner Weise verwandt. – velocirabbit