Ein "long" als String ?



  • long l="Hello World !";
    printf("%s\n",l);  // --> Hello World !
    

    Wie ist so etwas überhaupt möglich ?
    Ich dachte immer, ein "long" sei ein Integer Typ und kann nur Ganzzahlen aufnehmen.
    Wieso gehen auch Strings ?

    Ich bin Danke der Intrinsic.h von den XToolkits darauf gestossen.

        XtSetArg(wargs[i], XtNheight, 25); i++;
        XtSetArg(wargs[i], XtNlabel, "Hallo"); i++;
    
    #define XtSetArg(arg, n, d) \
        ((void)( (arg).name = (n), (arg).value = (XtArgVal)(d) ))
    
    typedef long		XtArgVal;
    

    Noch der Link zu "long": https://de.wikipedia.org/wiki/Datentypen_in_C



  • @Mathuas Da das hier das C++ Forum ist, hier eine C++ Antwort:

    Das geht mit dem Datentyp long wie er von der Sprache zur Verfügung gestellt wird, auch nicht.

    Und in C solltest du dringend die Compiler Warnungen beachten.

    Warum kann das funktionieren: "Hello World" ist ein const char* -> Also einen Pointer auf einen Char (den ersten der Zeichenkette), was der Adresse im Speicher entspricht. Einen Pointer kann man natürlich als Zahl speichern. Das passiert bei long l="Hello World !";.
    %s erwartet dann einen const char*, also, wieder die Adresse des ersten Chars der Zeichenkette. Hier kann jetzt leben wieder als Adresse interpretiert werden.



  • Man sollte noch hinzufügen, daß dies nur auf Systemen funktioniert, wo sizeof(long) >= sizeof(char *) (bzw. allgemein >= sizeof(void *)).
    Dies funktioniert also z.B. nicht mit dem MSVC bei 64bit-Kompilierung, da auch dort long nur 4 Bytes (also 32 Bit) groß ist: Built-in types (C++): Sizes of built-in types



  • @Mathuas sagte in Ein "long" als String ?:

    long l="Hello World !";
    printf("%s\n",l);  // --> Hello World !
    

    Wie ist so etwas überhaupt möglich ?
    Ich dachte immer, ein "long" sei ein Integer Typ und kann nur Ganzzahlen aufnehmen.
    Wieso gehen auch Strings ?

    Ich bin Danke der Intrinsic.h von den XToolkits darauf gestossen.

        XtSetArg(wargs[i], XtNheight, 25); i++;
        XtSetArg(wargs[i], XtNlabel, "Hallo"); i++;
    
    #define XtSetArg(arg, n, d) \
        ((void)( (arg).name = (n), (arg).value = (XtArgVal)(d) ))
    
    typedef long		XtArgVal;
    

    Noch der Link zu "long": https://de.wikipedia.org/wiki/Datentypen_in_C

    Du scheinst eine ältere Version der libXt zu nutzen.
    Denn in der neusten version ist ein XtArgVal wie folgt definiert (https://github.com/freedesktop/xorg-libXt/blob/master/include/X11/Intrinsic.h)

    typedef XtIntPtr	XtArgVal;
    

    Und XtIntPtr ist wie folgt definiert:

    #if __STDC_VERSION__ >= 199901L
    #include <stdint.h>
    typedef intptr_t	XtIntPtr;
    typedef uintptr_t	XtUIntPtr;
    #else
    typedef long		XtIntPtr;
    typedef unsigned long	XtUIntPtr;
    #endif
    

    das XtArgVal = long gilt nur wenn der compiler __STDC_VERSION__ < 199901 definiert (aka kein C99 oder neuer standard unterstützt bzw. aktiv ist.)



  • @Mathuas sagte in Ein "long" als String ?:

    Ich dachte immer, ein "long" sei ein Integer Typ und kann nur Ganzzahlen aufnehmen.

    chars sind auch nur Ganzzahlen, den den Code für das jeweilige Zeichen speichern. D.h. man kann ein long auch als Abfolge von chars im Speicher interpretieren, was hier getan wird. Das ganze ist natürlich alles andere als sauber und sollte so tunlichst nicht gemacht werden.


  • Mod

    @john-0 sagte in Ein "long" als String ?:

    @Mathuas sagte in Ein "long" als String ?:

    Ich dachte immer, ein "long" sei ein Integer Typ und kann nur Ganzzahlen aufnehmen.

    chars sind auch nur Ganzzahlen, den den Code für das jeweilige Zeichen speichern. D.h. man kann ein long auch als Abfolge von chars im Speicher interpretieren, was hier getan wird. Das ganze ist natürlich alles andere als sauber und sollte so tunlichst nicht gemacht werden.

    Das ist nicht, was hier passiert. Was hier passiert, wurde in der ersten Antwort schon korrekt erklärt. Was du beschreibst wäre so etwas wie hier:

    #include <stdio.h>
    
    int main(void) {
    	const char* str = "Hello World!";
    	long int value = *(long int*) str;
    	printf("%ld %.*s", value, sizeof(value), &value);
    }
    

    Was ein anderes Ergebnis hat (je nach sizeof(value)) und ganz gewiss niemand so programmieren wird, weil man solche Casts weder versehentlich noch absichtlich macht.



  • Danke für die Infos, ich dachte doch fast, mit dem 32Bit Integer sei etwas faul.

    @firefly sagte in Ein "long" als String ?:

    Du scheinst eine ältere Version der libXt zu nutzen.
    Denn in der neusten version ist ein XtArgVal wie folgt definiert (https://github.com/freedesktop/xorg-libXt/blob/master/include/X11/Intrinsic.h)

    Anscheinend wird bei Linux Mint eine veraltet lib der XToolkits mit geliefert. Ich vermute bei Debian und Ubuntu wird dies nicht anders sein.
    Dies verwundert mich, das dies ein grösserer Bug ist. Ein 32Bit Pointer auf einem 64Bit OS.
    Einziger Grund könnte sein, das kaum einer mehr die XToolkit braucht.

    Ich habe jetzt die XToolkits von github genommen und da steht jetzt auch intptr_t.


  • Mod

    @Mathuas sagte in Ein "long" als String ?:

    Dies verwundert mich, das dies ein grösserer Bug ist. Ein 32Bit Pointer auf einem 64Bit OS.

    Ich sehe da nix mit 32 Bit. long ist bei üblichen Linuxcompilern 64 Bit, deswegen klappt das hier ja auch.



  • @SeppJ sagte in Ein "long" als String ?:

    Das ist nicht, was hier passiert. Was hier passiert, wurde in der ersten Antwort schon korrekt erklärt. Was du beschreibst wäre so etwas wie hier:

    Mir war eher folgendes Beispiel im Kopf

    #include <stdio.h>
    
    int main(void) {
        long l = 'H' + 'e' * 256 + 'l' * 256 * 256 + 'l' * 256 * 256 * 256 + (long)'o' * 256 * 256 * 256 * 256 + (long)'!' * 256 * 256 * 256 * 256* 256;
    
        printf("%s\n", &l);
    }
    

    Früher haben das C Compiler oftmals ohne jede Warnung geschluckt.


  • Mod

    Das ist gar nicht mal sooo lange her, dass Compiler da überhaupt warnen. Vermutlich weniger als 10 Jahre. Braucht man schließlich tiefergehende Inspektion des Formatstringinhalts für, anstatt nur eine Analyse der syntaktischen Elemente. Es ist ja prinzipiell auch erst einmal korrektes C; das ist ein völlig legitimer Funktionsaufruf, der nur mit Wissen über die spezielle Semantik von printf als UD erkennbar ist.



  • Ich sehe da nix mit 32 Bit. long ist bei üblichen Linuxcompilern 64 Bit, deswegen klappt das hier ja auch.

    printf("SizeOf: %i\n",sizeof(long));
    

    Stimmt, es kommt 8 raus.



  • @SeppJ sagte in Ein "long" als String ?:

    @Mathuas sagte in Ein "long" als String ?:

    Dies verwundert mich, das dies ein grösserer Bug ist. Ein 32Bit Pointer auf einem 64Bit OS.

    Ich sehe da nix mit 32 Bit. long ist bei üblichen Linuxcompilern 64 Bit, deswegen klappt das hier ja auch.

    Auf Linux x86 ist sizeof(long) == sizeof(void*) .
    Vom Compiler unabhängig, da Teil der ABI.

    Mit MSVC ist sizeof(long) == 4.
    Ob das Teil der ABI ist, da kann man sich streiten, weil die Windows-Headers quasi überall typedefs verwenden. Also sizeof(LONG) == 4 muss gelten, aber die Frage ist ob LONG und long equivalent sein müssen.



  • @hustbaer sagte in Ein "long" als String ?:

    Auf Linux x86 ist sizeof(long) == sizeof(void*) .
    Vom Compiler unabhängig, da Teil der ABI.

    So wie es scheint, hat C das gleiche Problem wie Pascal, die Integer sind nicht einheitlich.
    Wie es bei Java, Python und co. weis ich nicht.


  • Mod

    Es ist schon einheitlich, aber in einem anderen Sinne als eine feste Bitbreite: Als der passendste Typ für die Maschine. Das ist kein Nachteil, sondern ein Vorteil auf einem anderen Gebiet als du dir wünscht. Wenn du feste Bitbreiten willst, musst du eben einen der dafür vorgesehenen Typdefs benutzen. Die halt nicht unbedingt existieren, sondern nur, wenn es die auf der Maschine tatsächlich gibt. Womit man auch einen Teil des Vorteils erkennt: Ein typisches C-Programm, das nicht absichtlich die Typen mit fester Breite benutzt, läuft ohne Änderung auf Systemen mit 24-Bit Bytes. Ja, das gibt es. Ist halt relevant für Sprachen, die auch zur Programmierung von Supercomputern oder Microcontrollern gedacht ist.

    PS: Weil mich dafür Beispiele interessierten (sind aber zu viele für eine Liste), hier ein besonders interessanter Fall: Die DEC-10 hatte sogar eine Variable Bytebreite von 1-36. Der GCC hat angeblich Unterstützung für diese Maschine. Aber meine Energie reicht gerade nicht für eine Recherche, was dort als CHAR_BITS oder sizeof(int) definiert ist (Der Link zum ABI ist leider tot).



  • @Mathuas sagte in Ein "long" als String ?:

    Wie es bei Java, Python und co. weis ich nicht.

    Bei Python hat int keine Beschränkung (ist also ein bigint).

    $ echo 'print(f"{2 ** 100 = }")' | python
    2 ** 100 = 1267650600228229401496703205376
    

    Aber wieder natürlich mit interessanten Features wie dass kleine gleiche Zahlen auf dasselbe Objekt zeigen können, große gleiche aber nicht.

    >>> j1=10234
    >>> j2=10234
    >>> j1 is j2
    False
    >>> i1=5
    >>> i2=5
    >>> i1 is i2
    True
    


  • @SeppJ sagte in Ein "long" als String ?:

    Es ist schon einheitlich, aber in einem anderen Sinne als eine feste Bitbreite: Als der passendste Typ für die Maschine.

    Für Java stimmt dies nicht. Dort umfasst ein

    • int immer 4 Byte,
    • long immer 8 Byte,
    • char immer 2 Byte, (vorzeichenlos)
    • short immer 2 Byte,
    • byte immer 1 Byte,
    • float immer 4 Byte,
    • und double immer 8 Byte.

    Und wie das intern technisch realisiert worden ist, das ist ein Implementierungsdetail der JVM und für den normalen Programmierer im Normalfall völlig irrelevant. Dieser muss nur den gültigen Wertebereich kennen.

    Java ist (sehr) streng typisiert.



  • Oder ... um ganz genau zu sein: statisch, explizit und implizit, stark typisiert; siehe auch hier: https://de.wikipedia.org/wiki/Typisierung_(Informatik)#Beispiele



  • @Fragender sagte in Ein "long" als String ?:

    @SeppJ sagte in Ein "long" als String ?:

    Es ist schon einheitlich, aber in einem anderen Sinne als eine feste Bitbreite: Als der passendste Typ für die Maschine.

    Für Java stimmt dies nicht. Dort umfasst ein

    • int immer 4 Byte,
    • long immer 8 Byte,
    • char immer 2 Byte, (vorzeichenlos)
    • short immer 2 Byte,
    • byte immer 1 Byte,
    • float immer 4 Byte,
    • und double immer 8 Byte.

    Und wie das intern technisch realisiert worden ist, das ist ein Implementierungsdetail der JVM und für den normalen Programmierer im Normalfall völlig irrelevant. Dieser muss nur den gültigen Wertebereich kennen.

    Java ist (sehr) streng typisiert.

    Ausser man verwendet einen Vertexpuffer von OpenGL, dann ist die Datenbreite auf einmal sehr wichtig.



  • @Mathuas sagte in Ein "long" als String ?:

    Ausser man verwendet einen Vertexpuffer von OpenGL, dann ist die Datenbreite auf einmal sehr wichtig.

    Nein, ist es nicht. OpenGL gehört nicht zu Java.



  • @hustbaer sagte in Ein "long" als String ?:

    Auf Linux x86 ist sizeof(long) == sizeof(void*) .
    Vom Compiler unabhängig, da Teil der ABI.

    Das ist nicht nur auf Linux x86 der Fall, sondern auf jedem OS, dass sich an die Single UNIX Specification hält. Diese definiert nämlich für den 32Bit Modus ILP32 und für den 64Bit Modus LP64. Es gab zwar Abweichungen von der SUS (klassisches UNICOS auf Cray Hardware nutzte SILP64), aber diese sind mittlerweile nur noch als historisch zu bezeichnen, da diese OS keinerlei Rolle mehr spielen.


Anmelden zum Antworten