Am besten gewöhnst du dir unnötig klammernsparende Schreibweisen gar nicht erst an. Wenn du nicht tagtäglich immer nur programmierst, unterlaufen dir sonst leicht mal Fehler. Oder du verlierst Zeit, weil du beim Suchen von (anderen) Fehlern immer wieder mal überlegen/nachschlagen musst, ob nicht diese Schreibweise der gesuchte Fehler ist.
Stichwort: Defensive Codierung.
Der Compiler übersetzt ohnehin unabhängig von der Schreibweise, es spart also nichts.
Ist nicht ok, ist "undefined behavior" (UB) und kann irgendwas bewirken, z.B. Programmabsturz. Oder der Compiler entfernt einfach die main()-Funktion komplett, weil er davon ausgehen darf dass UB nie auftritt und somit die main() Funktion sowieso nie aufgerufen wird, weil sie immer zu UB führt. Das gilt auch für Lesezugriffe.
Ist nicht ok, ist "undefined behavior" (UB) und kann irgendwas bewirken,
z.B. Programmabsturz.
Aber nur im Kontext der von Dir nicht zitierten vorangegangenen Codezeilen.
Für sich alleine betrachtet ist die von Dir zitierte Zeile 8 vollkommen legitimer Code, nämlich 'ne simple Pointerdereferenzierung.
"Kriminell" wird es durch die Zeilen 6 und 7 des Codes aus dem Eröffnungsthread, die den Pointer auf etwas anderes zeigen lassen als beim Aufruf der Funktion, der der Pointer übergeben wurde.
Ich dachte sizeof(INT) ist plattformabhängig und nicht generell 4.
Ist es auch nicht.
Man kann es getrost als Fehler betrachten, hier "generell" anzunehmen.
Auf 8- oder 16-Bit-Systemen ist ein /int/ üblicherweise 16 Bit groß, auf 32-Bit-Systemen ist er üblicherweise 32 Bit groß, auf 64-Bit-Systemen ist er meistens auch 32 Bit groß, kann aber auch 64 Bit groß sein.
Das liegt nicht an "Ansi-C" (das nennt man heutzutage C89), sondern
daran, daß da entweder 8- oder 16-Bit-CPUs drinstecken.
Das liegt nur und ausschliesslich am ABI der toolchain. Natürlich wird da in der Regel Rücksicht auf die Architektur genommen, aber ein Muss ist das nicht.
INT ist immer 4 Bytes. Das ist ein typedef in Windows.
Wenn wir schon beim Klugscheissen sind, hast Du dich grad selbst widerlegt.
sizeof(int) ist Plattformabhängig. Und bei Windows halt grad mal 4.
int32_t ist plattformunabhängig, nicht zufälligerweise immer und überall 4 Byte und das soll man auch verwenden
Die ganze Streiterei um C und seine Regeln und Definitionen erinnert mich einfach immer wieder an dieses Meme. Egal wie perfekt jemand C kann, irgendwann übersieht er was und schießt sich dann voll selbst ins Knie. Ohne Ausnahme.
Grüße von einem Rust-Coder, der sich lange genug mit der C-Grütze rumgeschlagen hat und jetzt die Show genießt.
Speichernavigation sollte man erst weiter hinten anfangen. Das ist besser, denn da kommen viele Anfänger durcheinander, wenn die nicht die Grundlagen erarbeitet hatten.
Ich hatte damals auch Probleme (weil: da kommt doch vieles auf einmal auf einen zu) und so brauchte ich so um die 2-3 Wochen, um da lockerer draufzuschauen.
Vielleicht ist es besser, erstmal mit Stackoverflow (nicht die Internetseite) und gdb in die Speichergeschichte einzusteigen. Kann jedenfalls auch hilfreich sein.
Hinsichtlich der Sonderzeichen ist das Erlenkötterbuch gut - da muss man aber bei der Stange bleiben, damit man die Zusammenhänge behalten kann.
Ist nicht ok, ist "undefined behavior" (UB) und kann irgendwas bewirken,
z.B. Programmabsturz. Oder der Compiler entfernt einfach die
main()-Funktion komplett, weil er davon ausgehen darf dass UB nie
auftritt und somit die main() Funktion sowieso nie aufgerufen wird, weil
sie immer zu UB führt. Das gilt auch für Lesezugriffe.
das glaube ich erst Mal nicht (ohne es zu wissen):
wenn der Compiler main übersetzt, weiß er doch nicht dass in fn1 ein undefined behavior auftritt (fn1 könnte ja auch in einem anderen File sein)
Compiler main übersetzt, weiß er doch nicht dass in fn1 ein undefined
behavior auftritt (fn1 könnte ja auch in einem anderen File sein)
Ich hab ja nicht gesagt dass der Compiler das UB erkennen muss. Aber wenn er es erkennt, darf er das Programm beliebig verunstalten ohne den C-Standard zu verletzen. Der Standard verlangt nicht vom Compiler, fehlerhaften Code irgendwie sinnvoll zu übersetzen.
Wenn fn1 in der selben Datei steht wie hier im Beispiel, sieht der Compiler was darin passiert. Wenn's eine andere Datei ist sieht er es auch, wenn LTO aktiviert ist.
das glaube ich erst Mal nicht (ohne es zu wissen):
wenn der Compiler main übersetzt, weiß er doch nicht dass in fn1 ein
undefined behavior auftritt (fn1 könnte ja auch in einem anderen File
sein)
fn1() übergibt einen Zeiger auf einen /int/ Wert. NUR EINEN!
Wenn nun dieser Zeiger inkrementiert wird:
1
j++;// adresse +sizeof(POINTER)
dann zeigt j hinter den angedachten /int/ Wert.
Also ist:
INT ist ein in Millionen von Zeilen verwendetes Typedef, das immer
4-Bytes hat.
Wo ist das immer 4 Btes groß?
Bei dir? In wie vielen Milliarden Zeilen C-Code NICHT?
Mir ist schon klar, dass das K&R-C unzulänglich ist/war und dass von dort die Ursprünge des Gewurstles, das sich über Dekaden hinzieht, herkommt.
"Inzwischen" kann man aber die Typen verwenden, die klar in der Größe definiert sind. Und das sollte man auch.
Bitte dazu mal <stdint.h> lesen, bzw. Kommentare/Erklärungen dazu.
Anekdote:
Ich hatte mal mit dem Propeller von Parallax rumgespielt. In interessanter 32 Bit Prozessor mit einer verkorksten Sprache "Spin".
Da war ein Integer Typ "word". Irgendwann wundere ich mich, warum der nur 16 Bit groß ist und frag im Forum nach. Bis dann der Entwickler des Propeller (Gracey ?) geantwortet hat, dass "word" immer 16 Bit lang ist. Auf die Frage woher denn die Definition kommt:
Er hat den Compiler für Spin in Assember (total gaga, aber Respekt für die Arbeit) 8086 (jedenfalls ein 16-Bitter) geschrieben und da ist das halt ein word.
Den Compiler mussten sie komplett neu schreiben, ASM ist nicht sonderlich portabel. Spin wurde dann um C ergänzt, der Propeller verschwindet so langsam in der Versenkung. Bis auf eine extrem engagierte Fan-Gemeinde.
long, double, long long, unsigned char, das ist völlig ballaballa.
Leute, schreibt euren code plattformunabhängig und leserlich.
Ah, und zum Rust-Kommentar:
Ja, da geb ich dir Recht. Der Designer hatte wohl genug unter C gelitten. Ich warte noch darauf, dass Rust endlich mal auf µC implementiert wird. Der eine STM32 (oder was) ist reichlich wenig um sich da drauf einzulassen. Ich wäre aber sofort dabei! Hab aber Rust seit zwei Jahren nicht mehr angefasst.
Aber man kann sich auf Folgendes verlassen:
Die Größe eines /int/ ist: sizeof(int)
Gilt auch für char,short,long,…,char*,short*,long*,…
Und mehr Annahmen macht man einfach nicht!
Aber man kann sich auf Folgendes verlassen:
Die Größe eines int ist: sizeof(int)
Gilt auch für char,short,long,…,char*,short*,long*,…
Und mehr Annahmen macht man einfach nicht!
Ich meine, man kann sich bei C darauf verlassen, dass int mindestens 16 Bit groß ist. So war das jedenfalls viele Jahre lang und es wäre ziemlich dumm, innerhalb der selben Sprache davon abzuweichen.
Ich meine, man kann sich bei C darauf verlassen, dass int mindestens
16 Bit groß ist.
/short/ ist (aus dem Gedächtnis) auch als <= /int/ und /long/ als >= /int/ definiert. Und /short/ muss < /long/ sein. Das ist allerdings alles relativ… ;-)
/int/ könnte also 16/32Bit und vielleicht sogar bald 64Bit (bei den demnächst erscheinenden 128Bit Prozessoren, Speicher kostet ja gerade nichts) breit sein. ;-)
Übrigens word das printf u.U. nie erreicht (Signed Overflow).
Komisch, bei mir schon.
Die resultierende Ausgabe habe ich oben mitgepostet...
Dass ein Programm das macht, was du erwartest, bedeutet nicht, dass es korrekt ist :-)
Die Logik ist:
Programm macht nicht was du erwartest => Programm ist inkorrekt
und
Programm ist korrekt => Programm macht was du erwartest
Was NICHT gelten MUSS:
Programm ist inkorrekt => Programm macht nicht was du erwartest
sowie
Programm macht was du erwartest => Programm ist korrekt.
Komisch, bei mir schon.
Weil Undefined Behaviour kein definiertes Verhalten erfordert
Programm kann das machen was du erwartest (was immer das ist)
Programm kann nicht-funktional sein.
Compiler kann eine Diagnostic ausgeben
Compiler kann terminieren
Programm kann sich aufhängen / abstürzen.
Letzteres passiert zu Beispiel mit GCC 13. Und mit Sanitizer:
$ gcc x.c -Os -o x.x -fsanitize=undefined && ./x.x
x.c:11:10: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'
Nein, sind wir nicht. Da Rust schon was mit C gemeinsam hat und gleichzeitig völlig anders ist ... An der Speicherverwaltung wird man sich in Rust (aber auch weitaus unbewusster und einfacher in C) zuerst die Zähne ausbeissen.
Damit hat Rust aufgeräumt, weil es in C saugefährlich ist (siehe oben) aber gleichzeitig schon auch recht praktisch ist.
Der typgrößenwirrwar von C lässt sich leider nur durch komplettes ignorieren der alten Herangehensweise beheben. Und ein gutes Beispiel dafür wie es richtig ist, ist halt Rust. Oder/und exklusiv <stdint.h> zu verwenden.
Es hilft schon etwas, andere Sprachkonzepte anzuschauen. Da gibt es gute Lösungen an denen man sich orientieren kann.
Es hilft schon etwas, andere Sprachkonzepte anzuschauen. Da gibt es gute
Lösungen an denen man sich orientieren kann.
Verstehe worauf du hinaus willst, aber gerade für den von dir zitierten Anfänger kann das aber nur noch verwirrender sein. Wenn man /C/ dann einigermaßen im Griff hat und wenn sich die Notwendigkeit ergibt, dann schaut man sich (mit diesem Grundlagenwissen) Anderes an. Python nutzt zB. nur eine int Klasse, die aber praktisch beliebig groß werden darf.
Python nutzt
zB. nur eine int Klasse, die aber praktisch beliebig groß werden darf.
Das ist halt eine andere Lösung.
C ist ein "Hochsprachenassembler", wo man oft genug Platz sparen muss oder auf die Hardware eingehen muss (Ports mit 8 I/O etc.).
Wenn man C so lernen will, dass int immer richtig ist, dann entdeckt man irgendwann, dass man long braucht. Also bin ich der Meinung, dass man es von Anfang an richtig lernen soll. Dass es streng definierte Typen gibt und man damit ganz gut zurecht kommt.
C ist auf µC sehr gut aufgehoben. Für dem Desktop gibt es besser geeignete Sprachen.
Wenn einem das nicht gefällt, dann soll er halt Python hernehmen. Oder Java, oder Rust, oder C++, oder Swing, oder C#. Das hat alles seine Vor- und Nachteile. Es ist aber nicht hilfreich, die Sprachkonzepte/Paradigmen zu ignorieren oder sie nicht zu akzeptieren. Man will ja mit einer Sprache programmieren, nicht gegen.
Man kann ja mal versuchen C in OCamel zu übersetzen. Oder gerne auch die andere Richtung.
Es gibt auch Leute, die sehr lange Dinge machen, die sie nicht richtig können.
Klar, denen kann man dann mit betreuten Programmiersprachen für "inklusives Programmieren" entgegenkommen, aber vielleicht wäre Rosenzüchten doch besser?
In C/C++ heißt es eben nicht "int" sondern "_BitInt".
Na ja, aber man muss sich vorher festlegen und nach dem Kompilieren bleibt es statisch (wenn ich das richtig sehe).
Das ist etwas völlig anderes als sich selbst dynamisch nach Bedarf anpassende Integer (wachsend und schrumpfend).
Des weiteren ist es auch noch arg limitiert.
Um mal mein Beispiel in /C/ zu zeigen, für 7^7^7 braucht es 2311998 Bits
Das ist etwas völlig anderes als sich selbst dynamisch nach Bedarf
anpassende Integer (wachsend und schrumpfend).
Ich nehme einfach mal an, dass es sowas (inzwischen) auch in C++ gibt.
In Rust (sorry) gibt es das auch:
1
usenum_bigint::BigUint;
Das ist die dynamische Version. Gibt auch statische Versionen.
Man kann sich solche Sachen doch selbst bauen. Auch in C. Ich brauchte mal dynamische Arrays. Dann schreib ich mir die halt, das dauert keinen Tag und man kann die ab dann immer wieder verwenden.
"Das gibts nicht" ist eine etwas einfältige Einstellung.
Bauen kann man sich alles Mögliche. Da braucht man noch nicht einmal eine Programmiersprache, das geht auch in BASIC.
Hier geht es aber darum was eine Sprache (in diesem Fall /C/) zur Verfügung stellt, nicht was man hinterher nach- und dazubasteln kann.
Übrigens geht es auch nicht um C++.
Es gibt auch Leute, die sehr lange Dinge machen, die sie nicht richtig
können.
Oder halt auch Leute, die einfach nur Menschen sind und dementsprechend auch Fehler machen. Möchtegern-Göttern wie dir passiert so etwas natürlich niemals.
Das haben allerdings wohl auch all die anderen erfahrenen C-Programmierer gedacht, die die Fehler produziert haben: Ich kann das, ich mache keine Fehler.
Klar, denen kann man dann mit betreuten Programmiersprachen für
"inklusives Programmieren" entgegenkommen
Natürlich ist das der sinnvollste Ansatz! Ein Hochsprache, in der Menschen programmieren sollen, hat es den Menschen so einfach wie möglich zu machen, keine Fehler zu produzieren, sonst taugt sie nix.
Wenn es z.B. sowas wie UB gibt, dann hat der Compiler die verdammte Pflicht, zu warnen und darf nicht einfach stillschweigend Code entfernen, weil ihm der überflüssig vorkommt. Er darf und sollte(!) einfach sagen, dass er ihm überflüssig vorkommt.
Also entweder ihr koennt C oder ihr bereitet auch auf die
Arbeitslosigkeit vor.
Claude kann kein C?
Wir lassen uns jetzt von KI-generierten Videos erklären warum C die einzige Sprache mit Jobsicherheit ist, was ja eigentlich nur sein könnte wenn es nicht KI-generierbar wäre?
GMP has a rich set of functions, and the functions have a regular
interface. The basic interface is for C, but wrappers exist for
other languages, including Ada, C++, C#, Julia, .NET, OCaml, Perl,
PHP, Python, R, Ruby, and Rust.
Die einzige Frage ist wieviele Leute es gibt, die sich mit Rust auskennen UND mit AVR auskennen UND mit Compiler-Interna auskennen UND die nötigen Resourcen (Lust, Zeit, ...) mitbringen, um da was zu bewegen.
Zumindest bei G++ ist diese Schnittmenge über die letzten 25 Jahren effektiv leer...
Die einzige Frage ist wieviele Leute es gibt, die sich mit Rust
auskennen UND mit AVR auskennen UND mit Compiler-Interna auskennen UND
die nötigen Resourcen (Lust, Zeit, ...) mitbringen, um da was zu
bewegen.
Zumindest bei G++ ist diese Schnittmenge über die letzten 25 Jahren
effektiv leer...
Zu Clang/LLVM weiß ich nix, da hakelt es Teilweise schon mit C weil es keine dedizierte Libc gibt. Und Clang ist an vielen Stellen inkompatibel mit GCC, d.h. Piggypack auf AVR-LibC funktioniert nicht wirklich gut.
Die C Unterstützung für AVR ist theoretisch? Vielleicht hast ja die
letzten 25 Jahre verpennt :-)
…
Zu Clang/LLVM weiß ich nix, da hakelt es Teilweise schon mit C weil es
keine dedizierte Libc gibt.
Ja, was denn jetzt? Kann man clang ernsthaft für den AVR (C/C++/Rust, was auch immer) nutzen, oder nicht?
Der Code ist UB. Was beim Overflow eines signed int passiert, ist nicht definiert. Einige Compiler machen daraus deshalb eine Endlosschleife: https://godbolt.org/z/qnc48jYM8