Forum: PC-Programmierung Fühlt sich der gcc hier veralbert?


von Bauform B. (bauformb)


Lesenswert?

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?
1
static void
2
foo (const char *bozo_exe)
3
{
4
   char   *args[16], *envs[4];
5
6
   printf ("Starting %s...\n", bozo_exe);
7
   asprintf (&envs[ 0], "HOME=%s",  "/");
8
   asprintf (&envs[ 1], "PWD=%s",   "/");
9
   asprintf (&envs[ 2], "LANG=%s",  "C");
10
   asprintf (&envs[ 3], "USER=%s",  cf.uid);
11
   asprintf (&envs[ 4], "PATH=%s",  "/sbin:/usr/sbin:/bin:/usr/bin");
12
   asprintf (&envs[ 5], "SHELL=%s", "/bin/sh");
13
   envs[ 6] = NULL;
14
15
   args[ 0] = strdup (bozo_exe);
16
   args[ 1] = strdup ("-P");
17
// ...
18
   if (execve (args[0], args, envs)) {
19
// ...
20
   }
21
}
$ gcc --version

gcc (Debian 15.3.0-1) 15.3.0
: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Bauform B. schrieb:
> Kann man dagegen etwas tun?

Python verwenden
von Norbert (der_norbert)


Lesenswert?

splint -strict nutzen
1
unbenannt.c:26:15: Possible out-of-bounds read: envs[5]
2
    Unable to resolve constraint:
3
    requires maxRead(envs @ unbenannt.c:26:15) >= 5
4
     needed to satisfy precondition:
5
    requires maxRead(envs @ unbenannt.c:26:15) >= 5
6
  A memory read references memory beyond the allocated storage. (Use
7
  -boundsread to inhibit warning)
8
unbenannt.c:28:4: Likely out-of-bounds store: envs[6]
9
    Unable to resolve constraint:
10
    requires 3 >= 6
11
     needed to satisfy precondition:
12
    requires maxSet(envs @ unbenannt.c:28:4) >= 6
13
  A memory write may write to an address beyond the allocated buffer. (Use
14
  -likelyboundswrite to inhibit warning)
von Mikro 7. (mikro77)


Lesenswert?

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."

WTF? -.-

Beispiel:

https://godbolt.org/z/qc647cjdv
von Bradward B. (Firma: Starfleet) (ltjg_boimler)


Lesenswert?

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.
: Bearbeitet durch User
von Rbx (rcx)


Lesenswert?

Bauform B. schrieb:
> Kann man dagegen etwas tun?

Anderen Compiler nehmen - oder mach es lieber gleich in Assembler. Wenn 
man lieber was lesen will:
https://blogs.oracle.com/linux/closing-a-hole-in-the-detection-of-buffer-overflows-with-gcc

edit: Rust wäre natürlich auch noch eine Empfehlung.
: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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.
von Nemopuk (nemopuk)


Angehängte Dateien:

Lesenswert?

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.
: Bearbeitet durch User
von Bradward B. (Firma: Starfleet) (ltjg_boimler)


Lesenswert?

>> 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.
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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).
: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

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
von Nick (b620ys)


Lesenswert?

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.
von Norbert (der_norbert)


Lesenswert?

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.
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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.
von Bradward B. (Firma: Starfleet) (ltjg_boimler)


Lesenswert?

>> 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.
: Bearbeitet durch User
von Norbert (der_norbert)


Angehängte Dateien:

Lesenswert?

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.
von Bradward B. (Firma: Starfleet) (ltjg_boimler)


Lesenswert?

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.
: Bearbeitet durch User
von Norbert (der_norbert)


Lesenswert?

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.
: Bearbeitet durch User
von Harald K. (kirnbichler)


Lesenswert?

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".
von Nick (b620ys)


Lesenswert?

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.
von Rbx (rcx)


Lesenswert?

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)
: Bearbeitet durch User
von Norbert (der_norbert)


Angehängte Dateien:

Lesenswert?

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.
von Rolf M. (rmagnus)


Lesenswert?

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.
von Norbert (der_norbert)


Lesenswert?

Rolf M. schrieb:
> Das wäre jetzt auch mein Vorschlag gewesen, mal die Optimierungen
> einzuschalten.

Optimierungen sind eingeschaltet.
1
$ grep "^:set make" ~/.vimrc 
2
:set makeprg=\(gcc\ -Wall\ -Wextra\ -pedantic\ -O3\ -std=c2x\ -save-temps\ -c\ %\ 2>&1\\\|tee\ errors\)
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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.
von Bradward B. (Firma: Starfleet) (ltjg_boimler)


Lesenswert?

> 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.
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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.
: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

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
von Norbert (der_norbert)


