2016-09-20 4 views
2

Ich habe über Integer-Zeiger-Subtraktion in C in diesem Thread gelesen: Pointer subtraction confusion, die einfach genug zu erfassen und zu testen war.Char Zeiger Subtraktion in C

Allerdings habe ich versucht, ein ähnliches Szenario mit einem char * zu replizieren, aber die Ergebnisse, die ich bekomme, machten keinen Sinn.

Hier ist das Szenario, das ich versucht:

#include <stdio.h> 
#include <string.h> 

int main() { 

    char a_arr[16] = ""; 
    char *a = a_arr; 
    char b_arr[1] = ""; 
    char *b = b_arr; 

    printf("\nThe amount by which they differ is: %d\n", a-b); 
    // a-b = 1, which makes sense since they are 1 char away 

    return 0; 
} 

Das nächste, was ich versuchte, ist das, was ich Probleme habe

Verständnis
#include <stdio.h> 
#include <string.h> 

int main() { 

    char a_arr[16] = ""; 
    char *a = a_arr; 
    char b_arr[2] = ""; 
    char *b = b_arr; 

    printf("\nThe amount by which they differ is: %d\n", a-b); 
    // a-b = 16, which doesn't really make sense to me..  

    return 0; 
} 

Meine Vermutung ist, dass es geht einige padding Zeug vom Compiler-Ende, das ich dachte, sollte nicht der Fall sein, da es ein Char-Array ist und keine Ausrichtung erforderlich wäre.

Ich bin mir nicht sicher, warum es 16 Bytes ist .. Irgendwelche Hilfe ich s sehr geschätzt!

Ich habe die folgende Online-Schnittstelle zu kompilieren und dieses Stück Code ausführen: http://www.tutorialspoint.com/compile_c_online.php

+3

Sie rufen undefiniertes Verhalten auf. Beide Zeiger müssen auf das gleiche Array zeigen! (oder genau danach) Was erwarten Sie von zwei völlig unabhängigen Zeigern zu subtrahieren? Was ist "1 Liter minus 2 Sekunden"? – Olaf

+0

@Olaf isn; t das gleiche Ergebnis wie "1 kg minus 2 Meilen"? –

+0

@SouravGhosh mehr wie "1000 Ampere minus 10 Volt" – Olaf

Antwort

2

In diesem Beispiel a_arr, a, b_arr und b sind wahrscheinlich alle auf dem Stack zugeordnet. Der Compiler muss keine besonderen Garantien für die Anordnung der Variablen auf dem Stack geben. So könnte der Compiler auf Vielfache von 16 Bytes aufgefüllt werden, oder andere Daten zwischen a und b einführen, oder Registerwerte zwischen a und b speichern, oder ....

Aus diesem Grund garantiert die Spezifikation nicht die Ergebnisse der Subtraktion von Zeigern, die zu zwei verschiedenen Arrays gehören, wie die Kommentatoren festgestellt haben. Die gute Nachricht ist, dass Sie dies normalerweise nicht tun müssen, es sei denn, Sie schreiben ein Betriebssystem oder eine Standardbibliothek :).

Bearbeiten Auch die Anordnung von Speicher und was in einem Register vs. auf dem Stapel gehalten wird, kann sich je nach Optimierungsgrad ändern. Ich denke nicht, dass das hier ein Faktor ist, aber es ist etwas, das man im Kopf behalten sollte.

+0

Die "Spezifikationen" sind der C-Standard, der solche Subtraktionen eindeutig verbietet (so dass UB der C-Weg ist zu sagen "Du verlässt den Standard"). IOW, nasale Dämonen können erscheinen, dein Haus ist weggefallen oder dein Computer schreit dich an. – Olaf

1

Ihr Compiler scheint b zuerst zu speichern, dann a im Speicher in Ihrem ersten Beispiel und a zuerst in der Sekunde. Als ich sie laufen erhalte ich:

The amount by which they differ is: 1 

und

The amount by which they differ is: 2 

so ist mein Compiler immer Speicherung b bei einer niedrigeren Adresse als a.

Welche Speicher sieht wahrscheinlich wie für Sie:

First Example: 
____________________ 
|B|  A  | 
-------------------- 

Second Example: 
______________________ 
|  A  |B | 
---------------------- 

Da die Kommentatoren darauf hingewiesen, gibt es keine Garantie von wo die Arrays befinden. Das Subtrahieren von Zeigern in zwei verschiedenen Arrays ist ein nicht definiertes Verhalten.

0

Wenn Sie Ihren Test richtig erstellen, werden Sie feststellen, dass char Zeiger Subtraktion verhält sich genauso wie int Zeiger Subtraktion. I.e.Der zurückgegebene Wert ist die Anzahl der char s zwischen den beiden Zeigern und nicht die Anzahl der Speicheradressen (die Bytes sein können oder nicht) zwischen ihnen.

#include <stdio.h> 
#include <string.h> 

