Ich hatte ein Interview und bekam diesen Code und fragte, was die Ausgabe für jede dieser printf-Anweisungen ist.

Ich habe meine Antworten als Kommentare, bin mir aber im Rest nicht sicher.

Kann jemand die verschiedenen Ausgaben für die Anweisungen 1, 3 und 7 erklären und warum?

Vielen Dank!

#include <stdio.h>

int main(int argc, const char * argv[]) {

    char *s = "12345";

    printf("%d\n", s);      // 1.Outputs "3999" is this the address of the first pointer?
    printf("%d\n", *s);     // 2.The decimal value of the first character
    printf("%c\n", s);      // 3.Outputs "\237" What is this value?
    printf("%c\n", *s);     // 4.Outputs "1"
    printf("%c\n", *(s+1)); // 5.Outputs "2"
    printf("%s\n", s);      // 6.Outputs "12345"
    printf("%s\n", *s);     // 7.I get an error, why?
    return 0;
}
1
Rami 17 Apr. 2018 im 20:10

4 Antworten

Beste Antwort

Ich habe den folgenden Online-C-Compiler verwendet, um Ihren Code auszuführen: und hier sind die Ergebnisse:

1. 4195988            - undefined behaviour (UB), manifesting here as the address
                        of the char array as you stated (for a 64 bit address you might or
                        might not get truncation)
2. 49                 - ASCII value of '1'
3. �                  - undefined behaviour, manifesting here as unsupported ASCII value
                        for a truncation of the address of the array of chars
                        (placing 32-bit address into a char - assuming a 32-bit system)
4. 1                  - obvious
5. 2                  - obvious
6. 12345              - obvious
7. Segmentation fault - undefined behaviour, trying to place the first char
                        of a char array into a string reserved position
                        (placing char into a string)

Anmerkung zu Punkt 3: Wir können ableiten, was zur Laufzeit stattgefunden hat. In dem spezifischen Beispiel in der Frage -

printf("%c\n", s);      // 3.Outputs "\237". What is this value?

Dies ist ein Hardware- / Compiler- / Betriebssystemverhalten beim Umgang mit der UB.

Warum? Aufgrund der Ausgabe "\ 237" -> bedeutet dies ein Abschneiden unter dem spezifischen Hardwaresystem, das diesen Code ausführt !

Bitte beachten Sie die folgende Erklärung (Annahme - 32-Bit-System):

char *s = "12345"; // Declaring a char pointer pointing to a char array
char c = s; // Placement of the pointer into a char - our UB
printf("Pointer to character array: %08x\n", s); // Get the raw bytes
printf("Pointer to character: %08x\n", c); // Get the raw bytes
printf("%c\n", s); // place the pointer as a character
                   // display is dependent on the ASCII value and the OS
                   // definitions for 128-255 ASCII values

Die Ausgänge:

Pointer to character array: 004006e4  // Classic 32-bit pointer
Pointer to character: ffffffe4        // Truncation to a signed char
                                      // (Note signed MSB padding to 32 bit display)
�                                     // ASCII value E4 = 228 is not displayed properly

Der letzte Befehl printf entspricht char c = s; printf("%c\n", c);. Warum? Dank Kürzung.

Ein zusätzliches Beispiel mit einer legitimen ASCII-Zeichenausgabe:

    char *fixedPointer = 0xABCD61; // Declaring a char pointer pointing to a dummy address
    char c = fixedPointer; // Placement of the pointer into a char - our UB
    printf("Pointer to 32-bit address: %08x\n", fixedPointer); // Get the raw bytes
    printf("Pointer to character: %08x\n", c); // Get the raw bytes
    printf("%c\n", fixedPointer);

Und die tatsächlichen Ergebnisse:

Pointer to 32-bit address: 00abcd61
Pointer to character: 00000061
a
0
Peter Mortensen 28 Apr. 2018 im 12:40

Dieser Anruf

printf("%d\n", s); 

Hat ein undefiniertes Verhalten, da ein ungültiger Formatbezeichner mit einem Zeiger verwendet wird.

Dieser Anruf

printf("%d\n", *s);

Gibt den internen Code (zum Beispiel ASCII-Code) des Zeichens '1' aus.

Dieser Anruf

printf("%c\n", s);

Hat ein undefiniertes Verhalten aufgrund der Verwendung eines ungültigen Formatbezeichners mit einem Zeiger.

Diese Anrufe

printf("%c\n", *s);
printf("%c\n", *(s+1));

Sind gültig. Der erste gibt das Zeichen '1' und der zweite das Zeichen '2' aus.

Dieser Anruf

printf("%s\n", s);

Ist korrekt und gibt die Zeichenfolge "12345" aus.

Dieser Anruf

printf("%s\n", *s);

Ist ungültig, da für ein Objekt vom Typ char ein ungültiger Formatbezeichner verwendet wird.

5
Rann Lifshitz 30 Mai 2018 im 07:42
  1. Dieser Code ist undefiniertes Verhalten (UB). Sie übergeben einen Zeiger, für den die Funktion einen int -Wert erfordert. In einer 64-Bit-Architektur ist ein Zeiger beispielsweise 64 Bit und ein int 32 Bit. Sie können einen abgeschnittenen Wert drucken.

  2. Sie übergeben den ersten char -Wert (vom Compiler automatisch in einen int konvertiert) und drucken ihn dezimal aus. Wahrscheinlich haben Sie 49 (den ASCII-Code für '1'). Dies ist eine legale Verwendung, aber seien Sie vorsichtig mit Überraschungen, da Sie negative Werte erhalten können, wenn Ihre Plattformimplementierung char {{X5} ist. }.

  3. Sie drucken den übergebenen Zeiger, der als char -Wert neu interpretiert wurde. Undefiniertes Verhalten, da Sie einen Zeiger nicht in einen char -Wert konvertieren können.

  4. Sie drucken den spitzen Wert von s als char, damit Sie das erste Zeichen der Zeichenfolge "12345" ('1') erhalten.

  5. Sie drucken das vorletzte char, auf das s zeigt, sodass Sie das zweite Zeichen der Zeichenfolge ('2') erhalten.

  6. Sie drucken die Zeichenfolge, auf die s zeigt, sodass Sie die gesamte Zeichenfolge erhalten. Dies ist legal und in der Tat die übliche Art, einen String zu drucken.

  7. Sie übergeben das erste Zeichen einer Zeichenfolge, die als Zeiger auf eine nullterminierte Zeichenfolge interpretiert werden soll (was nicht der Fall ist). Dies ist wieder undefiniertes Verhalten. Sie interpretieren einen char -Wert als Zeiger auf eine nullterminierte Zeichenfolge neu. Ein SIGSEGV ist in diesem Fall üblich (aber nicht garantiert :)). Das Signal wird gesendet, wenn das Programm versucht, auf nicht zugewiesenen Speicher zuzugreifen, bevor es das vermeintliche Nullzeichen erreicht, das die Zeichenfolge beendet (es könnte jedoch ein {{X2 finden) }} im Weg und nur Müll drucken).

1
Peter Mortensen 28 Apr. 2018 im 12:44

Die 7. Zeile schlägt fehl, da eine Zeichenfolge im C-Stil als Eingabe erwartet wird und Sie stattdessen ein Zeichen platzieren.

Schauen Sie sich an:

1
Rann Lifshitz 30 Mai 2018 im 07:43