Lesenswert?

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.
von Norbert (der_norbert)


Lesenswert?

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.
: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

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
: Bearbeitet durch User
von Norbert (der_norbert)


Lesenswert?

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?"
: Bearbeitet durch User
von Bradward B. (Firma: Starfleet) (ltjg_boimler)


Lesenswert?

> * 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).
: Bearbeitet durch User
von Norbert (der_norbert)


Lesenswert?

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.
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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.
: Bearbeitet durch User
von Mikro 7. (mikro77)


Lesenswert?

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.
von Bauform B. (bauformb)


Lesenswert?

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.
von Oliver S. (oliverso)


Lesenswert?

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
von Bradward B. (Firma: Starfleet) (ltjg_boimler)


Lesenswert?

> 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.
: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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
const char * 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.
von Norbert (der_norbert)


Lesenswert?

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.
1
$ gcc -v |& grep "gcc version"
2
gcc version 10.2.1 20210110 (Debian 10.2.1-6) 
3
4
$ gcc -Wall -Wextra -pedantic -std=c2x -O3 -save-temps -c unbenannt.c
5
unbenannt.c: In function ‘main’:
6
unbenannt.c:14:5: warning: array subscript 5 is above array bounds of ‘char *[4]’ [-Warray-bounds]
7
   14 |     asprintf (&envs[5], "SHELL=%s", "/bin/sh");
8
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9
unbenannt.c:7:11: note: while referencing ‘envs’
10
    7 |     char* envs[4];
11
      |           ^~~~
12
13
$ vi unbenannt.c  # Zeile 14 auskommentiert.
14
15
$ gcc -Wall -Wextra -pedantic -std=c2x -O3 -save-temps -c unbenannt.c
16
~/tmp-share$

Kein Fehler mehr. GAR KEINER!
Und das trotz 13 und 15.
1
  1 #define _GNU_SOURCE
2
  2 #include <stdio.h>
3
  3 #include <string.h>
4
  4 
5
  5 static void foo (const char *bozo_exe) {
6
  6     char* args[16];
7
  7     char* envs[4];
8
  8     printf ("Starting %s...\n", bozo_exe);
9
  9     asprintf (&envs[0], "HOME=%s",  "/");
10
 10     asprintf (&envs[1], "PWD=%s",   "/");
11
 11     asprintf (&envs[2], "LANG=%s",  "C");
12
 12     asprintf (&envs[3], "USER=%s",  "cf.uid");
13
 13     asprintf (&envs[4], "PATH=%s",  "/sbin:/usr/sbin:/bin:/usr/bin");
14
 14     // asprintf (&envs[5], "SHELL=%s", "/bin/sh");
15
 15     envs[6] = NULL;
16
 16     args[0] = strdup (bozo_exe);
17
 17     args[1] = strdup ("-P");
18
 18     (void)args;
19
 19 }
20
 20 
21
 21 int main(void) {
22
 22     const char *abc = "ABC";
23
 23     foo(abc);
24
 24     return 0;
25
 25 }

Vielleicht iss ja jetzt langsam mal gut.
von Oliver S. (oliverso)


Lesenswert?

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
von Bradward B. (Firma: Starfleet) (ltjg_boimler)


Lesenswert?

> 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.
: Bearbeitet durch User
von Norbert (der_norbert)


Lesenswert?

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.
: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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".
von Norbert (der_norbert)


Angehängte Dateien:

Lesenswert?

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!
: Bearbeitet durch User
von Nick (b620ys)


Lesenswert?

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"
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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"?
: Bearbeitet durch User
von Norbert (der_norbert)


Angehängte Dateien:

Lesenswert?

…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.
von Bradward B. (Firma: Starfleet) (ltjg_boimler)


Lesenswert?

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.
: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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.
von Bradward B. (Firma: Starfleet) (ltjg_boimler)


Lesenswert?

> 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.
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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...
von Bradward B. (Firma: Starfleet) (ltjg_boimler)


Lesenswert?

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.
von Oliver S. (oliverso)


Lesenswert?

Norbert schrieb:
> Ich sehe da – wie die ganze Zeit von mir behauptet – nur EINE Warnung
> und nicht DREI!

Mag sein. Ich sehe drei. Und nun?

Oliver
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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.
: Bearbeitet durch User
von Norbert (der_norbert)


Lesenswert?

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.
von Nick (b620ys)


Lesenswert?

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.
von Sheeva P. (sheevaplug)


Lesenswert?

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.
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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?
von Bradward B. (Firma: Starfleet) (ltjg_boimler)


Lesenswert?

>> 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
: Bearbeitet durch User
von Oliver S. (oliverso)


Angehängte Dateien:

Lesenswert?

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
: Bearbeitet durch User
von Rbx (rcx)