int main() 
{ 
    char a_arr[16] = ""; 
    char *a = a_arr; 
// char b_arr[1] = ""; 
    char *b = &a_arr[8]; 

    printf("\nThe amount by which they differ is: %d\n", b-a); 
    // b-a = 8, which makes sense since they are 8 chars away. 

    printf("\nThe amount by which their addresses differ is: %d\n", (int)b-(int)a); 
    // Which will depend on the implementation and may be something unexpected! 

    return 0; 
} 

Der Mikrocontroller Ich verwende hat 16-Bit-Datenbus und Register und in der Standardeinstellung speichert die Zeichen für Strings an abwechselnden (auch) Adressen. In diesem Fall wäre der erste Ausgang 8 und der zweite 16. Es gibt Compiler-Optionen, um die Zeichen von Strings in zusammenhängenden Speicherplätzen zu speichern, aber dies ist langsamer für den Zugriff, da es die 16-Bit-Datenregister verschiebt, um die ungeraden adressierten Bytes zu erhalten.

+0

Ja, der von Ihnen erwähnte Fall funktioniert. Aus diesem Grund habe ich einen ähnlichen Fall mit 2 verschiedenen Arrays getestet, aber ich wusste nicht, dass es ein undefiniertes Verhalten war. –

1

Ich schrieb Ihr Programm in etwas, das nur Speicher ablädt. Dies sollte Ihnen eine bessere Vorstellung davon geben, was in welchem ​​Speicher abgelegt wurde.

Wie andere darauf hingewiesen haben, bietet der Compiler keine Garantie für das Speicherlayout. Auch Überprüfung einer Speicheradresse kann ändern, wie der Compiler seinen Speicher organisiert. Ihre Frage ist nicht so sehr C, sondern die Eigenarten Ihres Compilers.

#include <stdio.h> 
#include <string.h> 

int main() 
{ 
    char a_arr[16] = ""; 
    char *a = a_arr; 
    char b_arr[1] = ""; 
    char *b = b_arr; 

    void *min, *max, *curr; 


    min = &a_arr; 
    if (min > (void *)&a) { 
     min = &a; 
    } 
    if (min > (void *)&b_arr) { 
     min = &b_arr; 
    } 
    if (min > (void *)&b) { 
     min = &b; 
    } 

    max = (void *)&a_arr + sizeof(a_arr); 
    if (max < (void *)&a + sizeof(a)) { 
     max = (void *)&a + sizeof(a); 
    } 
    if (max < (void *)&b_arr + sizeof(b_arr)) { 
     max = (void *)&b_arr + sizeof(b_arr); 
    } 
    if (max < (void *)&b + sizeof(b)) { 
     max = (void *)&b + sizeof(b); 
    } 

    // Now print them. 
    for (curr = min; curr <= max; ++curr) { 
     if (curr == &a_arr) 
      printf ("%10p: %10x - a_arr\n", curr, *((char *)curr)); 
     else if (curr == &a) 
      printf ("%10p: %10x - a\n", curr, *((char *)curr)); 
     else if (curr == &b_arr) 
      printf ("%10p: %10x - b_arr\n", curr, *((char *)curr)); 
     else if (curr == &b) 
      printf ("%10p: %10x - b\n", curr, *((char *)curr)); 
     else 
      printf ("%10p: %10x\n", curr, *((char *)curr)); 
    } 

    printf ("\nThe amount by which they differ is: %d\n", a-b); 

    return 0; 
} 

Und hier ist, wie es auf meiner Maschine läuft. Notieren Sie die drei verschwendeten Bytes nach b_arr. Diese Bytes werden verwendet, um jede Variable an einer Adresse beginnen zu lassen, die ein Vielfaches von 4 ist (dies ist bekannt als Wortgrenzenausrichtung und ist ziemlich Standard).

Ich vermute, dass Ihr Compiler b_arr auf einer 16-Byte-Grenze ausrichtet. Das ist ungewöhnlich, aber nicht überraschend. Compiler machen für Geschwindigkeit am seltsamsten.

Here ist eine andere Frage, die die unvorhersehbare Art der Speicherausrichtung schön illustriert. Im Allgemeinen sollten Sie das Speicherlayout nicht als deterministisch behandeln.

ffbfefbc: ffffffff - b 
    ffbfefbd: ffffffbf 
    ffbfefbe: ffffffef 
    ffbfefbf: ffffffc0 
    ffbfefc0:   0 - b_arr 
    ffbfefc1:   0 
    ffbfefc2:   0 
    ffbfefc3:   0 
    ffbfefc4: ffffffff - a 
    ffbfefc5: ffffffbf 
    ffbfefc6: ffffffef 
    ffbfefc7: ffffffc8 
    ffbfefc8:   0 - a_arr 
    ffbfefc9:   0 
    ffbfefca:   0 
    ffbfefcb:   0 
    ffbfefcc:   0 
    ffbfefcd:   0 
    ffbfefce:   0 
    ffbfefcf:   0 
    ffbfefd0:   0 
    ffbfefd1:   0 
    ffbfefd2:   0 
    ffbfefd3:   0 
    ffbfefd4:   0 
    ffbfefd5:   0 
    ffbfefd6:   0 
    ffbfefd7:   0 
    ffbfefd8:   0 

The amount by which they differ is: 8