Guten Morgen,
ist dieser Fehler so dumm, dass der nicht einmal eine Warnung wert ist?
Ungefähr 70 -W von -Waggregate-return bis -Wwrite-strings reichen nicht?
Kann man dagegen etwas tun?
IMHO ist es hier auch von der CPU/Speicer/System-architektur abhängig
ob es zu Problemen kommt.
Hier wird ein Feld aus vier char-pointern definiert, im weiteren verlauf
aber mehr als die vier verwendet. (Oder liegt das Problem wo anders ?!)
Da kommt es IMHO darauf an wie der Compiler die pointer verteilt und wie
die Adressberechnung in der CPU realisiert ist.
Und vielleicht werden die überzählingen pointer resp. schreibzugriffe
später wegoptimiert, da nie von diesen Adressen gelesen wird (dead code
elimination).
Aber ich stimme zu, eine warning wäre angebracht. Vielleicht ist diese
aber nicht eingeschaltet (-Wall) und manche warnings sind darüber hinaus
an bestimmte Optimierungsstufen gebunden.
Würde ein exlizites malloc() helfen ?
> "Enable optimization. Without at least -O2, GCC is not doing enough> analysis to know what a is, and that you ran off the edge."> WTF? -.-
Das ist aber seit Jahrzehnten so, die eingestellte Optimierungsstufe
bestimmt auch welchen Analysen der code unterworfen wird, die Idee ist
wohl die compile-zeit möglichst kurz zu halten.
Bradward B. schrieb:> IMHO ist es hier auch von der CPU/Speicer/System-architektur abhängig> ob es zu Problemen kommt.
Einfach irgendwo auf den Stack zu schreiben dürfte auf den allermeisten
Plattformen ziemliche Probleme erzeugen. Diese Überlegung ist aber
müßig, es ist auf jeden Fall falsch und man kann sich absolut nicht
darauf verlassen, dass da irgendwas bestimmtes passiert.
In diesem Fall könnte man durchaus erwarten, dass der Compiler das
Problem erkennen würde weil die Definition der Variable und der Index
direkt sichtbar sind. Aber auch das wäre nur eine Nettigkeit des
Compilers, auf die man sich nicht verlassen sollte.
Zum debuggen solcher Problem kann man z.B. valgrind verwenden oder
"-fsanitize=undefined" an den GCC übergeben.
Bauform B. schrieb:> Kann man dagegen etwas tun?
Deswegen benutze ich sehr gerne QT Creator als Editor, denn dieser warnt
vor viel mehr potentiellen Fehlern, als es der GCC tut.
>> IMHO ist es hier auch von der CPU/Speicer/System-architektur abhängig>> ob es zu Problemen kommt.>> Einfach irgendwo auf den Stack zu schreiben dürfte auf den allermeisten> Plattformen ziemliche Probleme erzeugen. Diese Überlegung ist aber> müßig, es ist auf jeden Fall falsch und man kann sich absolut nicht> darauf verlassen, dass da irgendwas bestimmtes passiert.
Heap oder stack - that is the question. Und dann wäre noch der cache.
Bradward B. schrieb:> Heap oder stack - that is the question. Und dann wäre noch der cache.
Mir ist keine Plattform bekannt, die lokale Variablen auf einen Heap
legen würde. Eine Plattform, die keinen Stack hat aber eine dynamische
Speicherverwaltung ist schwer vorstellbar? Und selbst wenn, irgendwo auf
den Heap zu schreiben ist auch nicht viel besser.
Der Cache ist irrelevant, für Single-Thread Anwendungen ist der Cache
unsichtbar (DMA, mmap etc. ausgenommen).
In function 'foo',
inlined from 'main' at <source>:30:5:
<source>:19:8: warning: array subscript 6 is above array bounds of 'char
*[4]' [-Warray-bounds=]
19 | envs[ 6] = NULL;
| ~~~~^~~~
Mit Optimierung -O3
Oliver
Rbx schrieb:> edit: Rust wäre natürlich auch noch eine Empfehlung.
Ich habs mir verkniffen. :-)
Nein, ich will damit keinen C gegen Rust Krieg lostreten. Ich mag C.
Selbst mit Klimmzügen bringt man es nicht hin, so einen Rust-code zu
kompilieren.
Auch mit unsafe {...} wird es anstrengend und nur mit Vorsatz möglich.
Oliver S. schrieb:> In function 'foo',> inlined from 'main' at <source>:30:5:> <source>:19:8: warning: array subscript 6 is above array bounds of 'char> *[4]' [-Warray-bounds=]> 19 | envs[ 6] = NULL;> | ~~~~^~~~>> Mit Optimierung -O3>> Oliver
Wobei ja envs[4] und envs[5] auch noch eine Rüge verdienten.
Zumal sie zuerst in Erscheinung treten.
Nick schrieb:> Nein, ich will damit keinen C gegen Rust Krieg lostreten. Ich mag C.
Wobei das Beispiel hier stark nach der Art von Aufgabe aussieht, die man
typischerweise mit einer gewöhnlichen Scriptsprache erledigen kann:
Stringverarbeitung und Prozesse starten. Da ist die Performance
vermutlich sowieso nicht besonders relevant. Die gezeigte Funktion ist
in (Ba)sh ein Einzeiler, Python wäre für komplexere Prozeduren zu
bevorzugen.
PS: Ist das strdup() nicht unnötig? chdir() statt "PWD=%s" würde mir
auch etwas sauberer vorkommen.
>> In function 'foo',>> inlined from 'main' at <source>:30:5:>> <source>:19:8: warning: array subscript 6 is above array bounds of 'char>> *[4]' [-Warray-bounds=]>> 19 | envs[ 6] = NULL;> Wobei ja envs[4] und envs[5] auch noch eine Rüge verdienten.> Zumal sie zuerst in Erscheinung treten.
Falls es um die "in order of appearance" dreht könnte aber
1
char*args[16],*envs[4];
mit Zeilennummer angemeckert werden - stünde da 'ne 7 wäre [4],[5] und
[6] nicht unerwartet.
Bradward B. schrieb:> Falls es um die "in order of appearance" dreht
Nein, darum geht es nicht. Es wird nur Zeile 14 angemeckert (and
rightfully so), jedoch nicht die Zeilen 13 und 15.
Norbert schrieb:> Bradward B. schrieb:>> Falls es um die "in order of appearance" dreht>> Nein, darum geht es nicht. Es wird nur Zeile 14 angemeckert (and> rightfully so), jedoch nicht die Zeilen 13 und 15.
Und es nicht ausgeschlossen, das der "(Tipp-)Fehler" resp. die Korrektur
in der Zeile steckt, die von der Warning nicht genannt wird, hier Zeile
vier.
Bradward B. schrieb:> Und es nicht ausgeschlossen, das der "(Tipp-)Fehler" resp. die Korrektur> in der Zeile steckt, die von der Warning nicht genannt wird, hier Zeile> vier.
Wie soll denn der Compiler wissen, was du dir gerade so wünscht?
Der soll Fehler melden und nicht wie eine AI auf Ecstasy halluzinieren!
Im Übrigen gibt er diesbezüglich eine note aus.
Norbert schrieb:> Wie soll denn der Compiler wissen, was du dir gerade so wünscht?
Deswegen nutzen manche Leute ja auch Programmiersprachen aus dem Bereich
"betreutes Programmieren".
Niklas G. schrieb:> Wobei das Beispiel hier stark nach der Art von Aufgabe aussieht, die man> typischerweise mit einer gewöhnlichen Scriptsprache erledigen kann:
Ja, kann man und 100 andere Sprachen auch. Der TO hat nur mal ein
C-Problem. Du kannst mir natürlich vorwerfen, dass ich Rust erwähnt
habe. Zumindest sind C und Rust aus der gleichen Familie (darf man das
sagen?).
Aber nochmal, einen Programmiersprachen-Krieg wollte ich nicht anzetteln
und will ich auch nicht weiterführen. Dafür kenn ich zu viele Sprachen.
Niklas G. schrieb:> PS: Ist das strdup() nicht unnötig?
Über das erste (bozo_exe) kann man diskuptieren, dann müsste es aber im
Kommentar stehen, dass der Aufrufer dafür verantwortlich ist. Eine
brandgefährliche Taktik!
Beim zweiten: Wer garantiert mir, dass das als Konstante irgendwo steht?
ein static const hätte das sichergestellt. Auf was anderes würde ich
mich nicht einlassen wollen. Ja, OK, muss ja irgendwo stehen. Dann ist
es zumindest für mich schlechter Programmierstil.
D. Knuth "Literate Programming", lesenswert.
Norbert schrieb:> s wird nur Zeile 14 angemeckert (and> rightfully so), jedoch nicht die Zeilen 13 und 15.
Spasseshalber mal die angemeckerte Zeile auskommentieren und dann
nochmal versuchen. Die Compiler kommen bei Mehrfachfehlern schon mal ins
Trudeln.
Bradward B. schrieb:> Und dann wäre noch der cache.Niklas G. schrieb:> Der Cache ist irrelevant,
Da ist üblicherweise nur das Alignment wichtig.
Nick schrieb:> Nein, ich will damit keinen C gegen Rust Krieg lostreten.
Ist doch auch nicht nötig, wir sind doch hier nicht so eine Sekte wie
auf Stack Overflow.
(Stack Overflow is Dead (Here's Why) -
https://www.youtube.com/watch?v=dsfqyVYkrbM)
Nick schrieb:> Spasseshalber mal die angemeckerte Zeile auskommentieren und dann> nochmal versuchen. Die Compiler kommen bei Mehrfachfehlern schon mal ins> Trudeln.
Wäre zwar schön, wird aber eher schlimmer.
Mikro 7. schrieb:> Interessant...>> https://stackoverflow.com/questions/382993/why-do-compilers-not-warn-about-out-of-bounds-static-array-indices>> "Enable optimization. Without at least -O2, GCC is not doing enough> analysis to know what a is, and that you ran off the edge."
Das wäre jetzt auch mein Vorschlag gewesen, mal die Optimierungen
einzuschalten.
> WTF? -.-
Wie meinst? Der Optimizer muss eine viel detaillierte Code-Analyse
machen, um zu erkennen, welche Teile bereits zur Compilezeit
durchgeführt werden können. Das führt natürlich auch dazu, dass dann
solche Fehler leichter gefunden werden können, weil sie eben schon zur
Compilezeit auftreten und nicht erst zur Laufzeit.
Nick schrieb:> Beim zweiten: Wer garantiert mir, dass das als Konstante irgendwo steht?
Wozu wäre das nötig? Beim Aufruf von execve() kann der Kernel den
Dateinamen und das "-P" problemlos lesen. execve() kehrt bei Erfolg
nicht zurück, der Speicherbereich des Prozesses wird durch das neue
Programm ersetzt und der Kernel kopiert die Strings in den neuen
Prozess hinein.
Es spielt also keine Rolle was das Programm sonst noch mit den Strings
machen will, das wird schließlich nicht mehr ausgeführt.
Nur im Fehlerfall kehrt execve() zurück, aber sofern die Strings nicht
in der Fehlerbehandlung irgendwo anders hin gereicht und dort verwendet
werden ist das strdup() immer noch unnötig.
Rbx schrieb:> Niklas G. schrieb:>> Der Cache ist irrelevant,>> Da ist üblicherweise nur das Alignment wichtig.
Nur für die Geschwindigkeit, nicht die Korrektheit. Der Cache kann wohl
immer exakt das gleiche Alignment was der Prozessor sonst auch kann.
Ergo: Transparent.
> Wie soll denn der Compiler wissen, was du dir gerade so wünscht?> Der soll Fehler melden und nicht wie eine AI auf Ecstasy halluzinieren!
Der Compiler hat einen Widerspruch zwischen mehren Zeilen endteckt, den
könnter er vollständig nennen. Der Widerspruch besteht hier in der Größe
des feldes und seiner Nutzung. Also entweder ist das Feld zu klein oder
der Zugriff zuviel. Genau das könnte der Compiler melden. Er könnte auch
die Größe automatisch anpassen und das melden.
Vor Jahren gab es mal einen schweres Sicherheitsproblem, weil ein
Programmiere einen Zugriff auf nichtinitialisierten Speicher wegen einer
Warning diesbezüglich umschrieb. Blöd das der ursprüngliche
Programmierer, den Speicher absichtlich uninitialisiert lies, um so eine
Zufällige Größe für die Verschlüsselung zu generieren. Durch den
vermeintlichen Fix durch zwangs-initialisierung war der Zufall natürlich
dahin ... manchmal ist eben zuviel Determinismus schädlich.
Bradward B. schrieb:> den Speicher absichtlich uninitialisiert lies, um so eine> Zufällige Größe für die Verschlüsselung zu generieren
Wer sich auf den Inhalt von nicht-initialisiertem Speicher verlässt,
auch als Zufalls-Generator, dem ist nicht zu helfen. Die Verteilung der
Zufallsdaten kann sehr schlecht sein, z.B. "Immer Null", oder auch immer
der gleiche Datenrest aus einer zuvor laufenden Prozedur. Jeder, der mit
Kryptographie arbeitet sollte wissen wie man an gute Zufallsdaten kommt.
Norbert schrieb:> Oliver S. schrieb:>> In function 'foo',>> inlined from 'main' at <source>:30:5:>> <source>:19:8: warning: array subscript 6 is above array bounds of 'char>> *[4]' [-Warray-bounds=]>> 19 | envs[ 6] = NULL;>> | ~~~~^~~~>>>> Mit Optimierung -O3>>>> Oliver>> Wobei ja envs[4] und envs[5] auch noch eine Rüge verdienten.> Zumal sie zuerst in Erscheinung treten.
envs[5] meckert er auch an, envs[4] aber nicht.
Oliver
Bradward B. schrieb:> Der Compiler hat einen Widerspruch zwischen mehren Zeilen endteckt, den> könnter er vollständig nennen. Der Widerspruch besteht hier in der Größe> des feldes und seiner Nutzung. Also entweder ist das Feld zu klein oder> der Zugriff zuviel. Genau das könnte der Compiler melden.
Lies dir doch bitte mal die Fehlermeldung genau durch.
Beitrag "Re: Fühlt sich der gcc hier veralbert?"
* Die Zeile 7 ist syntaktisch absolut OK.
* Die Zeilen 13, 14 und 15 sind erkennbar fehlerhaft (out of bounds)
* Nur Zeile 14 wird gemeldet.
* Und der Compiler generiert eine note bzgl. Zeile 7
Im Grunde genommen hätte er noch nicht einmal eine Warnung, sondern
vielmehr einen(drei) Fehler generieren müssen.
Norbert schrieb:> Oliver S. schrieb:>> envs[5] meckert er auch an, envs[4] aber nicht.>> Das ist, wie ich feststellen musste, abhängig von der jeweiligen> Compiler Version.
auf godbolt liefern gcc 12 - 16, jeweils letzte Version, alle die
gleichen Meldungen, mit -Wall -Wextra -O3
Oliver
Oliver S. schrieb:> auf godbolt liefern gcc 12 - 16, jeweils letzte Version, alle die> gleichen Meldungen, mit -Wall -Wextra -O3
Und ein gcc version 10.2.1 20210110 (Debian 10.2.1-6) macht's eben
nicht!
Wie man hier erkennen kann:
Beitrag "Re: Fühlt sich der gcc hier veralbert?"
> * Die Zeile 7 ist syntaktisch absolut OK.>> * Die Zeilen 13, 14 und 15 sind erkennbar fehlerhaft (out of bounds)>> * Nur Zeile 14 wird gemeldet.>> * Und der Compiler generiert eine note bzgl. Zeile 7>> Im Grunde genommen hätte er noch nicht einmal eine Warnung, sondern> vielmehr einen(drei) Fehler generieren müssen.
Und diese drei "Fehler" wären mit einer Anpassung der Zeile7 keine
'Fehler' mehr.
Vom Syntax her sind alle Zeilen in Ordnung, sonst hätte schon der Parser
gekotzt.
"Programmiersprachlich" ist das Ganze auch in Ordnung, Speicherschutz
ist in C nunmal in der Verantwortung des Coders und nicht in der des
Compilers. Wenn es Probleme gibt, dann zur Laufzeit, und die "fängt"
ohnehin nicht der Compiler ab, sonder die runtime.lib. Oder das
Betriebssystem, bspw."segementation fault".
Und Deklaration ist nun mal m.E keine Allokation. IMHO ist es halt im
Programmier-Alltag nicht ungewöhnlich, das man Tage nach der
array-deklaration drauf kommt, das man doch etwas mehr benötigt, aber im
"flow" vergisst, die Dekleration händisch anzupassen.
Vielleicht verhindern ja " Programmiersprachen aus dem Bereich
'betreutes Programmieren' solche "menschlichen Fehler". Ich meine bspw.
ADA bringt extra range checking mit (auch wenn es aus Performancegründen
gerne deaktiviert wird).
Bradward B. schrieb:> Und diese drei "Fehler"
Es sind keine "angeblichen Fehler", es sind FEHLER. PUNKT.
Aber ich gebe auf mit Erklärungsversuchen. Glaub' was du willst.
Bradward B. schrieb:> "Programmiersprachlich" ist das Ganze auch in Ordnung,
Nein. Der C-Standard definiert so etwas ganz eindeutig als
fehlerhaften Code. Es ist kein korrektes C-Programm. Nicht alle Fehler
sind Syntaxfehler, und C ist mehr als nur Syntax. Nur weil ein Fehler
nicht vom Compiler gefunden wird, heißt das noch lange nicht dass es
korrekt ist.
"Nachts ist es kälter als draußen" ist zwar sprachlich auch korrekt,
aber inhaltlich sinnlos.
Rolf M. schrieb:>> WTF? -.->> Wie meinst? Der Optimizer muss eine viel detaillierte Code-Analyse> machen, um zu erkennen, welche Teile bereits zur Compilezeit> durchgeführt werden können. Das führt natürlich auch dazu, dass dann> solche Fehler leichter gefunden werden können, weil sie eben schon zur> Compilezeit auftreten und nicht erst zur Laufzeit.
Das mag ja so sein. Ich finde es aber sehr irritierend.
Vielen Dank miteinander! Das ist doch interessanter geworden als ich
dachte. Also gut, selbst solche offensichtlichen Fehler werden erst ab
-O2 gefunden. Ist mir recht, hier auf dem PC kann ich Assembler sowieso
nicht lesen. Aus Gewohnheit benutze ich immer nur -O, als Kompromiss
zwecks Debugging bei den Cortex-M.
Norbert schrieb:> Im Grunde genommen hätte er noch nicht einmal eine Warnung, sondern> vielmehr einen(drei) Fehler generieren müssen.
Das wäre nett, aber er hat das so falsch, praktisch wörtlich, übersetzt.
Wohl, weil envs[5] und envs[6] ziemlich sicher UB ist? envs[4] ist evt.
irgendwie ok, wegen der Sonderregel "just beyond an array".
Niklas G. schrieb:> PS: Ist das strdup() nicht unnötig? chdir() statt "PWD=%s" würde mir> auch etwas sauberer vorkommen.
chdir() wird auch gemacht. Ich denke, im Zweifelsfall braucht man
beides.
strdup() ist eine ganz andere, besonders traurige Geschichte. Sowohl
args[1]="-P"; als auch args[1]=(char *)"-P" werden angemeckert. Sehr
unpraktisch, aber eigentlich richtig, "-P" ist ja const und die Pointer
nicht.
Norbert schrieb:> Oliver S. schrieb:>> auf godbolt liefern gcc 12 - 16, jeweils letzte Version, alle die>> gleichen Meldungen, mit -Wall -Wextra -O3>> Und ein gcc version 10.2.1 20210110 (Debian 10.2.1-6) macht's eben> nicht!>> Wie man hier erkennen kann:> Beitrag "Re: Fühlt sich der gcc hier veralbert?"
Gerade noch die anderen probiert. Alle auf godbolt vorhandenen gccs,
also 4-16, bringen die Warnungen.
clang22 meckert genauso auch nur 5 und 6 an.
Oliver
> Nein. Der C-Standard definiert so etwas ganz eindeutig als> fehlerhaften Code.
Nope, vielleicht als "undefined behaviour" aber nicht als "Failure" was
den Compile-run abbrechen liesse.
Aber gern kann man die entsprechende Passage im Standard zeigen, wo
dergleichen als syntax failure definiert ist.
Bauform B. schrieb:> strdup() ist eine ganz andere, besonders traurige Geschichte. Sowohl> args[1]="-P"; als auch args[1]=(char *)"-P" werden angemeckert.
Das eigentliche Problem ist dass die Argumente von execve() so
deklariert sind dass argv und envp Arrays von Zeigern auf
*nicht*-Konstante Strings sein müssen. Das hat aber historische Gründe,
tatsächlich wird execve() diese Strings nicht verändern. Daher kann
man das "const" legal wegcasten und spart sich das strdup():
1
constchar*args[]={bozo_exe,"-P",NULL};
2
...
3
if(execve(args[0],(char*const*)args,envs)){
Bradward B. schrieb:> Nope, vielleicht als "undefined behaviour" aber nicht als "Failure" was> den Compile-run abbrechen liesse.
Programme mit undefined behaviour sind aber fehlerhaft und müssen noch
nichtmal irgendwie sinnvoll übersetzt werden.
Bradward B. schrieb:> Aber gern kann man die entsprechende Passage im Standard zeigen, wo> dergleichen als syntax failure definiert ist.
Ich habe nicht behauptet dass es Syntaxfehler sind, im Gegenteil. Aber
Fehler sind es dennoch. Denn C definiert, wie gesagt, mehr als nur die
Syntax, und es definiert solche Out-Of-Bounds Zugriffe als Fehler.
Oliver S. schrieb:> Gerade noch die anderen probiert. Alle auf godbolt vorhandenen gccs,> also 4-16, bringen die Warnungen.
Ja dann mach's halt richtig.
Norbert schrieb:> Vielleicht iss ja jetzt langsam mal gut.
Nun ja, du hast mit dem Geplärre angefangen…
Ernsthafte Programmierer compilieren mit adress sanitizer, der dann bei
der Ausführung auch mit einem steinalten gcc 10 sagt:
=================================================================
==1==ERROR: AddressSanitizer: stack-buffer-overflow on address
0x7ffef8a45dd0 at pc …
In diesem Sinne.
Oliver
> Aber> Fehler sind es dennoch. Denn C definiert, wie gesagt, mehr als nur die> Syntax, und es definiert solche Out-Of-Bounds Zugriffe als Fehler.
Literaturstelle, bitte?
und ich glaube hier wird munter an den Begriffen vorbei geredet.
"Error", "Warning" ist doch hier im Sinne von Compiler-Meldungen
gemeint. Und solange der Compiler einen assembler-code für den Zugriff
auf diese Address generieren kann, ist programmiertechnisch alles
akzeptabel, auch wenn es während der runtime "krachen" sollte.
"Errors report problems that make it impossible to compile your program"
aus:
https://gcc.gnu.org/onlinedocs/gcc-11.5.0/gcc/Warnings-and-Errors.html
Beispiel Routenführung wie:
* " gehe zum Haus "Kirchgasse", Nr. 12
* " von dort gehe drei Grundstücke nach rechts weiter "
völlig korrekt auch falls in dem jeweiligen Ort die Bebauung der
Kirchgasse mit der Nummer 12 enden sollte. Der "Fehler" steckt da nicht
im Programm sondern in der tatsächlichen Bebauung.
Oliver S. schrieb:> Nun ja, du hast mit dem Geplärre angefangen…
Du hast eine verifizierbar FALSCHE Aussage getroffen und bist nicht in
der Lage dies zuzugeben. Also laste mir das nicht an.
Oliver S. schrieb:> compilieren mit adress sanitizer
Und das ist ein Strohmann Argument, da du auf normalem Wege nicht weiter
kommst.
Bradward B. schrieb:> Literaturstelle, bitte?
C-Standard Draft n3220:
3.5.3
1 undefined behavior
behavior, upon use of a nonportable or erroneous program construct or of
erroneous data, for which this document imposes no requirements
2 Note 1 to entry: Possible undefined behavior ranges from ignoring the
situation completely with unpredictable results, to behaving during
translation or program execution in a documented manner characteristic
of the environment (with or without the issuance of a diagnostic
message), to terminating a translation or execution (with the issuance
of a diagnostic message)
6.5.4.2 Address and indirection operators
If an invalid value has been assigned to the pointer,
the behavior of the unary * operator is undefined.
Bradward B. schrieb:> "Error", "Warning" ist doch hier im Sinne von Compiler-Meldungen> gemeint.
Für dich vielleicht. Aber es gibt wie gesagt auch andere Fehler.
Bradward B. schrieb:> Und solange der Compiler einen assembler-code für den Zugriff> auf diese Address generieren kann, ist programmiertechnisch alles> akzeptabel, auch wenn es während der runtime "krachen" sollte.
Was ist "programmiertechnisch akzeptabel"? Wenn der C-Standard etwas als
"undefined behaviour" und daher als "erroneous" deklariert, kann man
kein sinnvolles oder vorhersehbares Ergebnis erwarten. Es kann zwar sein
dass manche Compiler ausnahmsweise bestimmte Fälle dieser Art doch
wohldefiniert umsetzen, aber sich darauf zu verlassen ist gefährlich.
Mir ist kein C-Compiler bekannt, der für Array-Out-Of-Bounds Zugriffe
ein konsistentes Verhalten definiert, nicht einmal einen Absturz.
Ein Quellcode, der kein konsistentes sinnvolles Verhalten liefern kann
und für den in der Sprachspezifikation und in der Compiler-Dokumentation
kein Verhalten gegeben ist, mag für dich vielleicht
"programmiertechnisch akzeptabel" sein, aber selbst der C-Standard nennt
dies "erroneous" und für mich und den Rest der Welt ist das ein Fehler,
auch wenn der Compiler dafür einen (sinnlosen) Assemblercode
generieren kann. Was korrekt ist und was nicht wird nur vom C-Standard
vorgegeben, und nicht ob irgendein Compiler dafür irgendeinen
Assemblercode für ausspucken kann.
Wenn im Code eine nicht-existente Funktion aufgerufen wird, was der
Linker mit "undefined reference" quittiert, ist das für dich
"programmiertechnisch akzeptabel" weil der Compiler keine Fehlermeldung
ausgibt? Was ist mit Division durch Null, oder Dereferenzierung von
Null-Pointern?
Der GCC könnte für völlig vermurksten Code auch zufällige
Assembler-Instruktionen rauswerfen, dadurch wird der Code noch lange
nicht korrekt.
C ist eben mehr als nur der Compiler und die Übersetzung in
Assemblercode. C definiert auch ein bestimmtes Laufzeitverhalten. Ein
korrektes C-Programm liefert immer das gleiche, vorhersehbare
Ergebnis. Also genau das, was man von einem Computer erwartet. Würde C
nur eine Syntax vorgeben die in Assembler übersetzt wird, ohne ein
Laufzeitverhalten vorzugeben, wäre die Sprache ziemlich nutzlos - was
will man mit einer Sprache, bei der "2+2" immer mal etwas
unterschiedliches ausgibt? Korrekte, und nur korrekte, Programme
liefern konsistente Ergebnisse. Ein fehlerhaftes Programm liefert
irgendwas, das ist nutzlos und wird somit allgemein als Fehler
angesehen, und nicht als "programmiertechnisch akzeptabel".
Oliver S. schrieb:> Gerade noch die anderen probiert. Alle auf godbolt vorhandenen gccs,> also 4-16, bringen die Warnungen.
Und wieder einmal zeigt sich die Gültigkeit des Sprichwortes:
Lügen haben kurze Beine!
Ich sehe da – wie die ganze Zeit von mir behauptet – nur EINE Warnung
und nicht DREI!
Niklas G. schrieb:> Wenn der C-Standard etwas als> "undefined behaviour" und daher als "erroneous" deklariert, kann man> kein sinnvolles oder vorhersehbares Ergebnis erwarten.
Der STandard definiert den Begriff. Nich was erroneous ist. Da hätte er
viel zu tun um alle möglichen Fehler abzudecken.
Wie ein Kollege zu sagen pflegte, wenn das Programm erstmalig compiliert
wurde "Formal ist es schon mal richtig"
Nick schrieb:> Nich was erroneous ist. Da hätte er> viel zu tun um alle möglichen Fehler abzudecken.
Was undefiniert ist, ist erroneous. Manche Sachen sind explizit als
undefiniert angegeben. Alles was nicht definiert ist, ist logischerweise
undefined.
Nick schrieb:> Wie ein Kollege zu sagen pflegte, wenn das Programm erstmalig compiliert> wurde "Formal ist es schon mal richtig"
Stimmt halt nicht. Höchstens syntaktisch korrekt. Der C-Standard
definiert formal die Korrektheit nicht nur auf Basis der Syntax sondern
auch "inhaltlich". Diese "inhaltlichen" Definitionen sind durchaus
komplex, d.h. das Zusammenspiel mehrerer einzelner Elemente (z.B.
Array-Definition und Array-Zugriff) können fehlerhaft sein, auch wenn
die einzelnen Teile nicht unbedingt fehlerhaft sein müssen. Und genau
deswegen kann der Compiler solche Probleme nicht im Allgemeinen finden.
Ist ein Programm, das laut C-Standard "erroneous" ist, aber von einem
Compiler in Assemblercode übersetzt wird, von einem anderen Compiler
(oder sogar dem selben Compiler mit anderen Optionen oder in anderer
Version) aber mit Fehlermeldung quittiert wird, "korrekt"?
…und mit auskommentierter Zeile 14 dann üblerweise gar keine Fehler
mehr.
Also lieber Oliver S., erzähl mir nicht was sich auf meinem Rechner
abspielt wenn du die gleichen Fehler mit godbold problemlos hättest
nachstellen können.
Nick schrieb:> Niklas G. schrieb:>> Wenn der C-Standard etwas als>> "undefined behaviour" und daher als "erroneous" deklariert, kann man>> kein sinnvolles oder vorhersehbares Ergebnis erwarten.
Doch kann man, " ... (or) to behaving during
translation or program execution in a documented manner characteristic
of the environment (or) ... " Also das kann funktionieren, muss aber
nicht, das kann komplett wegfallen oder es fehlerhaft mit ungewollten
Ergebnissen durchlaufen. Eben "undefined", russisch Roulette auf der
black box.
> Der STandard definiert den Begriff. Nich was erroneous ist. Da hätte er> viel zu tun um alle möglichen Fehler abzudecken.>>> Wie ein Kollege zu sagen pflegte, wenn das Programm erstmalig compiliert> wurde "Formal ist es schon mal richtig"
Eben. Und danke für den Auszug, das ist mal 'ne sachliche Grundlage.
Vielleicht beziehe ich mich auf eine ältere Definition von C aber
Zugriffe auf nicht deklarierte Feldelemente sind in C an sich nicht
verboten. Es wird davon abgeraten, weil das Verhalten je nach Maschine
unterschiedlich sein kann. Vielleicht hat man ja Gründe für "dirty
tricks", C will ja dem Programmierer nicht die Kontrolle über die
Maschine streitig machen (das wollen die Programmiersprachen aus dem
Bereich "betreutes Programmieren") sondern erleichtern. Vielleicht will
man ja auch das memory managment der Maschine testen, wie es sich bei
solchen Zugriffen verhält - kackt das System ab oder reagiert es
"robust" auf solche "Speicherschreiber".
Deshalb gibt es ja auch Ergänzungen wie MISRA die aus dem Hackmesser "C"
ein kindersicheres Werkzeug machen wollen, also dem Löwen die Zähne
ziehen um ihn zum "Plüschtier" zu entschärfen.
> "Error", "Warning" ist doch hier im Sinne von Compiler-Meldungen> gemeint.>> Für dich vielleicht. Aber es gibt wie gesagt auch andere Fehler. "erroneous"
Das thread-thema lautet "gcc veralbert", das wird hier durch die Bank
auf den Compiler bezogen, nicht auf C an sich.
Bradward B. schrieb:> Also das kann funktionieren
Aber nur, wenn die Plattform (inkl. Compiler) das entsprechend
spezifiziert. Aber bei Array-Out-Of-Bound ist das bei keinem Compiler
der Fall.
Bradward B. schrieb:> Zugriffe auf nicht deklarierte Feldelemente sind in C an sich nicht> verboten
Was ist für dich "verboten"?
Es ist "undefined behaviour". Noch verbotener wäre nur wenn der
C-Standard vom Compiler verlangen würde, immer sofort mit
Fehlermeldung zu quittieren - da das aber nicht möglich ist (Satz von
Rice etc), kann der Standard es nur als UB deklarieren.
Bradward B. schrieb:> Es wird davon abgeraten, weil das Verhalten je nach Maschine> unterschiedlich sein kann.
Von welchen Undefined Behaviour Dingen würdest du denn nicht abraten?
Bradward B. schrieb:> C will ja dem Programmierer nicht die Kontrolle über die> Maschine streitig machen
C abstrahiert die Programmierung von der konkreten Plattform, was
notwendigerweise mit einem Kontrollverlust einhergeht. Gerade in Bezug
auf Speicherzugriffe sind manche Dinge in C schlicht nicht möglich.
Bradward B. schrieb:> Vielleicht will> man ja auch das memory managment der Maschine testen
Dafür ist C nicht gemacht. Wie gesagt kann der Compiler solche wilden
Speicherzugriffe auch einfach aus dem Code verschwinden lassen ohne den
C-Standard zu verletzen - damit ist C nicht dazu geeignet um irgendwo im
Speicher herumzuporkeln.
Bradward B. schrieb:> Programmiersprachen aus dem> Bereich "betreutes Programmieren"
Die können letztendlich genau das gleiche wie C, da all diese Sprachen
turing-vollständig sind. C kann vielleicht manche Sachen schneller, was
aber damit erkauft wird, dass viele Fehler nicht so leicht ersichtlich
sind. Paradoxerweise ist z.B. FORTRAN teilweise noch schneller als C,
gerade weil es manche Schweinereien verbietet.
Bradward B. schrieb:> Das thread-thema lautet "gcc veralbert", das wird hier durch die Bank> auf den Compiler bezogen, nicht auf C an sich.
Selbst der GCC meckert den Arrayzugriff je nach Optionen an, kann also
nicht so besonders korrekt sein.
> Dafür ist C nicht gemacht. Wie gesagt kann der Compiler solche wilden> Speicherzugriffe auch einfach aus dem Code verschwinden lassen ohne den> C-Standard zu verletzen - damit ist C nicht dazu geeignet um irgendwo im> Speicher herumzuporkeln.
Schön wärs, aber C kennt IMHO keinerlei Beschränkung bezüglich
Speicherzugriffe.
Zwar lässt C gern mal MEMORY-mapped IO Zugriffe gerne mal bei gut
gemeinter aber schlecht gemachter Optimierung aus, aber nur so lange bis
man das keyword "volatile" gelernt hat. Ist gerade bei µC-Programmierung
somit das wichtigste keyword.
Bradward B. schrieb:> Schön wärs, aber C kennt IMHO keinerlei Beschränkung bezüglich> Speicherzugriffe.
Dann kompilier mal mit "-fsanitize=undefined" . Nur weil C das nicht
immer verhindert, heißt das nicht, dass C da besonders für geeignet
wäre. Nur weil man mit einem Golf schon gegen einen Brückenpfeiler
fahren kann, ist der Golf nicht dafür konzipiert oder besonders effektiv
dabei, Brücken abzureißen. Empfehlen würde ich das auch nicht.
Bradward B. schrieb:> Zwar lässt C gern mal MEMORY-mapped IO Zugriffe gerne mal bei gut> gemeinter aber schlecht gemachter Optimierung aus, aber nur so lange bis> man das keyword "volatile" gelernt hat.
volatile ist aber ein grober Hammer, mit dem man auch nicht alles
erreichen kann.
Bradward B. schrieb:> Ist gerade bei µC-Programmierung> somit das wichtigste keyword.
Komisch, manche Leute schaffen es komplexe Mikrocontroller-Anwendungen
in C oder C++ ganz ohne volatile zu schreiben...
Niklas G. schrieb:> Bradward B. schrieb:>> Schön wärs, aber C kennt IMHO keinerlei Beschränkung bezüglich>> Speicherzugriffe.>> Dann kompilier mal mit "-fsanitize=undefined" . Nur weil C das nicht> immer verhindert, heißt das nicht, dass C da besonders für geeignet> wäre.
Doch C ist schon ziemlich hardwarenah, das wurde ja extra für
hardwarenahe Sachen gemacht. Und die Option sanitize ist ne ziemlich
neumodische Sonderlocke, wurde meines Wissens 2013 eingeführt.
* https://github.com/google/sanitizers/wiki/AddressSanitizer> Nur weil man mit einem Golf schon gegen einen Brückenpfeiler> fahren kann, ist der Golf nicht dafür konzipiert oder besonders effektiv> dabei, Brücken abzureißen.
C würde ich jetzt nicht mit ner Studentschleuder wie den Golf
vergleichen, auch wenn man beides öfters an Hochschulen sieht. C ist
unter den Programmiersprachen schon so "geländegängig" wie ein Unimog.
und nicht jeder Student ist ein Warmduscher oder ein "real programmer"
;-) https://thehardcorecoder.com/2014/03/07/real-programmers/> Komisch, manche Leute schaffen es komplexe Mikrocontroller-Anwendungen> in C oder C++ ganz ohne volatile zu schreiben...
Klar, wenn man die Optimierung disabled, oder nicht direkt auf die
IO-Ports zugreift. C ist halt Assembler mit ein bißchen zuckerguß
drüber.
Bradward B. schrieb:> Doch C ist schon ziemlich hardwarenah, das wurde ja extra für> hardwarenahe Sachen gemacht.
Es wurde nicht dafür gemacht, um mit sinnvollen Ergebnissen irgendwo in
den Speicher schreiben zu können.
Bradward B. schrieb:> Und die Option sanitize ist ne ziemlich> neumodische Sonderlocke, wurde meines Wissens 2013 eingeführt.
Durch die Einführung von einzelnen Features in einzelnen Compilern
ändert sich aber nicht die Bedeutung des Standards. Vielmehr waren wilde
Zugriffe schon immer falsch, der GCC kann sie jetzt nur besser
aufspüren.
Bradward B. schrieb:> C ist> unter den Programmiersprachen schon so "geländegängig" wie ein Unimog.
Altmodisch und ineffizient?
Bradward B. schrieb:> Klar, wenn man die Optimierung disabled, oder nicht direkt auf die> IO-Ports zugreift.
Sorry, ich hab was durcheinander geworfen, volatile ist nicht nötig für
die Synchronisierung von Variablenzugriffen zwischen main() und ISR's
(auch mit Optimierungen), für memory-mapped Register gibt es zwar auch
andere Möglichkeiten aber die sind nicht wirklich praktikabel.
Dennoch ist volatile eine Krücke, eigentlich bräuchte C hier ein
präziseres Werkzeug, aber weil C eben nicht für direktes Rumbasteln am
Speicher gemacht ist wird's das wohl nicht geben. Tatsächlich gibt es
sogar Pläne, "volatile" ganz zu entfernen...
Bradward B. schrieb:> C ist halt Assembler mit ein bißchen zuckerguß> drüber.
Das ist übervereinfacht.
Oliver S. schrieb:> Mag sein. Ich sehe drei. Und nun?
Da man davon ausgehen darf, dass es nicht mehrere unterschiedlich
arbeitende Godbolt Seiten gibt…
UND da ich bereits mehrere Screenshots von nur EINEM Fehler bzw. KEINEM
Fehler zur Verfügung gestellt habe…
UND da alle Anderen diese Situation ebenfalls problemlos nachvollziehen
können, wäre es nun an der Zeit über eine schwerwiegende neuronal
bedingte Komponente nachzudenken.
Denn etwas dreifach zu sehen was für alle nachvollziehbar nur einfach da
ist, sollte man nicht auf die leichte Schulter nehmen.
Niklas G. schrieb:> Aber bei Array-Out-Of-Bound ist das bei keinem Compiler> der Fall.
Wenn ich weiß, dass auf der Architektur immer in 4 Byte Schritten
allokiert wird (word aligned), dann kann ich auch gefahrlos über das
Array hinaus schreiben/lesen wenn das z.B. char ist und ich innerhalb
der Grenzen bleib.
Das ist Korinthengekacke.
Norbert schrieb:> splint -strict nutzen
Okay, ein Linter ist natürlich immer eine feine Sache, um ein Problem so
früh wie möglich zu entdecken. Hier bei uns werden zusätzlich dazu noch
die Unit-, Integrations- und Systemtests vor Releases mit den Werkzeugen
der Valgrind-Suite überprüft. Wir hatten schon mehrere Fälle, in denen
damit Fehler gefunden wurden, die den Lintern zuvor entgangen waren.
Nick schrieb:> dann kann ich auch gefahrlos über das> Array hinaus schreiben/lesen wenn das z.B. char ist und ich innerhalb> der Grenzen bleib.
Welche Architektur allokiert alles an Word-Grenzen? Die gängigen 32&64
bitter nicht, die würden dort durchaus andere einzelne char-Variablen
ablegen. Was wenn der Compiler dann doch sieht dass du über die Grenze
hinweg zugreifst und den Zugriff ganz wegoptimiert weil er davon
ausgehen darf, dass UB nie auftritt? Was wenn der Compiler ein
Canary-Byte dort ablegt?
>> Doch C ist schon ziemlich hardwarenah, das wurde ja extra für>> hardwarenahe Sachen gemacht.>> Es wurde nicht dafür gemacht, um mit sinnvollen Ergebnissen irgendwo in> den Speicher schreiben zu können.
"Sinn" ist keine Eigenschaft eines Codes, Determiniertheit ist eine.
Und ja (Arbeits-)Speicherzugriffe sind determiniert, man liest was darin
geschrieben wurde. Ob der Speicher genüg Zellen hat, interessiert den
Compiler nicht, das muss der C-Programmierer selber checken resp. der
User sicherstellen.
>> Und die Option sanitize ist ne ziemlich>> neumodische Sonderlocke, wurde meines Wissens 2013 eingeführt.>> Durch die Einführung von einzelnen Features in einzelnen Compilern> ändert sich aber nicht die Bedeutung des Standards. Vielmehr waren wilde> Zugriffe schon immer falsch, der GCC kann sie jetzt nur besser> aufspüren.
Was sind "wilde Speicher-Zugriffe" ? Das definiert jeder anders.
"Falsch/Richtig" ist auch so ein eigentlich fachfremder Begriff.
"Division durch Null" ist nicht "falsch" sondern "Undefiniert".
Und nochmals "Wilde Speicherzugriffe" mögen in den meisten Scenarion
unerwünscht sein, sie sind aber möglich. Diverse Sicherheitslücken
basieren auf "wilde Zugriffe" (bspw "RAW-Hammer").
Der Gedanke, ein System zu härten, indem man "wilde Zugriffe" und
ähnlichen "Ungemach§ einfach verbietet, ist zwar naheliegend aber
unrealistisch.
BTW: "Raw Hammer" beruht auf der indirekten Beeinflußung benachtbarter
(wenige Namometer) Speicherzellen. Man "hämmert" solange auf eine
Speicherzelle mit Zugriffen (Lesen würde genügen) ein bis die Ladung der
Nachbarn (auf die man keinen Zugriff hat) sich verändert.
https://www.heise.de/hintergrund/Der-Hammer-mit-dem-Rowhammer-2764420.html
Norbert schrieb:> Da man davon ausgehen darf, dass es nicht mehrere unterschiedlich> arbeitende Godbolt Seiten gibt…>> UND da ich bereits mehrere Screenshots von nur EINEM Fehler bzw. KEINEM> Fehler zur Verfügung gestellt habe…
Die deinen Horizont begrenzende Untertasse scheint etwas klein zu sein.
Bei mir kommen, wie ich schon mehrfach schrieb, bei gcc4-gcc 16 immer
Warnungen zu Index 5 und Index 6.
Oliver
Niklas G. schrieb:> Welche Architektur allokiert alles an Word-Grenzen? Die gängigen 32&64> bitter nicht, die würden dort durchaus andere einzelne char-Variablen> ablegen.
Da muss ich die Frage umkehren: Wer macht das, dass nicht an
Wort-Grenzen allokiert wird?
Ich seh schon, woraus du hinaus willst. Wenn 1 char allokiert wird, dann
braucht das bei 32 Bittern üblicherweise 8 Bytes. 4 Bytes für den
pointer auf den nächsten alloc-block und 4 bytes für den einen char.
Allokiert man 2 chars in einem array, bleibt es bei den 8 bytes. 4 char
array immer noch 8 bytes, 5 char array dann 12 bytes.
Gilt bei 32 bit Adressraum! Wenn der Adressraum über der Wortgrenze ist,
dann ist der pointer auf den nächsten Block natürlich größer.
Dass ich keine andere Methode der allokation kenne bedeutet nicht, dass
andere Plattformen (>= 16 bit) es anders machen und ganz bewusst
mis-aligned arbeiten um dadurch langsamer zu werden.
Niklas G. schrieb:> Was wenn der Compiler dann doch sieht dass du über die Grenze> hinweg zugreifst und den Zugriff ganz wegoptimiert weil er davon> ausgehen darf, dass UB nie auftritt?
Langsam! Es ist nicht die Aufgabe eines C-Compilers, den Unsinn des
Programmierers abzufangen. Es muss lediglich garantieren, dass richtiger
code auch richtig funktioniert. Wenn absichtlich/unabsichtlich Unsinn
gemacht wird, kann das dem Compiler egal sein.
Dass Compiler solche zusätzlichen Analysen machen, steht nicht im
C-Standard. Das ist nur eine freundliche Zugabe. lint,
statische/dynamische code-Analyse, bounds-checking, ... steht alles
nicht im Standard. C ist keine "betreute Progammiersprache" (ich find
die Bezeichnung witzig). Es ist und bleibt ein "Hochsprachen-Assembler"
der extrem gefährlich ist. Das ist einfach der Preis, den man für
Effektivität und Geschwindigkeit zahlt. Das ist auch die Motivation für
Rust gewesen (mMn). Und auch, schon ewig her, für Obj-C, das die
alloc-Geschichte anders handhabt (wobei ich das inzwischen vergessen hab
wie genau das war). Wie sich das borrow-Konzept und die (genialen)
slices in Rust auf die Performance und code-Größe auswirken weiß ich
schlicht nicht.
Letztendlich ist das auch für C egal. Es garantiert, dass richtiger code
richtig funktioniert. Falscher code ist in der Verantwortung des
Missbrauchenden.
Oliver S. schrieb:> Bei mir kommen, wie ich schon mehrfach schrieb, bei gcc4-gcc 16 immer> Warnungen zu Index 5 und Index 6.
Dann lebst du in einem Paralleluniversum.
(Und deine blöden Sprüche darfst du dir gerne hinschieben wo die Sonne
nie scheint. Falls da noch Platz ist.)
Rbx schrieb:> Seiten wie hier durcharbeiten:> https://www.wackerart.de/c.html
Der Typ hat eine Abneigung gegenüber C (schreibt er selbst) und er hat
nicht verstanden und will nicht verstehen warum C so ist wie es ist.
Nemopuk schrieb:>> Allokiert man 2 chars in einem array, bleibt es bei den 8 bytes.>> Nein
Dann ist es also so, dass ein char pointer in ein Array nach einem
increment 4 Bytes weiter zeigt. Oder ein pChar + sizeof(char) äquivalent
zu pChar + 4 ist. Aber sizeof(char) trotzdem 1 ist. Und folglich muss
unterschieden werden, ob der ponter in ein statisches oder in ein
allokiertes array zeigt.
Oder irgendwie so.
Alexander S. schrieb:> Der Typ hat eine Abneigung gegenüber C (schreibt er selbst) und er hat> nicht verstanden und will nicht verstehen warum C so ist wie es ist.
So einen haben wir hier auch. Der nannte sich früher "C-Hater".
Alexander S. schrieb:> Der Typ hat eine Abneigung gegenüber C (schreibt er selbst) und er hat> nicht verstanden und will nicht verstehen warum C so ist wie es ist.
Ojojojoj!
Die Einführung hat mir schon genügt. Die ist wirklich lesenswert,
notfalls bitte nachholen.
Fortran konnte ich ein bissl, hat mich immer an naturwissenschaftliches
Basic erinnert. Ja, Fortran ist in manchen Bereichen wirklich mächtig
(gewesen). Pascal mochte ich sehr, das war nach Basic meine erste
sinnvolle Programmiersprache. Ada ist OK.
Wer labern will, nimmt Cobol.
Das sind Sprachkonzepte die teilweise leider, teilweise
erfreulicherweise untergegangen sind. Basic, Modula2, Eiffel, Cobol,
Objective-C, PL-sql (**würg**).
Um das mal wieder zu bemerken: Ich hatte eine massive Abneigung
gegenüber C.
Norbert schrieb:> Dann lebst du in einem Paralleluniversum.
Natürlich. In einem, in dem es Gründe für so etwas gibt, und man, wenn
man möchte, diese auch suchen und finden kann, anstatt
kindergartenmässing rumzublöken.
Schau dir deinen Code an, schau dir den Originalcode oben an, und finde
die Unterschiede. Das Thema hier ist nicht irgend ein Code, sondern der
des TO. Und dann kannst du darüber nachdenken, was z.B. der Optimierer
alles mit Variablen anstellt, die zwar geschrieben, aber nie benutzt
werden. Oder was es noch für Gründe geben könnte.
In diesem Sinne
Oliver
Niklas G. schrieb:> Mir ist keine Plattform bekannt, die lokale Variablen auf einen Heap> legen würde.
IBM S/390 hat keinen Stack Pointer in Hardware implementiert.
Nick schrieb:> Dann ist es also so, dass ein char pointer in ein Array nach einem> increment 4 Bytes weiter zeigt.
Nein auch das nicht.
Ein char ist 1 Byte groß, egal welches Alignment der Compiler anwendet.
Wenn du einen Zeiger auf ein char inkrementierst, dann wird er um 1
erhöht. Und das passt auch gut zum char-array, denn innerhalb des Array
liegen die Zeichen Byte für Byte direkt hintereinander (ohne Lücken).
1
#include<stdio.h>
2
3
intmain(){
4
chara='A';
5
charb='B';
6
charc='C';
7
printf("a='%c', b='%c, c='%c'\n",a,b,c);
8
printf("addr von a='%0x', addr von b='%0x', addr von c='%0x'\n",&a,&b,&c);
9
10
char*p1=&a;
11
char*p2=p1;
12
p2++;
13
printf("p1=%0x, p2=%0x\n",p1,p2);
14
15
chard[]="Hallo";
16
printf("addr von H='%0x', addr von a='%0x', addr von l='%0x', addr von l='%0x', addr von o='%0x'\n",&d[0],&d[1],&d[2],&d[3],&d[4]);
17
}
Unter Linux ausgeführt kommt dies heraus:
1
$ gcc test.c && ./a.out
2
3
a='A', b='B, c='C'
4
addr von a='774fb05f', addr von b='774fb05e', addr von c='774fb05d'
5
p1=774fb05f, p2=774fb060
6
addr von H='774fb057', addr von a='774fb058', addr von l='774fb059', addr von l='774fb05a', addr von o='774fb05b'
Wie du siehst hat der Compiler die char Variablen ohne Lücke direkt
hintereinander gelegt (in rückwärts-Reihenfolge, warum auch immer).
Der Zeiger wurde um 1 inkrementiert.
Die Zeichen vom Array liegen direkt aufeinander folgend im Speicher.
Das war übrigens eine 64 Bit Maschine. Zum Vergleich mit erzwungenem
Alignment 8 (64 Bit):
1
intmain(){
2
chara__attribute__((aligned(8)))='A';
3
charb__attribute__((aligned(8)))='B';
4
charc__attribute__((aligned(8)))='C';
5
printf("a='%c', b='%c, c='%c'\n",a,b,c);
6
printf("addr von a='%0x', addr von b='%0x', addr von c='%0x'\n",&a,&b,&c);
7
8
char*p1=&a;
9
char*p2=p1;
10
p2++;
11
printf("p1=%0x, p2=%0x\n",p1,p2);
12
13
chard[]__attribute__((aligned(8)))="Hallo";
14
printf("addr von H='%0x', addr von a='%0x', addr von l='%0x', addr von l='%0x', addr von o='%0x'\n",&d[0],&d[1],&d[2],&d[3],&d[4]);
15
}
Ergibt folgende Ausgabe:
1
$ gcc test.c && ./a.out
2
3
a='A', b='B, c='C'
4
addr von a='11c42098', addr von b='11c42090', addr von c='11c42088'
5
p1=11c42098, p2=11c42099
6
addr von H='11c42080', addr von a='11c42081', addr von l='11c42082', addr von l='11c42083', addr von o='11c42084'
Dieses mal wurden die drei char Variablen an 64 Bit Adressen
ausgerichtet.
Der Zeiger wurde dennoch um 1 inkrementiert.
Die Zeichen vom Array liegen wieder direkt aufeinander folgend im
Speicher.
Um es nochmal klipp und klar zu sagen:
1) sizeof(char) ist immer 1.
2) wenn du einen Zeiger auf ein char inkrementierst, dann wird er um 1
erhöht.
3) in einem Array liegen alle Zeichen direkt hintereinander.
4) das ist völlig unabhängig vom Alignment.
Nemopuk schrieb:> Nick schrieb:>> Dann ist es also so, dass ein char pointer in ein Array nach einem>> increment 4 Bytes weiter zeigt.>> Nein auch das nicht.
Es ging ums allokieren, hast du wohl überlesen. Also malloc.
Dein Elaborat ist also hier sinnlos.
Nick schrieb:> Es ging ums allokieren, hast du wohl überlesen.
Ich habe
> dass ein char pointer in ein Array nach einem increment 4 Bytes weiter zeigt
gelesen
Ich habe wohl zu viel geschrieben. Hier ein kürzeres Beispiel dass genau
diesen einen Fall zeigt:
1
#include<stdio.h>
2
intmain(){
3
4
chard[]__attribute__((aligned(8)))="Hallo";
5
6
char*p1=d;
7
char*p2=p1;
8
p2++;
9
printf("addr von d=%0x, p1=%0x, p2=%0x\n",d,p1,p2);
10
}
Ausgabe:
1
$ gcc test.c && ./a.out
2
3
addr von d=90743ee8, p1=90743ee8, p2=90743ee9
p2 ist NICHT "4 Bytes weiter".
Lass uns nochmal auf den vorherigen Punkt zurück kommen, denn ich
glaube, dort haben wir ein Missverständnis:
Nick schrieb:> Allokiert man 2 chars in einem array, bleibt es bei den 8 bytes. 4 char> array immer noch 8 bytes, 5 char array dann 12 bytes.
War wohl so gemeint:
1
chara[2]__attribute__((aligned(4)))="H";
2
charb[4]__attribute__((aligned(4)))="Hal";
3
charc[5]__attribute__((aligned(4)))="Hall";
4
inti;
Du wolltest wohl darauf hinaus, dass der Compiler hinter den Arrays
Platz reserviert, damit die nächste Variable an der nächsten 32 Bit
Adresse ausgerichtet ist.
Aber das muss nicht immer sein, denn es könnte andere Variablen geben,
die nicht an 32 Bit ausgerichtet sein müssen. Ein char zum Beispiel.
Ich meine bei Cortex-M ist es z.B. so, dass 16 Bit Variablen an 16 Bit
Adressen ausgerichtet werden und 32 Bit Variablen an 32 Bit Adressen.
Aber 8 Bit Variablen können überall stehen und eben diese Lücken füllen.
Nemopuk schrieb:> Ich habe>> dass ein char pointer in ein Array nach einem increment 4 Bytes weiter zeigt> gelesen
Du hast gleichzeitig den Sarkasmus überlesen. Lies den Beitrag nochmal
in Ruhe durch.
Beitrag "Re: Fühlt sich der gcc hier veralbert?"
und geh notfalls einen Schritt zurück auf deinen dort zitierten Beitrag.
> Du hast gleichzeitig den Sarkasmus überlesen
Irgendwas kapiere ich immer noch nicht. Aber egal, davon geht die Welt
nicht unter.
Da ich gerade ein STM32 Board mit Cortex-M33 hier liegen habe:
b=2000008d 3 Bytes incl. 2 Füllbytes, keine 32 Bit Ausrichtung
2
fuenf=20000090 8 Bytes incl. 3 Füllbytes
3
vier=20000098 4 Bytes, keine Füllbytes
4
zwei=2000009c 2 Bytes, keine Füllbytes
5
a=2000009e keine 32 Bit Ausrichtung
Wie man sieht, sind die Arrays an 32 Bit Grenzen ausgerichtet, die
einzelnen chars aber nicht. Und die Größe der Arrays ist (samt
Füllbytes) dem zufolge nicht immer ein Vielfaches von 4.
Nemopuk schrieb:> Und die Größe der Arrays ist (samt> Füllbytes) dem zufolge nicht immer ein Vielfaches von 4.
malloc ist dir wirklich kein Begriff? Es ging um die Größe der
allokierten (hint: mit malloc) Blöcke. Und die sind üblicherweise aus
Performancegründen Vielfache der Wortgröße mit n >= 2. Nochmal: Wenn der
Adressraum größer ist, dann ist der pointer auf den Folgeblock nicht von
der Wortbreite abhängig.
Das mit dem "Vielfaches von 4" hast du dir unterwegs dazufantasiert.
Ich glaub, ich hab genug dazu gesagt.
Nick schrieb:> Nemopuk schrieb:>> Mir war nicht klar, dass es dir nur um malloc() ging.>> Wie oft ich "allokiert" geschrieben hab, kannst du selbst nachzählen.
Auch die Allokation von Speicher auf dem Stack für lokale Variablen
durch den Compiler wird durchaus als solche bezeichnet, nicht nur ein
malloc auf dem Heap. Versuch dich mal an die Compilerbau-Vorlesungen
zurück zu erinnern. ;-)
Ralf D. schrieb:> Auch die Allokation von Speicher auf dem Stack für lokale Variablen> durch den Compiler wird durchaus als solche bezeichnet
Wenn man da Beitrag "Re: Fühlt sich der gcc hier veralbert?" was
anderes wie malloc reininterpretiert, muss man schon arg verbohrt sein.
Nick schrieb:> Es ging ums allokieren, hast du wohl überlesen. Also malloc.
Und wenn man es danach immer noch nicht verstanden hat, kann ich auch
nicht mehr helfen.