Ich habe hier mal ein kleines C Rätsel:
1 | char* str = "abc"; |
2 | str[2] = 'd'; |
3 | printf("%s", str); |
Was kommt dabei raus?
|
Forum: Offtopic Was ist die Ausgabe?Ich habe hier mal ein kleines C Rätsel:
Was kommt dabei raus? :
Bearbeitet durch User
Vermutlich nen Segmentation Fault :) Probiers mal so:
Vielleicht möchte dies ein weiterer User mal erklären warum dies so ist. Beste Grüße Das ist schon mal richtig und mit dem Array geht es auch. Nun ist es Aufgabe des Lesers, hersuszufinden, wieso. Und falls ihr noch weitere solche Tricks habt, nur raus damit. :
Bearbeitet durch User
Til Hoff schrieb: > Und falls ihr noch weitere solche Tricks habt, nur raus damit. Trick? Du beliebst zu scherzen. Lies ein C-Buch. Da steht das alles drinnen. Auch warum du deine erste Version nicht machen darfst. <Kopfschüttel> heutzutage ist es also schon ein Trick, wenn man sein Handwerk gelernt hat. Was kommt als nächstes? Bauarbeiter die sich in 2 Abendschulungen zum Chirurgen umschulen lassen? Dolmetscher mit Sprachkenntnissen, die sie gerade mal dazu befähigen bei McDonalds etwas zu bestellen? ... :
Bearbeitet durch User
Til Hoff schrieb: > Hättest du denn einen "wirklichen" Trick? Sicher. Google mal nach "Duffs Device" Til Hoff schrieb: > Hättest du denn einen "wirklichen" Trick? Klar, Til. War hier zwar schon im Forum, kennst Du aber vielleicht noch nicht:
Was ist hiernach der Wert in u32? LG, Sebastian Til Hoff schrieb: > Ich habe hier mal ein kleines C Rätsel: > >
> > Was kommt dabei raus? 1) Es ist kein gültiges C-Programm. Z.B. Definiert die erste Zeile str als char*, zdie zweite Zeile definiert str als int[]. Die zweite Zeile ist keine Zuweisung, da außerhalb einer Funktion. Die dritte Zeile fängt an wie eine Funktionsdefnition / -deklaration mit implizitem Rückgabewert int. 2) Selbst wenn man es syntaktisch zu einem C-Programm macht, ist die 1. Zeile laut C-Standard undefiniert. Früher kannte GCC mal -fwritable-strings o.ä., aber das gibt es schon lange nicht mehr. ...und hier die Ausgabe mit GCC:
> 1) Es ist kein gültiges C-Programm. Z.B. Definiert die erste Zeile > str als char*, zdie zweite Zeile definiert str als int[]. Die > zweite Zeile ist keine Zuweisung, da außerhalb einer Funktion. > Die dritte Zeile fängt an wie eine Funktionsdefnition / -deklaration > mit implizitem Rückgabewert int. Ist das nötig? nIch denke mal jeder hat verstanden, dass man sich int main() etc denken soll. Entsprechende Includes wie die Standardausgabe natürlich auch. Zu der Frage mit dem leftshift: und folglich: :
Bearbeitet durch User
> Sicher. Google mal nach "Duffs Device"
Das ist cool. Weißt du denn wie es mit der Optimierung bei CC steht?
Laut der Wiki ist ein Performanceboost bei guten Compilern wohl nicht zu
erwarten (speziell interessiert mich arm none eabi, wenn du den kennst).
Til Hoff schrieb: > Zu der Frage mit dem leftshift: > > > > und folglich: > > Falsch. Es kommt 0 raus. Ein char (uint8_t) wird um 8 Stellen nach links geschoben, danach ist es leer. Der Typecast nach unsigned lonk (uint32_t) kommt erst mit der Zuweisung, zu dem Zeitpunkt ist aber das leftshift schon durchgeführt worden. Auch falsch. Das Ergebnis hängt ab von der Größe von int. * 32-Bit int: es kommt 0x8800 raus. * 16-Bit int und 8-Bit char: es kommt 0xffff8800 raus. * 16-Bit int und 16-Bit char: es kommt 0x8800 raus. Johann L. schrieb: > * 32-Bit int: es kommt 0x8800 raus. > * 16-Bit int und 8-Bit char: es kommt 0xffff8800 raus. > * 16-Bit int und 16-Bit char: es kommt 0x8800 raus. Immer noch nicht ganz richtig: * 16-Bit int und 8-Bit char: undefined Frank Bär schrieb: > Til Hoff schrieb: >> Zu der Frage mit dem leftshift: >> >>> >> und folglich: >> >> > Falsch. Es kommt 0 raus. > > Ein char (uint8_t) wird um 8 Stellen nach links geschoben, danach ist es > leer. char != uint8_t > Der Typecast nach unsigned lonk (uint32_t) kommt erst mit der Zuweisung, > zu dem Zeitpunkt ist aber das leftshift schon durchgeführt worden. unsigned long ist nur bedingt gleich uint32_t Nun ja, die Online IDE, die ich getestet habe, hat 0x8800 bzw die 356... In Dezimalschreibweise ausgegeben. --> https://ideone.com/SZbd1t :
Bearbeitet durch User
Simon K. schrieb: > Frank Bär schrieb: >> Til Hoff schrieb: >>> Zu der Frage mit dem leftshift: >>> >>>> >>> und folglich: >>> >>> >> Falsch. Es kommt 0 raus. >> >> Ein char (uint8_t) wird um 8 Stellen nach links geschoben, danach ist es >> leer. > char != uint8_t Ahja? Was denn sonst? Ich lasse mich gern eines besseren belehren. Aber nur mal so nebenher: Dieser ganze Mist von wegen 32bit-dies und 16bit-jenes ist der Grund, warum man diese Datentypen nicht benutzen sollte. int, char, long sind nicht sauber definiert. Vernünftige und vor allem Compiler-unabhängige Datentypen gibts nur aus der stdint.h. Alles andere ist ein Ratespiel und damit unsauber. 16Bit-Compiler sehen 32 Bit als long, 32bit-Compiler finden das normal und arbeiten bei long mit 64 Bit. Was float und double angeht, wird es richtig finster, da geht es munter zwischen 16 und 64 Bit hin und her. >> Der Typecast nach unsigned lonk (uint32_t) kommt erst mit der Zuweisung, >> zu dem Zeitpunkt ist aber das leftshift schon durchgeführt worden. > unsigned long ist nur bedingt gleich uint32_t Siehe rant oben. Frank Bär schrieb: > Ahja? Was denn sonst? Ich lasse mich gern eines besseren belehren. uint8_t hat acht Bit. char nicht unbedingt. Schaut mal hier http://home.fhtw-berlin.de/~junghans/cref/CONCEPT/pointers.html
Was nutzt man eigentlich soetwas?
Frank Bär schrieb: > Ahja? Was denn sonst? Ich lasse mich gern eines besseren belehren. Erstmal ist uint8_t explizit unsigned, während char entweder signed oder unsigned sein kann. uint8_t ist also höchstens unsigned char, aber nicht char. Desweiteren gibt es Prozessoren, die gar nicht auf Byteebene addressieren können, dort existiert uint8_t schlichtweg nicht. Char hingegen ist immer die kleinste adressierbare Einheit, bei den C2k DSPs von TI z.B. 16 Bit (was zu tollen Effekten führt, wenn man Code portiert, der sich darauf verlässt, dass ein Char 8 Bit hat). Jürgen F. schrieb: > Was nutzt man eigentlich soetwas? main() > { > int *Width; > *Width = 34; > } Ach komm schon, in jedem noch so schlechten C Tutorial werden Pointer erklärt. :
Bearbeitet durch User
Ja aber der Pointer zeigt doch auf noch gar nichts, oder doch? Also ich meine der Pointer ist nicht initialisiert. :
Bearbeitet durch User
Frank Bär schrieb: > Simon K. schrieb: >> Frank Bär schrieb: >>> >>> Falsch. Es kommt 0 raus. >>> >>> Ein char (uint8_t) wird um 8 Stellen nach links geschoben, danach ist Es wird eben kein char (und auch kein uint8_t) geschoben, sondern ein int. Vor dem Schieben wird der uint8_t zum int promoted. Dann wird geschoben, mit undefiniertem Verhalten falls der zu schiebende Wert dann negativ ist, wie Yalu bemerkte. Erst dann wird das Ergebnis zu einem uint32_t gemacht. > Aber nur mal so nebenher: Dieser ganze Mist von wegen 32bit-dies und > 16bit-jenes ist der Grund, warum man diese Datentypen nicht benutzen > sollte. int, char, long sind nicht sauber definiert. stdint.h hilft aber auch nicht, wenn es um integer-promotion rules geht. Ein int bleibt ein int, und die promotion-Rules sind auch in C99 / C11 die gleichen wie in C89. Richtig. Deshalb nutzt man es ja auch nicht. Uninitialisierte Pointer sind undefiniert. Je nach Zielplattform gibt es eine Fehlermeldung zur Laufzeit (z.B. unter Windows), oder du schreibst irgendwo im Speicher rum (µC), was entweder zu einem korrupten RAM führt (wenn im Pointer zufällig was sinnvolles steht) oder gleich einen Fault auslöst (wenn du auf eine unzulässige Addresse schreiben willst). Natürlich kann der Compiler solche Dinge auch nach belieben wegoptimieren. Es kann aber auch zu sowas führen: http://static3.wikia.nocookie.net/__cb20130416015541/powerlisting/images/9/93/Divded_by_zero_6848.jpg vn nn schrieb: > Erstmal ist uint8_t explizit unsigned, während char entweder signed oder > unsigned sein kann. uint8_t ist also höchstens unsigned char, aber nicht > char. Desweiteren gibt es Prozessoren, die gar nicht auf Byteebene > addressieren können, dort existiert uint8_t schlichtweg nicht. Char > hingegen ist immer die kleinste adressierbare Einheit, bei den C2k DSPs > von TI z.B. 16 Bit (was zu tollen Effekten führt, wenn man Code > portiert, der sich darauf verlässt, dass ein Char 8 Bit hat). Der nächste Grund, warum man char meiden sollte. Spätestens, wenn man irgendwelche Ergebnisse extern speichern will, geht der Arsch auf Grundeis. Da ist man nunmal auf die Bitrepräsentation angewiesen, und kann nicht dem Compiler die Auswahl überlassen, was er für richtig hält. Und bezogen auf die 16bit-Adressierung - es gibt mit an Sicherheit grenzender Wahrscheinlichkeit eine Möglichkeit für den Compiler (sicherlich ineffizient), trotzdem eine 8Bit-Adressierung vorzunehmen. Aber entscheidend ist an der Stelle doch, dass man sich eben NICHT davon abhängig macht, wie der Compiler bzw. der Prozessor gerade gelaunt oder gestrickt sind. uint8_t ist uint8_t, die Bitrepräsentation ist über alle Plattformen und Compiler gleich. Eine Funktion, die solche Datentypen verwendet, ist mit kleinsten bzw. gänzlich ohne Anpassung portierbar. Ich hatte mal die Freude, ein 15k-Zeilen-Programm von einem 16bit- auf einen 32-Bit-Prozessor zu portieren. Dabei auch noch mit Compilerwechsel. Die erste Maßnahme war die Eliminierung uneindeutiger Datentypen. Solange man die Daten nur intern weiterverwendet, mag das noch gehen, aber sobald man eine beliebige Kommunikationsschnittstelle bedienen möchte, sind solche Datentypen kontraproduktiv. Johann L. schrieb: >> Aber nur mal so nebenher: Dieser ganze Mist von wegen 32bit-dies und >> 16bit-jenes ist der Grund, warum man diese Datentypen nicht benutzen >> sollte. int, char, long sind nicht sauber definiert. > > stdint.h hilft aber auch nicht, wenn es um integer-promotion rules geht. > Ein int bleibt ein int, und die promotion-Rules sind auch in C99 / C11 > die gleichen wie in C89. Meine Einlassung bzgl. stdint.h bezog sich weniger auf Compiler-Arithmetik an sich, als auf die Unzahl von Möglichkeiten, die sich durch die Uneindeutigkeit von sog. Standard-Datentypen (die heutzutage auch noch als verwendenswert gelehrt werden) ergeben. Aber danke, das wusste ich tatsächlich nicht. Die Definition "smaller than int" ist nun eigentlich eine noch größere Dummheit, aber das scheint wohl der Lauf der Dinge. Ein C99-konformer 8Bit-Compiler würde also entweder int als 8Bit-Datentyp interpretieren oder, noch schlimmer, jede Rechnung in 16 Bit durchführen. Gut, dass es das nicht gibt. Frank Bär schrieb: > Und bezogen auf die 16bit-Adressierung - es gibt mit an Sicherheit > grenzender Wahrscheinlichkeit eine Möglichkeit für den Compiler > (sicherlich ineffizient), trotzdem eine 8Bit-Adressierung vorzunehmen. Der vollständigkeit halber: nein. Jürgen F. schrieb im Beitrag #3523964: > Schön dass auf einer Hochschulseite solche Beispiele gezeigt werden, ist > bestimmt förderlich um Verwirrung zu vermeiden. Dort steht auch sonst jede Menge Mist. vn nn schrieb: > Frank Bär schrieb: >> Und bezogen auf die 16bit-Adressierung - es gibt mit an Sicherheit >> grenzender Wahrscheinlichkeit eine Möglichkeit für den Compiler >> (sicherlich ineffizient), trotzdem eine 8Bit-Adressierung vorzunehmen. > > Der vollständigkeit halber: nein. Der Vollständigkeit halber: doch. 2 Minuten danach suchen spuckt __byte() aus. Hier ist ein Beispiel dazu: http://processors.wiki.ti.com/index.php/Byte_Accesses_with_the_C28x_CPU#Using_the_byte_Intrinsic Nicht schön, aber damit funktioniert dann auch 8Bit-Arithmetik. Würde mich auch schwer gewundert haben... Johann L. schrieb: > Es wird eben kein char (und auch kein uint8_t) geschoben, sondern ein > int. Vor dem Schieben wird der uint8_t zum int promoted. Dann wird > geschoben, mit undefiniertem Verhalten falls der zu schiebende Wert dann > negativ ist, wie Yalu bemerkte. Erst dann wird das Ergebnis zu einem > uint32_t gemacht. > > stdint.h hilft aber auch nicht, wenn es um integer-promotion rules geht. > Ein int bleibt ein int, und die promotion-Rules sind auch in C99 / C11 > die gleichen wie in C89. Ok, mein kleines Rätsel ist also geknackt. Herzlichen Glückwunsch, Johann und Yalu! Insbesonders der Hinweis von Yalu, das << von negativen Zahlen im C-Standard als undefiniert spezififiert ist, war mir neu! Siehe auch http://stackoverflow.com/questions/3668734/unsigned-and-signed-values-in-c-what-is-the-output. Der OP wird allerdings wohl leicht erschüttert ob der Entwicklung seines Threads sein :) :) :) LG, Sebastian Sebastian Wangnick schrieb: > Insbesonders der Hinweis von Yalu, das << von negativen > Zahlen im C-Standard als undefiniert spezififiert ist, war mir neu! Das hast du falsch verstanden, denn du darfst prinzipiell auch negative Zahlen nach links shiften. Der Standard sagt folgendes: "The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1 × 2**E2, reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and nonnegative value, and E1 × 2**E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined." Im obigen Beispiel
ist 0x88 eine positive Zahl, die nach den Integer-Promotion-Regeln in ein int konvertiert wird. Das Ergebnis von u8 << 8 ist daher ebenfalls vom Typ int. Laut Standard soll 0x88 << 8 = 0x88 * 2**8 = 0x8800 sein. Ist int nur 16 Bit breit, ist der maximal darstellbare Wert 0x7fff, somit kann 0x8800 nicht als int dargestellt werden, und das Verhalten ist undefiniert. Folgendes ist aber trotz des negativen linken Operanden von << in Ordnung:
Das Ergebnis -0x4400 ist auch als 16-Bit-Integerzahl darstellbar, weswegen hier keine Probleme auftreten. Edit: Alles, was ich in diesem Beitrag über negative Operanden geschrieben habe, hat sich als falsch herausgestellt (s. Beitrag von Sebastian Wangnick vom 08.02.2014 um 11:19). Richtig ist: Left-Shifts von negativen Zahlen sind immer undefined. :
Bearbeitet durch Moderator
Frank Bär schrieb: > Der Vollständigkeit halber: doch. 2 Minuten danach suchen spuckt > __byte() aus. > Hier ist ein Beispiel dazu: > http://processors.wiki.ti.com/index.php/Byte_Accesses_with_the_C28x_CPU#Using_the_byte_Intrinsic > Nicht schön, aber damit funktioniert dann auch 8Bit-Arithmetik. > Würde mich auch schwer gewundert haben... In Ordnung, du sollst recht haben, hab ich tatsächlich nicht gewusst. Da ich bisher keinen Bedarf danach hatte, hab ich mich auf Aussagen dritter verlassen. Yalu X. schrieb: > Das hast du falsch verstanden, denn du darfst prinzipiell auch negative > Zahlen nach links shiften. Der Standard sagt folgendes: > > "The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated > bits are filled with zeros. If E1 has an unsigned type, the value of > the result is E1 × 2**E2, reduced modulo one more than the maximum > value representable in the result type. If E1 has a signed type and > nonnegative value, and E1 × 2**E2 is representable in the result type, > then that is the resulting value; otherwise, the behavior is > undefined." > > Im obigen Beispiel > unsigned char u8 = 0x88; > unsigned long u32 = u8<<8; > > ist 0x88 eine positive Zahl, die nach den Integer-Promotion-Regeln in > ein int konvertiert wird. Das Ergebnis von u8 << 8 ist daher ebenfalls > vom Typ int. Laut Standard soll 0x88 << 8 = 0x88 * 2**8 = 0x8800 sein. > Ist int nur 16 Bit breit, ist der maximal darstellbare Wert 0x7fff, > somit kann 0x8800 nicht als int dargestellt werden, und das Verhalten > ist undefiniert. Moment. "If E1 has an unsigned type, [...]. If E1 has a signed type and nonnegative value, [...]; otherwise, the behavior is undefined." Danach ist das Verhalten für alle negativen Zahlen (also "signed type" und nicht "nonnegative value") als undefiniert spezifiziert, oder? Oder meint der Standardtext, dass das Verhalten genau dann "undefined" ist, wenn sowohl "E1 has a signed type and nonnegative value" und dann auch das Ergebnis nicht repräsentierbar ist? In diesem Fall wäre dann aber das Verhalten für negative Zahlen (also "signed type" und eben nicht "nonnegative value") dadurch gar nicht erfasst? Und gilt dann einfach die erste Klausel "The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros.", und das Resultat ist arithmetisch gar nicht spezifiziert und damit repräsentationsabhängig? LG, Sebastian Ihr müßt Euch alle mal ein gutes C-Buch kaufen, damit die Resultate nicht so verschieden ausfallen. ;-) SCNR Paul Sebastian Wangnick schrieb: > Danach ist das Verhalten für alle negativen Zahlen (also "signed type" > und nicht "nonnegative value") als undefiniert spezifiziert, oder? Ja, du hast völlig recht. Ich habe gestern in "If E1 has a signed type and nonnegative value, and E1 × 2**E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined." das "and nonnegative value" überlesen. Danke für den Hinweis. Ich hatte für ein paar Tage mal keinen PC und schon ist der Thread hier total überfüllt :D Ist natürlich genial, dass sich so etwas aus meiner Frage entwickelt hat. "Duffs device" ist übrigens auch spannend, aber anscheinend ja nicht mehr "gut", d.h. kein Element der Leistungsoptimierung mehr (dank der Compiler): http://de.wikipedia.org/wiki/Duff%E2%80%99s_Device Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.
|
|