Lesenswert?

Vielleicht nochmal das K&R C Buch durcharbeiten, und dann nochmal solche 
Seiten wie hier durcharbeiten:
https://www.wackerart.de/c.html
von Nick (b620ys)


Lesenswert?

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.
von Nemopuk (nemopuk)


Lesenswert?

Nick schrieb:
> Allokiert man 2 chars in einem array, bleibt es bei den 8 bytes.

Nein
von Norbert (der_norbert)


Angehängte Dateien:

Lesenswert?

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.)
von Alexander S. (alesi)


Lesenswert?

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.
von Nick (b620ys)


Lesenswert?

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.
von Harald K. (kirnbichler)


Lesenswert?

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".
von Nick (b620ys)


Lesenswert?

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.
von H. H. (Gast)


Lesenswert?

Harald K. schrieb:
> Der nannte sich früher "C-Hater".

Hat er einen neuen Namen?
von Oliver S. (oliverso)


Lesenswert?

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
von Harald K. (kirnbichler)


Lesenswert?

H. H. schrieb:
> Hat er einen neuen Namen?

Ja, das ist unser "Observer".
von Markus L. (rollerblade)


Lesenswert?

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.
von Nemopuk (nemopuk)


Lesenswert?

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
int main() {
4
  char a ='A';
5
  char b ='B';
6
  char c ='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
  char d[] ="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
int main() {
2
  char a __attribute__ ((aligned (8))) ='A';
3
  char b __attribute__ ((aligned (8))) ='B';
4
  char c __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
  char d[] __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.
: Bearbeitet durch User
von Nick (b620ys)


Lesenswert?

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.
von Nemopuk (nemopuk)


Lesenswert?

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
int main() {
3
4
  char d[] __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
char a[2] __attribute__ ((aligned (4))) ="H";
2
char b[4] __attribute__ ((aligned (4))) ="Hal";
3
char c[5] __attribute__ ((aligned (4))) ="Hall";
4
int i;

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.
: Bearbeitet durch User
von Nick (b620ys)


Lesenswert?

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.
von Nemopuk (nemopuk)


Lesenswert?

> 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:
1
#include <stdio.h>
2
#include "stm32c5xx.h"
3
#include "tusb.h"
4
5
...
6
7
char a;
8
char zwei[2];
9
char vier[4];
10
char fuenf[5];
11
char b;
12
13
void main_task(void) {
14
15
            ...
16
17
            char buf[100];
18
            sprintf(buf,"zwei=%0x vier=%0x fuenf=%0x a=%0x b=%0x\n",zwei,vier,fuenf,&a,&b);
19
            tud_cdc_n_write_str(0, buf);
20
21
            ...
22
}

Ausgabe:
1
zwei=2000009c vier=20000098 fuenf=20000090 a=2000009e b=2000008d

Ich bringe das mal in die richtige Reihenfolge:
1
    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.
: Bearbeitet durch User
von Nick (b620ys)


Lesenswert?

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.
von Nemopuk (nemopuk)


Lesenswert?

Mir war nicht klar, dass es dir nur um malloc() ging. Ich dachte es ging 
um das Alignment des Compilers bzw. der CPU.

Die malloc() Funktion der Newlib Nano verwendet offenbar ein 8 Byte 
Raster.

https://github.com/eblot/newlib/blob/2a63fa0fd26ffb6603f69d9e369e944fe449c246/newlib/libc/stdlib/nano-mallocr.c#L107

(Darüber habe ich mir nie Gedanken gemacht, weil es bisher immer 
"einfach so" zufriedenstellend funktionierte).
: Bearbeitet durch User
von Alexander S. (alesi)


Lesenswert?

Nemopuk schrieb:
1
 printf("addr von a='%0x', addr von b='%0x', addr von c='%0x'\n",&a,&b,&c);

Das Formatelement für Zeiger in printf ist '%p', nicht '%0x'. Mit 
-Wformat gibt der gcc mit Deinem Code eine Warnung aus.
1
print_ptr.c:13:25: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘char *’ [-Wformat=]
2
   13 |   printf("addr von a='%0x', addr von b='%0x', addr von c='%0x'\n",&a,&b,&c);
3
      |                       ~~^                                         ~~
4
      |                         |                                         |
5
      |                         unsigned int                              char *
6
      |                       %0s
von Nick (b620ys)


Lesenswert?

Nemopuk schrieb:
> Mir war nicht klar, dass es dir nur um malloc() ging.

Wie oft ich "allokiert" geschrieben hab, kannst du selbst nachzählen.
von Ralf D. (doeblitz)


Lesenswert?

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. ;-)
von Nick (b620ys)


Lesenswert?

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.
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.