Forum: Mikrocontroller und Digitale Elektronik Auf element außerhalb array zugreifen


von Felix (Gast)


Lesenswert?

Hallo,
ich erstelle ein array, mit 8 Elementen (in c).
Jetzt frage ich ausversehen das 10. Element ab. Da steht dann natürlich 
Müll drinn. Wo greife ich dann genau hin? Direkt auf den Wert in der 
nachfolgenden Adresse? Oder ganz woanders? Kann es Möglichkeiten geben, 
was zum Absturz oder so führen könnten?

von STK500-Besitzer (Gast)


Lesenswert?

Felix schrieb:
> Direkt auf den Wert in der
> nachfolgenden Adresse?

Ja.

Felix schrieb:
> Kann es Möglichkeiten geben,
> was zum Absturz oder so führen könnten?

Beim Lesen kann eher nichts passieren (kommt auf dein Programm an).
Schreiben kann da eher zu Problemen führen.

von Stefan F. (Gast)


Lesenswert?

Felix schrieb:
> Wo greife ich dann genau hin?

Das ist undefiniert. Bei einer einfachen linearen Speicheradressierung 
(wie sie Mikrocontroller meist haben) würdest du damit auf die 
Speicherzellen hinter dem Array zugreifen.

Bei Systemen mit Memory Mapping Unit ist das Ergebnis unbestimmt. Wenn 
du Glück hast, liegt dahinter Speicher, der deinem Programm gehört. Wenn 
nicht, führt der Versuch, auf fremden Speicher zuzugreifen womöglich zu 
einem Programmabbruch.

> Kann es Möglichkeiten geben, was zum Absturz oder so führen könnten?

Ja. Wie gesagt kann die Memory Mapping Unit bzw. das Betriebssystem den 
Zugriff verbieten. Bei vielen Mikrocontrollern sind auch I/O Register in 
den Speicher gemappt, bei denen sogar Lesezugriffe schon Seiteneffekte 
auslösen. Bei einem seriellen Port könnte ein Lesezugriff z.B. bewirken, 
dass das gelesene Byte aus einem Empfangs-Strom verloren geht.

von Nop (Gast)


Lesenswert?

Wenn der Compiler mitbekommt, daß Du da out of bounds zugreifst, kann er 
auch die ganze Funktion wegoptimieren, oder gleich das ganze Programm. 
Oder sonstwas für Code erzeugen.

von Axel S. (a-za-z0-9)


Lesenswert?

Felix schrieb:
> ich erstelle ein array, mit 8 Elementen (in c).
> Jetzt frage ich ausversehen das 10. Element ab.
> Wo greife ich dann genau hin? Direkt auf den Wert in der
> nachfolgenden Adresse?

Implementation defined. Oder auf gut Deutsch: hängt vom Compiler ab. Der 
Compiler ordnet deine Variablen im Speicher an. Weitgehend nach seinem 
Gusto. Typisch liegen alle globalen und statischen Variablen beieinander 
im Speicher (im Datensegment). Du greifst dann also auf eine andere 
Variable bzw. auch nur auf einen Teil einer anderen Variable zu.

> Kann es Möglichkeiten geben,
> was zum Absturz oder so führen könnten?

Auf µC eher nicht. Aber PC Betriebssysteme ergreifen Maßnahmen, um 
Programme voneinander abzuschotten. Wenn du beim Zugriff über das 
Arrayende hinaus den Speicherbereich deines Programms verläßt, läßt das 
Betriebssystem es abstürzen. Windows-User kennen das als 
"Schutzverletzuung"

von Markus F. (mfro)


Lesenswert?

Axel S. schrieb:
> Implementation defined.

'array out of bounds' ist undefined behaviour, keineswegs implementation 
defined.

Passieren kann also so ungefähr alles; in den meisten Fällen dürfte 
schlicht ein falscher Wert (möglicherweise der einer anderen Variable) 
zurückgeliefert werden oder (Speicherschutz an einer Page-Grenze) ein 
Crash resultieren.

von Dr. Sommer (Gast)


Lesenswert?

Axel S. schrieb:
> Auf µC eher nicht

Doch, so ziemlich alle ARM Controller haben unbenutzte Speicher 
Bereiche, wenn man da drauf zugreift (egal ob lesen oder schreiben) gibt 
es eine Fault Exception ("Absturz").

von Sven B. (scummos)


Lesenswert?

STK500-Besitzer schrieb:
> Felix schrieb:
>> Kann es Möglichkeiten geben,
>> was zum Absturz oder so führen könnten?
>
> Beim Lesen kann eher nichts passieren (kommt auf dein Programm an).
> Schreiben kann da eher zu Problemen führen.

Doch, das Betriebssystem kann sagen "He, das ist gar nicht dein 
Speicher!". Wird auch oft passieren.

von A. S. (Gast)


Lesenswert?

Felix schrieb:
> Jetzt frage ich ausversehen das 10. Element ab.

Manchmal ist das sogar gewollt, z.B. die "Nutzdaten" nach einen 
bestimmten Header für unterschiedlich lange Telegramme.

Wenn es nicht gewollt ist, dann sorge dafür, dass Compiler-Warnungen 
oder Lint dir das anzeigen. Liegt dahinter eine andere Variable, die 
überschrieben wird, dann kann diese beliebige Fehler nach sich ziehen. 
Oder liegt dort ein Register, dann kann auch rein lesend schon Müll 
passieren, egal, was Dein Compiler daraus macht.

von Rolf M. (rmagnus)


Lesenswert?

Stefanus F. schrieb:
> Felix schrieb:
>> Wo greife ich dann genau hin?
>
> Das ist undefiniert. Bei einer einfachen linearen Speicheradressierung
> (wie sie Mikrocontroller meist haben) würdest du damit auf die
> Speicherzellen hinter dem Array zugreifen.
>
> Bei Systemen mit Memory Mapping Unit ist das Ergebnis unbestimmt. Wenn
> du Glück hast, liegt dahinter Speicher, der deinem Programm gehört. Wenn
> nicht, führt der Versuch, auf fremden Speicher zuzugreifen womöglich zu
> einem Programmabbruch.

Ich sehe es genau umgekehrt: Wenn du Glück hast, wird das Programm 
abgebrochen, und du merkst dadurch gleich, dass es einen Fehler hat. 
Wenn du Pech hast, scheint es beim Test problemlos zu laufen, und 
nachher im Einsatz passieren dann merkwürdige scheinbar unerklärliche 
Dinge.

Axel S. schrieb:
> Der Compiler ordnet deine Variablen im Speicher an. Weitgehend nach
> seinem Gusto.

Das macht bei Variablen mit statischer Lebensdauer in der Regel der 
Linker.

>> Kann es Möglichkeiten geben,
>> was zum Absturz oder so führen könnten?
>
> Auf µC eher nicht. Aber PC Betriebssysteme ergreifen Maßnahmen, um
> Programme voneinander abzuschotten. Wenn du beim Zugriff über das
> Arrayende hinaus den Speicherbereich deines Programms verläßt, läßt das
> Betriebssystem es abstürzen. Windows-User kennen das als
> "Schutzverletzuung"

Bei PC-Betriebssystemen ist der Speicher der Programme eh virtualisiert 
durch Paging. Speicher, der anderen Programmen gehört, ist also aus 
Sicht deines Programmes gar nicht existent.

: Bearbeitet durch User
von Dussel (Gast)


Lesenswert?

Es gibt Sicherheitslücken, bei denen bewusst über Arraygrenzen 
geschrieben wird. So kann man zum Beispiel ausführbaren Code in Bereiche 
schreiben, die später ausgeführt werden.
Mir fällt der Name gerade nicht ein, aber das war früher eine relativ 
häufige Lücke.

von GEKU (Gast)


Lesenswert?

Dussel schrieb:
> früher

Dussel schrieb:
> Mir fällt der Name gerade nicht ein, aber das war früher eine relativ
> häufige Lücke.

StackOverFlow?

von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

GEKU schrieb:
> StackOverFlow?

Eher allgemeiner gesagt "buffer overflow":
https://de.wikipedia.org/wiki/Pufferüberlauf

von Axel S. (a-za-z0-9)


Lesenswert?

Dussel schrieb:
> Es gibt Sicherheitslücken, bei denen bewusst über Arraygrenzen
> geschrieben wird.

Das nennt sich buffer overflow oder deutsch Pufferüberlauf.

> So kann man zum Beispiel ausführbaren Code in Bereiche
> schreiben, die später ausgeführt werden.

Der AVR ist da glücklicherweise außen vor, weil Harvard-Architektur. 
Mainstream-Architekturen nutzen die MMU dazu, ausführbaren Speicher und 
vom Programm beschreibbaren Speicher zu trennen.

Beim Stack bleibt da leider eine Lücke. Ein Pufferüberlauf bei lokalen 
Variablen erlaubt möglicherweise die Manipulation der Rücksprungadresse.

> das war früher eine relativ häufige Lücke.

Leider nicht nur früher. C mit seiner bescheidenen Implementierung von 
Strings lädt leider dazu ein. Wenn man in C mit Strings hantiert, sollte 
man sich angewöhnen, von allen Funktionen die Variante mit dem extra "n" 
im Namen und der Pufferlänge als extra Parameter zu verwenden. Also 
strncpy() statt strcpy() oder auch snprintf() statt sprintf(). usw. usf.

Oder vielleicht besser gleich C++ und richtige Strings verwenden.

von Rolf M. (rmagnus)


Lesenswert?

Axel S. schrieb:
> Also strncpy() statt strcpy() oder auch snprintf() statt sprintf(). usw.
> usf.

Gerade strncpy() wird auch als gefährlich eingestuft, da es an den 
Zielstring kein \0 anhängt, wenn der Quellstring zu lang ist (dafür aber 
unnötig viele davon, wenn er kürzer ist). Wer da nicht immer drauf 
achtet, hat ruck-zuck auch einen Buffer-Overflow, nicht im strncpy() 
selber, aber beim nächsten Iterieren durch den String dann.
Aus dem Grund gibt es auf einigen Systemen eine Funktion strlcpy(), die 
immer einen korrekten C-String erzeugt, sofern der Input auch einer 
ist.

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

A. S. schrieb:

> Manchmal ist das sogar gewollt, z.B. die "Nutzdaten" nach einen
> bestimmten Header für unterschiedlich lange Telegramme.

Das macht man so nicht, schon weil es in jedem Falle undefined behaviour 
ist und der Compiler es rauswerfen kann. Das manifestiert sich dann 
gerne als unerwarteter Bug nach einem Compiler-Update - insbesondere GCC 
ist da sehr rücksichtlos, was Abwärtskompatibilität zu kaputtem Code 
angeht.

Deswegen liest man nicht einfach über den Header hinaus, sondern nimmt 
die Struktur, in der das Telegramm ja ohnehin stecken wird, und die 
einen Datenteil haben muß.

Notfalls wirft man die ganze Struktur in ein Overlay-Char-Array und 
fummelt das dann manuell mit Char-Pointern raus, die dürfen schließlich 
aliasen.

von S. R. (svenska)


Lesenswert?

Nop schrieb:
> Das macht man so nicht, schon weil es in jedem Falle
> undefined behaviour ist und der Compiler es rauswerfen kann.

Jaein, ich glaube A. S. meint sowas:
1
struct telegramm {
2
  char type;
3
  int  len;
4
  char data[]
5
};

Wo man dann mit "data" durch den Datenteil des Telegramms iterieren 
kann, wobei die Struktur nur den Header des Telegramms überlagert.

Das ist eine übliche Vorgehensweise. So üblich, dass der GCC genau dafür 
eine Erweiterung hat...

von Nop (Gast)


Lesenswert?

S. R. schrieb:

> Wo man dann mit "data" durch den Datenteil des Telegramms iterieren
> kann, wobei die Struktur nur den Header des Telegramms überlagert.

Achso, ich hatte das eher so verstanden, daß der Header selber ein Array 
ist und man den einfach out of bounds liest, weil dahinter ja die Daten 
kommen.

von Einer K. (Gast)


Lesenswert?

Nop schrieb:
> out of bounds
In C gibt es die Äquivalenz von Array und Zeiger Zugriffen, welche 
zwangsläufig keinerlei Schutz liefert.

Beispiel:
> int array[] = {1,2,5};

 und später dann:
> print( 2[array] ); // sagt: 5
oder
> print( array[2] ); // sagt: 5
oder
> print( *(array+2)); // sagt: 5
Diese drei Varianten sind vollkommen gleichwertig



print( 42[array] );
Äquivalent zu
print( *(array+42) );
Welches dann wohl in die Wiese zeigt....

von Rolf M. (rmagnus)


Lesenswert?

S. R. schrieb:
> Das ist eine übliche Vorgehensweise. So üblich, dass der GCC genau dafür
> eine Erweiterung hat...

Das ist keine Erweiterung, sondern Standard-C.

von Nop (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:

> In C gibt es die Äquivalenz von Array und Zeiger Zugriffen, welche
> zwangsläufig keinerlei Schutz liefert.

Das ist eine reine Notationsfrage. Trotzdem sind Arrays und Pointer in C 
keineswegs identisch.

Wenn die Speicherreservierung in einer anderen translation unit erfolgt 
als der Zugriff, weil das als Pointer an eine Funktion aus einer anderen 
Datei übergeben wird, dann dürfte der Compiler den Schmu nicht bemerken.

Ist es hingegen in derselben Datei, dann kann der Compiler durchaus 
zuschlagen.

von Einer K. (Gast)


Lesenswert?

Nop schrieb:
> Ist es hingegen in derselben Datei, dann kann der Compiler durchaus
> zuschlagen.
Ja?
Welcher?

Der AVR-gcc tut es nicht.
Der lässt es ohne Murren durch.
Egal, ob C oder C++.
Egal, ob in der gleichen Übersetzungseinheit, oder auch nicht.



Weiterhin:
Arduino Fanboy D. schrieb:
> keinerlei Schutz


Teste es bitte.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Arduino Fanboy D. schrieb:
> Nop schrieb:
>> Ist es hingegen in derselben Datei, dann kann der Compiler durchaus
>> zuschlagen.
> Ja?
> Welcher?
>
> Der AVR-gcc tut es nicht.

Doch, aber natürlich nur dann, wenn bereits zur Compilezeit erkennbar
ist, dass ein Index zu groß wird, wie bspw. hier (avr-gcc-9.1.0 -Os):

1
int array[4];
2
3
int main(void) {
4
  for(int i=0; i<=4; i++)
5
    array[i] = i;
6
}
1
test.c: In function 'main':
2
test.c:5:14: warning: iteration 4 invokes undefined behavior [-Waggressive-loop-optimizations]
3
    5 |     array[i] = i;
4
      |     ~~~~~~~~~^~~
5
test.c:4:3: note: within this loop
6
    4 |   for(int i=0; i<=4; i++)
7
      |   ^~~


Ist die obere Schleifengrenze hingegen das Ergebnis einer komplexeren
Berechnung oder gar einer I/O-Operation, wird keine Warnung ausgegeben.
Das würden auch Compiler für andere Programmiersprachen nicht tun. Da
bliebe dann nur die Laufzeitprüfung, die in C aber kaum praktikabel ist.

von Einer K. (Gast)


Lesenswert?

Yalu X. schrieb:
> Doch, aber natürlich nur dann, wenn bereits zur Compilezeit erkennbar
> ist, dass ein Index zu groß wird, wie bspw. hier (avr-gcc-9.1.0 -Os):

Da zaubert er eine Schleife rein.....
Ein Argumentverdreher, er ist...
1
int array[4];
2
3
int main(void) {
4
    array[42] = 5;
5
    42[array] = 5;
6
    *(array+42) = 5;
7
}

Obwohl deutlich erkennbar, dass der Index zu groß ist, geht es ohne 
Murren durch, auch mit avr-gcc-9.1.0 -Os

von Nop (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> Nop schrieb:
>> Ist es hingegen in derselben Datei, dann kann der Compiler durchaus
>> zuschlagen.
> Ja?
> Welcher?

Jeder. In jeder Version nach dem nächsten Update. Ich habe schon genug 
kaputten Code gesehen, der nach dem nächsten Compiler-Update nicht mehr 
lief. Das verhindert man, indem man sowas gar nicht erst schreibt, 
sondern sich mal damit befaßt, was der C-Standard dazu meint.

> Weiterhin:
> Arduino Fanboy D. schrieb:
>> keinerlei Schutz
>
> Teste es bitte.

Bauche ich nicht, denn experimentelles undefined behaviour ist Blödsinn 
- siehe oben.

von Nop (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> int array[4];
>
> int main(void) {
>     array[42] = 5;
>     42[array] = 5;
>     *(array+42) = 5;
> }

GCC 7.3.0 (MingW) warnt übrigens mit -Wall sehr wohl "subscript out of 
bounds", das nur der Vollständigkeit halber. Daß er ohne -Wall nicht 
warnt, bedeutet nicht, daß er es nicht für unerwartete Optimierungen 
nutzen kann.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Arduino Fanboy D. schrieb:
> int array[4];
>
> int main(void) {
>    array[42] = 5;
> }
>
> Obwohl deutlich erkennbar, dass der Index zu groß ist, geht es ohne
> Murren durch

Ok, hier braucht man noch ein -Wall oder -Warray-bounds.

1
test.c:3:9: warning: array subscript 42 is above array bounds of 'int[4]' [-Warray-bounds]
2
    3 |    array[42] = 5;
3
      |    ~~~~~^~~~
4
test.c:1:5: note: while referencing 'array'
5
    1 | int array[4];
6
      |     ^~~~~

Wobei ich mittlerweile (nach seinem vorletzten Beitrag) glaube, dass Nop
mit

Nop schrieb:
> zuschlagen

etwas anderes gemeint hat.

: Bearbeitet durch Moderator
von Einer K. (Gast)


Lesenswert?

Nop schrieb:
> Jeder.
Gelogen.

Deine Behauptung war, dass der Compiler das meldet.
Und diese ist widerlegt.

Da kannst du an den Haaren herbei ziehen was du willst...

Yalu X. schrieb:
> Ok, hier braucht man noch ein -Wall oder -Warray-bounds.
Ja, dann wirds was....

von Nop (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:

> Gelogen.
>
> Deine Behauptung war, dass der Compiler das meldet.

Lerne lesen. Meien ursprüngliche Aussage war, ich zitiere mich:

"Wenn der Compiler mitbekommt, daß Du da out of bounds zugreifst, kann 
er
auch die ganze Funktion wegoptimieren, oder gleich das ganze Programm.
Oder sonstwas für Code erzeugen."

Ich habe nicht geschrieben, daß er dazu warnen muß. Wenn Du ernsthaft 
glaubst, daß der Compiler nur solches undefined behaviour ausnutzen 
würde, vor welchem er auch warnt, dann bist Du massiv auf dem Holzweg.

von A. S. (Gast)


Lesenswert?

Nop schrieb:
> "Wenn der Compiler mitbekommt, daß Du da out of bounds zugreifst, kann
> er auch die ganze Funktion wegoptimieren, o

Ich bezweifle das. Habe jetzt am Handy nicht den Standard, aber ich habe 
den so verstanden: greift man außerhalb des (reservierten) Speichers zu, 
undefined behaviour. Hat man Speicher reserviert (wie auch immer), dann 
wie erwartet. Egal ob das [] Konstrukt in einer Union steckt, per ptr 
auf einen mallocbereich zeigt oder zum memdump dient.

von Einer K. (Gast)


Lesenswert?

Nop schrieb:
> Lerne lesen.

Lerne selber lesen.


Das array[42] = 5; zieht der Compiler durch!
Das optimiert er nicht weg, nur weil/wenn er eine Warnung wirft.
1
   array[42] = 5;
2
 1bc:  85 e0         ldi  r24, 0x05  ; 5
3
 1be:  90 e0         ldi  r25, 0x00  ; 0
4
 1c0:  90 93 55 01   sts  0x0155, r25  ; 0x800155 <__bss_end+0x46>
5
 1c4:  80 93 54 01   sts  0x0154, r24  ; 0x800154 <__bss_end+0x45>
*(array+42)=5; erzeugt identischen Code
Dank der Äquivalenz von Array und Zeiger Zugriffen.

von Joachim B. (jar)


Lesenswert?

Nop schrieb:
> Ich habe schon genug
> kaputten Code gesehen, der nach dem nächsten Compiler-Update nicht mehr
> lief. Das verhindert man, indem man sowas gar nicht erst schreibt,
> sondern sich mal damit befaßt, was der C-Standard dazu meint.

ach was meint denn der C-Standard zu char? singend oder unsigned?
Was ist mit Char über 127?

Ich erlebe da immer wundersames. Möchte ich einfach nur ein Array of 
Bytes mit 8 Bit haben, was den vollen Char Umfang ermöglicht (16 Bit mal 
ausgenommen) ärgern mich immer so Fehler oder Warnungen von ist defined 
als unsigned ist aber signed oder umgedreht. Warum kann ein Byte nicht 
einfach uint8_t sein, was ist verwerflich uint8_t auch auf str 
Funktionen anzuwenden? (kann denn ein char negativ werden?)

Und jeder Funktion eine Typprüfung oder cast vorzusetzen ist auch nicht 
gerade anwenderfreundlich.

Letztens wieder mit Nokia Zeichensatz bis 255, isprint ignoriert alles 
über 127

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Joachim B. schrieb:

> ach was meint denn der C-Standard zu char? singend oder unsigned?
> Was ist mit Char über 127?

Ist im Zusammenhang mit aliasing irrelevant, wenn man nur die binären 
Daten rausgreifen will.


Arduino Fanboy D. schrieb:

> Das optimiert er nicht weg, nur weil/wenn er eine Warnung wirft.

Ich seh schon, Du verstehst von der Materie exakt soviel, wie man es von 
einem "Arduino Fanboy" auch erwarten würde. Leute wie Du sind es, die 
die Bugtracker der Compiler nach Versionsupdates mit angeblichen 
Compilerbugs fluten, weil ihr kaputter Code auf einmal zerbrochen ist.

EOD.

von Joachim B. (jar)


Lesenswert?

Nop schrieb:
> Leute wie Du sind es, die
> die Bugtracker der Compiler nach Versionsupdates mit angeblichen
> Compilerbugs fluten, weil ihr kaputter Code auf einmal zerbrochen ist.

wer schreibt denn "perfekten" Code?
Ich habe niemanden gefunden, angeblich wird das in einer Uni gelehrt, 
aber die das lernen sind i.d.R. keine code monkeys.

Joachim B. schrieb:
> Und jeder Funktion eine Typprüfung oder cast vorzusetzen ist auch nicht
> gerade anwenderfreundlich.

und überfordert jeden µC oder Programmierer.

Wie kann es sonst sein das Fedora Version23 beim Dateimanger PCMANFM 
abschmiert?
Dateifunktionen sollten doch mittlerweile schon funktionieren.

von Nop (Gast)


Lesenswert?

Joachim B. schrieb:

> wer schreibt denn "perfekten" Code?

Es ist eine Sache, ob man irrtümlich verkehrten Code schreibt, aber an 
sich bestrebt ist, das zu vermeiden - aber eine andere, oder ob man sich 
hinstellt und behauptet, kaputter Code sei doch OK, weil er jetzt gerade 
mal nicht zerbreche.

von Einer K. (Gast)


Lesenswert?

A. S. schrieb:
> aber ich habe
> den so verstanden: greift man außerhalb des (reservierten) Speichers zu,
> undefined behaviour.

Ja, aber es ist nicht das Zugriffsverfahren, welches undefiniert ist, 
sondern das was passiert, wenn dort zugegriffen wird.

Dem Nop seine Behauptung, dass der Compiler solche Zugriffe unterbindet, 
ist schlicht falsch.
Sondern, der Compiler zieht das durch, trotz evtl geworfener Warnung.
Da kann der Nop mich angreifen und beleidigen, wie er will.
Da beißt keine Maus einen Faden von ab.

von Joachim B. (jar)


Lesenswert?

Nop schrieb:
> oder ob man sich
> hinstellt und behauptet, kaputter Code sei doch OK

würde mir nie einfallen, dazu ärgere ich mich zu oft über falschen Code.
Aber wie sagte ein Chef mal, manchmal muss man einem Entwickler das Teil 
aus der Hand reissen. Gilt offensichtlich auch für Programmierer, zu 
vieles ist nicht bedacht worden, manchmal auch einfach vergessen und an 
andere Fehlermöglichkeiten denkt man auch mal nicht.

: Bearbeitet durch User
von S. R. (svenska)


Lesenswert?

Rolf M. schrieb:
>> Das ist eine übliche Vorgehensweise. So üblich,
>> dass der GCC genau dafür eine Erweiterung hat...
>
> Das ist keine Erweiterung, sondern Standard-C.

Stimmt, flexible Arrays sind seit C99 vorhanden.
Vorher hat man ein Array der Länge null angegeben,
was eine GCC-Erweiterung ist.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Arduino Fanboy D. schrieb:
> Dem Nop seine Behauptung, dass der Compiler solche Zugriffe unterbindet,
> ist schlicht falsch.

Ihr redet aneinander vorbei. Ich hatte den Nop erst auch missverstanden,
was u.a. damit zusammenhing, dass ich nur einen Teil seiner Beiträge
gelesen habe. Spätestens nach seinen zusätzlichen Erläuterungen sollte
aber klar sein, was er gemeint hat, so dass man darauf nicht noch ewig
herumreiten muss.

von Rolf M. (rmagnus)


Lesenswert?

Joachim B. schrieb:
> Warum kann ein Byte nicht einfach uint8_t sein, was ist verwerflich
> uint8_t auch auf str Funktionen anzuwenden? (kann denn ein char negativ
> werden?)

char dient lediglich als Speicherplatz für ein Zeichen. Da man mit Text 
nicht rechnet, ist die signedness egal. Es ist in der Regel nicht 
zweckmäßig, es überall auf unsigned zu zwingen, nur weil man der Meinung 
ist, dass da kein Vorzeichen sein darf, und sich damit ggf. 
einzuhandeln, dass man bei jeder Stringfunktion eine Warnung bekommt, 
weil die Signedness nicht passt.

Arduino Fanboy D. schrieb:
> Dem Nop seine Behauptung, dass der Compiler solche Zugriffe unterbindet,
> ist schlicht falsch.

Wo hat er das denn behauptet?

> Sondern, der Compiler zieht das durch, trotz evtl geworfener Warnung.

Der Compiler darf machen, was er will. Undefiniertes Verhalten ist nicht 
auf die Laufzeit des Programms beschränkt. Es heißt, dass alles 
passieren kann - auch zur Compilezeit.

von Nop (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:

> Dem Nop seine Behauptung, dass der Compiler solche Zugriffe unterbindet,
> ist schlicht falsch.

Was daher kommt, daß ich das so nicht behauptet habe und Du nichtmal im 
Ansatz begriffen hast, was die Konsequenzen von undefined behaviour 
sind. Der Compiler KANN das wegoptimieren. Nicht nur den Zugriff, gleich 
die ganze Funktion. Oder auch das ganze Programm. Oder ganz beliebigen 
Code erzeugen, z.B. eine Endlosschleife.

Ist es hingegen in einer anderen translation unit in einem 
Funktionsaufruf, dann wird der Compiler das nicht wegoptimieren, weil er 
in der Funktion nicht weiß, daß der Speicher hinter dem Pointer, zu dem 
das Array verflacht ist, nicht ausreicht, und somit das undefined 
behaviour nicht sieht.

Dann kann das Programm lediglich ganz normal zur Laufzeit abstürzen 
und/oder verkehrte Ergebnisse bringen.

von Mikro 7. (mikro77)


Lesenswert?

A. S. schrieb:
> Nop schrieb:
>> "Wenn der Compiler mitbekommt, daß Du da out of bounds zugreifst, kann
>> er auch die ganze Funktion wegoptimieren, o
>
> Ich bezweifle das.

Das Verhalten bei einem out-of-bound Zugriff ist in C nicht definiert. 
Es kann alles passieren: Vielleicht soagr etwas mehr oder weniger 
erwartetes; oder aber die Funktion wird wegoptimiert; oder, ess regnet 
rosarote Elefanten. :-)

Ich komme eher aus der Asm Ecke. Daher war das für mich anfangs nicht 
wirklich verständlich: Wenn der Speicher im Adressraum des Prozesses 
gamapped ist, dann wird doch wohl darauf zugegriffen!

Oder auch nicht? Vielleicht?

Der Compilerbauer kann in so einer Situation machen was er will: Auf 
einer der letzten C++ Cons gab es einen netten Vortrag zu dem UB Thema: 
https://www.youtube.com/watch?v=ehyHyAIa5so

von Joachim B. (jar)


Lesenswert?

Rolf M. schrieb:
> char dient lediglich als Speicherplatz für ein Zeichen.

eben drum, wer sagt denn das Zeichen nicht über 127 gehen dürfen?

Wenn die Variable aber signed char ist geht char 128 eben nicht


Rolf M. schrieb:
> dass man bei jeder Stringfunktion eine Warnung bekommt,
> weil die Signedness nicht passt.

wie kommt man auf die Idee char signed zu definieren?

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Joachim B. schrieb:

> wie kommt man auf die Idee char signed zu definieren?

Weil es irrlevant ist. char (ohne signed / unsigned davor) ist nicht zum 
Rechnen da, und ob der numerische Wert eines characters positiv oder 
negativ ist, das ist Jacke wie Hose.

von Jemand (Gast)


Lesenswert?

Hier wird immer von dem Zugriff geredet, aber bereits das Erzeugen des 
Pointers ist problematisch:

6.5.6 Additive operators
When an expression that has integer type is added to or subtracted from 
a pointer, the
result has the type of the pointer operand.
[...]
If both the pointer
operand and the result point to elements of the same array object, or 
one past the last
element of the array object, the evaluation shall not produce an 
overflow; otherwise, the
behavior is undefined.

von Einer K. (Gast)


Lesenswert?

Mikro 7. schrieb:
> Das Verhalten bei einem out-of-bound Zugriff ist in C nicht definiert.

Dank der Äquivalenz von Zeiger und Arrayzugriffen, ist der Compiler 
verpflichtet den Code zu generieren.

Das Verhalten zur Laufzeit, liegt dann in Gottes Hand.
1
int* test = (int*)100; // zeigt in die Wiese
2
3
4
int main() 
5
{
6
 test[42] = 5;
7
}

von S. R. (svenska)


Lesenswert?

Rolf M. schrieb:
> char dient lediglich als Speicherplatz für ein Zeichen.
> Da man mit Text nicht rechnet, ist die signedness egal.

Soweit nachvollziehbar. Hätten sie mal einen eigenen Datentyp für Bytes 
vorgesehen...

Joachim B. schrieb:
> wie kommt man auf die Idee char signed zu definieren?

ASCII hat nur 7 Bit, das obere Bit hat eine Sonderbedeutung (Parität, 
Attribut, ...). Mit einem "signed char" kann man die Sonderbedeutung 
recht schnell extrahieren.

Es ist eine idiotische Entscheidung, aber im Rahmen der damaligen 
Technologie nachvollziehbar. Und heute haben wir Kompatiblität.

von Mikro 7. (mikro77)


Lesenswert?

Joachim B. schrieb:
> wie kommt man auf die Idee char signed zu definieren?

Bloss der Vollständigkeit halber: Im Gegensatz zu den den anderen "int" 
Typen, gib es für char tatsächlich drei Varianten: char, signed char und 
unsigned char.

Hier bspw. etwas zum Lesen (gibt aber bestimmt bessere Links): 
https://stackoverflow.com/questions/2054939/is-char-signed-or-unsigned-by-default

Arduino Fanboy D. schrieb:
> Mikro 7. schrieb:
>> Das Verhalten bei einem out-of-bound Zugriff ist in C nicht definiert.
>
> Dank der Äquivalenz von Zeiger und Arrayzugriffen, ist der Compiler
> verpflichtet den Code zu generieren.

Nö.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Joachim B. schrieb:
> Letztens wieder mit Nokia Zeichensatz bis 255, isprint ignoriert alles
> über 127

Woher soll der Compiler bzw. die Standardbibliothek auch wissen, welche
Zeichen sich hinter den Codes 128..255 der Nokia-Zeichencodierung
verbergen?

Je nach Umgebung kannst du die is*-Funktionen mittels

1
setlocale(LC_CTYPE, "...");

dazu bringen, auch andere 8-Bit-Zeichenkodierungen als den Default
(7-Bit-ASCII) zu akzeptieren, bspw. ISO 8859-x. Dann liefern isprint und
isalpha auch für Umlaute ein true zurück.

von Rolf M. (rmagnus)


Lesenswert?

Joachim B. schrieb:
> Rolf M. schrieb:
>> char dient lediglich als Speicherplatz für ein Zeichen.
>
> eben drum, wer sagt denn das Zeichen nicht über 127 gehen dürfen?

Es sind Zeichen, keine Zahlen. Es gibt kein "über 127". Sieh es 
einfach als einen Speicherblock, der (in dem Fall) 8 Bits breit ist. Wie 
diese Bits interpretiert werden würden, wenn man damit rechen würde, ist 
nicht von Belang. Daher ist auch nicht von Belang, ob man dafür einen 
signed- oder unsigned-Typen verwendet. Text kennt kein Konzept einer 
Signedness.
Wenn man char richtig verwendet, macht es für das Programm auch 
keinerlei Unterschied.

> Rolf M. schrieb:
>> dass man bei jeder Stringfunktion eine Warnung bekommt,
>> weil die Signedness nicht passt.
>
> wie kommt man auf die Idee char signed zu definieren?

Wie kommt man darauf, es anders zu definieren? Alle anderen 
Integer-Typen sind auch signed, wenn nichts explizit angegeben ist.

: Bearbeitet durch User
von Joachim B. (jar)


Lesenswert?

Rolf M. schrieb:
> Sieh es
> einfach als einen Speicherblock, der (in dem Fall) 8 Bits breit ist.

genau das ist doch das Problem, oder was sehe ich falsch?

#define GRAD 132

char text[3] = { "°C" }; (zur Ausgabe auf die Usart)

möchte ich Zeichen ersetzen zur Ausgabe auf das Display text[0]= GRAD 
(entprechend Zeichensatz für Nokia) und GRAD liegt auf 132 ist es 
ausserhalb 0-127 signed char.

An dieser Stelle habe ich mit der Arduino IDE offensichtlich mehr 
Probleme als früher unter Atmel Studio auch mit gcc.

Witzig oder nicht, stellte ich letztens fest das die IDE sogar selber 
umstellt

char text[4] = {"A°A"};

A°A gibt nicht etwa wie ich erwartet hätte in text[4]

text[0] = 65d;
text[1] = 132d;
text[2] = 65d;
text[3] = 0;

sondern irgendwie so
text[0] = 65d;
text[1] = 132d;
text[2] = große Zahl über 127 (ich glaube 176)
text[3] = 0;

Mal sieht die Printausgabe OK aus mal gibt es nur Quadrate, auch da 
verstehe ich nicht warum mal ° und mal QUADRAT gezeigt wird.

von Jemand (Gast)


Lesenswert?

Joachim B. schrieb:
> A°A gibt nicht etwa wie ich erwartet hätte in text[4]
>
> text[0] = 65d;
> text[1] = 132d;
> text[2] = 65d;
> text[3] = 0;
>
> sondern irgendwie so
> text[0] = 65d;
> text[1] = 132d;
> text[2] = große Zahl über 127 (ich glaube 176)
> text[3] = 0;

Ich soll dir von UTF-8 Grüße ausrichten.

von B. S. (bestucki)


Lesenswert?

Joachim B. schrieb:
> möchte ich Zeichen ersetzen zur Ausgabe auf das Display text[0]= GRAD
> (entprechend Zeichensatz für Nokia) und GRAD liegt auf 132 ist es
> ausserhalb 0-127 signed char.

char ist dazu da, den Systenzeichensatz zu repräsentieren. Zwischen 
0...127 ist dir ASCII garantiert, alle anderen Werte sind implementation 
defined. Willst du einen anderen Zeichensatz verwenden, so benötigst du 
einen anderen Typen als char und entsprechende Konvertierungsfunktionen.

Joachim B. schrieb:
> Mal sieht die Printausgabe OK aus mal gibt es nur Quadrate, auch da
> verstehe ich nicht warum mal ° und mal QUADRAT gezeigt wird.

'°' ist nicht in ASCII enthalten.

von Rolf M. (rmagnus)


Lesenswert?

Das Problem ist da aber nicht die Signedness von char, sondern der 
verwendete Zeichensatz. Die genannten 176 entsprechend dem Grad-Zeichen 
im ISO-8859-1-Zeichensatz.

von mh (Gast)


Lesenswert?

B. S. schrieb:
> Joachim B. schrieb:
>> möchte ich Zeichen ersetzen zur Ausgabe auf das Display text[0]= GRAD
>> (entprechend Zeichensatz für Nokia) und GRAD liegt auf 132 ist es
>> ausserhalb 0-127 signed char.
>
> char ist dazu da, den Systenzeichensatz zu repräsentieren.
Es ist der "execution character set" garantiert.
> Zwischen 0...127 ist dir ASCII garantiert, alle anderen Werte sind
> implementation
> defined. Willst du einen anderen Zeichensatz verwenden, so benötigst du
> einen anderen Typen als char und entsprechende Konvertierungsfunktionen.
Wenn du mit "ASCII garantiert" meinst, dass alle ASCII Zeichen vorhanden 
sind, dann stimmt das vielleicht. Ich habe bis jetzt nicht überprüft, ob 
alle Zeichen vorhanden sind. Die Reihenfolge ist allerdings nicht 
garantiert. ASCII kommt im Standard nur als Fußnote vor.

von B. S. (bestucki)


Lesenswert?

mh schrieb:
> Wenn du mit "ASCII garantiert" meinst, dass alle ASCII Zeichen vorhanden
> sind, dann stimmt das vielleicht. Ich habe bis jetzt nicht überprüft, ob
> alle Zeichen vorhanden sind. Die Reihenfolge ist allerdings nicht
> garantiert. ASCII kommt im Standard nur als Fußnote vor.

Habs gerade nachgelesen: Der Zeichensatz ist gemäss ASCII, die 
Reihenfolge der Werte kann jedoch beliebig sein. Das Einzige was 
garantiert ist, ist, dass alle ASCII-Zeichen vorzeichenlos in einen char 
passen, die Werte der Zeichen
1
0123456789!"#%&'()*+,-./:;<=>?[\]^_{~}
 aufsteigend ohne Lücke repräsentiert werden und dass '\0' == 0 ist. 
Alles Andere ist implementation defined.

Rolf M. schrieb:
> Das Problem ist da aber nicht die Signedness von char

Kann aber zum Problem werden, wenn man den kompletten Wertebereich von 
256 Zeichen ausnutzen möchte. Leider existieren für die Repräsentation 
eines signed Typen immer noch drei unterschiedliche Varianten (wobei 
zugegeben fast nur das 2er-Komplement benutzt wird).

von mh (Gast)


Lesenswert?

B. S. schrieb:
> Das Einzige was
> garantiert ist, ist, dass alle ASCII-Zeichen vorzeichenlos in einen char
> passen, die Werte der Zeichen0123456789!"#%&'()*+,-./:;<=>?[\]^_{~}
> aufsteigend ohne Lücke repräsentiert werden und dass '\0' == 0 ist.
> Alles Andere ist implementation defined.

Das stimmt noch nicht ganz. Es müssen nur die Ziffern aufeinanderfolgend 
sein.

von Joachim B. (jar)


Lesenswert?

Jemand schrieb:
> Ich soll dir von UTF-8 Grüße ausrichten.

echt nicht hilfreich,
das geht die IDE nichts an was ich printe!

B. S. schrieb:
> Joachim B. schrieb:
>> Mal sieht die Printausgabe OK aus mal gibt es nur Quadrate, auch da
>> verstehe ich nicht warum mal ° und mal QUADRAT gezeigt wird.
>
> '°' ist nicht in ASCII enthalten.

mag ja sein, mich stört das es mal funktioniert und mal NICHT, verstehst 
du den Unterschied?

Rolf M. schrieb:
> Die genannten 176 entsprechend dem Grad-Zeichen
> im ISO-8859-1-Zeichensatz.

und da stört mich der Wechel, mal so mal so!

Rolf M. schrieb:
> Das Problem ist da aber nicht die Signedness von char,

aber an anderer Stelle der str Funktionen.

B. S. schrieb:
> Rolf M. schrieb:
>> Das Problem ist da aber nicht die Signedness von char
>
> Kann aber zum Problem werden, wenn man den kompletten Wertebereich von
> 256 Zeichen ausnutzen möchte

eben, auf µC wozu als Standard int16 verwenden? für 256 Zeichen - 
Steuerzeichen.

von mh (Gast)


Lesenswert?

Joachim B. schrieb:
> Jemand schrieb:
>> Ich soll dir von UTF-8 Grüße ausrichten.
>
> echt nicht hilfreich, [...]

Niemand hier kann etwas dafür, dass du char, string und Zeichencodierung 
in C  nicht verstehst.

Joachim B. schrieb:
> Rolf M. schrieb:
>> Das Problem ist da aber nicht die Signedness von char,
>
> aber an anderer Stelle der str Funktionen.

Die würden auch wunderbar für deinen Anwendungsfall funktionieren, mit 
Ausnahme der is_XXX Funktionen. Du musst nur etwas flexibler sein. Auch 
wenn "dein" Zeichensatz einem Zeichen einen Wert >127 zuordnet, kannst 
du ihn mit einer C Implementation benutzen für die char==signed char 
ist.

von Rolf M. (rmagnus)


Lesenswert?

Joachim B. schrieb:
> Jemand schrieb:
>> Ich soll dir von UTF-8 Grüße ausrichten.
>
> echt nicht hilfreich,
> das geht die IDE nichts an was ich printe!

Wenn du eine Textdatei bearbeitest, muss der Editor gezwungenermaßen 
eine bestimmte Zeichenkodierung verwenden. Und deine IDE wird die 
verwenden, auf die sie eingestellt ist.
Dazu kommt noch der C-Compiler und ggf. dessen Umkodierung vom Source 
Character Set ins Execution Character Set.
Wenn das nicht zusammenpasst, kann schon mal Blödsinn rauskommen.

von B. S. (bestucki)


Lesenswert?

mh schrieb:
> Das stimmt noch nicht ganz. Es müssen nur die Ziffern aufeinanderfolgend
> sein.

Echt? Ich hatte den Paragraphen anders interpretiert.

Joachim B. schrieb:
> mag ja sein, mich stört das es mal funktioniert und mal NICHT, verstehst
> du den Unterschied?

Nennt sich implementation defined behaviour. Darauf kann man sich nur 
auf einer spezifischen Plattform mit einer spezifischen Compilerversion 
verlassen.

von Joachim B. (jar)


Lesenswert?

mh schrieb:
> Du musst nur etwas flexibler sein

werde ich ja auch, da arbeite ich lieber mit memcpy statt mit str 
Funktionen.

Dem memxxx ist es egal ob ich uint8_t verschiebe char oder unsigned 
char.

von Dirk B. (dirkb2)


Lesenswert?

Joachim B. schrieb:
> Dem memxxx ist es egal ob ich uint8_t verschiebe char oder unsigned
> char.

Den Stringfunktionen der Standardlibrary auch.

Der Compiler meckert bei der Übergabe, weil die Typen nicht 
übereinstimmen.

Das kann man mit cast lösen, wenn man unbedingt unsigned char will und 
den Compiler nicht umstellen will.

Denn du kannst dem Compiler sagen, ob char signed oder unsigned ist.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Dirk B. schrieb:
> Denn du kannst dem Compiler sagen, ob char signed oder unsigned ist.

Damit bekommt man die Warnung bzgl. Zeiger aber nicht weg, weil char ein 
anderer Typ ist als signed char (auch mit -fsigned-char) und ein anderer 
als unsigned char (auch mit -funsigned-char).

Kurz: char, signed char und unsigned char sind 3 verschiedene Typen.

von mh (Gast)


Lesenswert?

B. S. schrieb:
> mh schrieb:
>> Das stimmt noch nicht ganz. Es müssen nur die Ziffern aufeinanderfolgend
>> sein.
>
> Echt? Ich hatte den Paragraphen anders interpretiert.
1
In both the source and execution basic character sets, the value of each character after 0 in the above list of decimal digits shall be one greater than the value of the previous.
Und ein paar Zeilen vorher die "list of decimal digits"
1
the 10 decimal digits
2
0123456789

von Hans (Gast)


Lesenswert?

Felix schrieb:
> Wo greife ich dann genau hin?

So ziemlich ins Klo!

von mh (Gast)


Lesenswert?

Nachtrag: Ich beziehe mich auf c11 draft N1570, 5.2.1 Character sets

von Phiarc (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> Mikro 7. schrieb:
>> Das Verhalten bei einem out-of-bound Zugriff ist in C nicht definiert.
>
> Dank der Äquivalenz von Zeiger und Arrayzugriffen, ist der Compiler
> verpflichtet den Code zu generieren.

Das ist schlicht falsch.

von S. R. (svenska)


Lesenswert?

Joachim B. schrieb:
>> Ich soll dir von UTF-8 Grüße ausrichten.
> echt nicht hilfreich,
> das geht die IDE nichts an was ich printe!

Aber den Editor.
Wenn du falsche Bytes in den Quelltext schreibst, dann kann der Compiler 
da auch nix für. Eigene Selber-Schuld.

von Joachim B. (jar)


Lesenswert?

S. R. schrieb:
> dann kann der Compiler
> da auch nix für. Eigene Selber-Schuld.

auf das dünne Brett gehe ich noch nicht,

offensichtlich bin ich nicht verstanden worden,

wie schon geschrieben, mal printet der Atmel an der USART die richtige 
Kombination für "°C" und ich kann das Zeichen "°" für das Display 
erstzen, mal kommt eben Unfug heraus. Ich wollte nur wissen warum das so 
ist, entweder die IDE ist schlau und kodiert passend um, warum dann 
nicht immer, warum mal so mal so. Das nervt doch.
Ich kann mit allem leben wenn es konsequent ist, aber mal so mal so 
ergibt für mich keinen passenden Ansatz richtig einzugreifen.

von mh (Gast)


Lesenswert?

Joachim B. schrieb:
> Ich kann mit allem leben wenn es konsequent ist, aber mal so mal so
> ergibt für mich keinen passenden Ansatz richtig einzugreifen.

Wie wärs wenn du herausfindest was das Problem ist?

von Joachim B. (jar)


Lesenswert?

mh schrieb:
> Wie wärs wenn du herausfindest was das Problem ist?

keine Sorge, da bin ich bei, nur manchmal gibts hier auch gute Tipps.
Mir die Schuld zuzuschieben ist leicht, hilft aber nicht wenn ein 
Verhalten in der IDE inkonsequent ist.

Das macht die Analyse halt schwieriger.

"°C" druckt seriell einwandfrei, ich erkenne das ° und kann es zum 
Display austauschen.
"A°A" eben nicht, das nachfolgende A ist im Eimer

: Bearbeitet durch User
von Christopher J. (christopher_j23)


Lesenswert?

Nop schrieb:
> Das verhindert man, indem man sowas gar nicht erst schreibt,
> sondern sich mal damit befaßt, was der C-Standard dazu meint.

Hat jemand mal die passende Stelle aus dem Standard, also die die sagt, 
dass out-of-bounds access undefined behaviour ist?

Ich kenne nur die Stelle 6.5.2.1 "Array Subscripting":
"A postfix expression followed by an expression in square brackets [] is 
a subscripted designation of an element of an array object. The 
definition of the subscript operator [] is that E1[E2] is identical to 
(*((E1)+(E2))). Because of the conversion rules that apply to the binary 
+ operator, if E1 is an array object (equivalently, a pointer to the 
initial element of an array object) and E2 is an integer, E1[E2] 
designates the E2-th element of E1 (counting from zero)."

von Yalu X. (yalu) (Moderator)


Lesenswert?

Joachim B. schrieb:
> char text[4] = {"A°A"};

Vorsicht!

Wenn die von deinem Editor/IDE verwendete Zeichencodierung UTF-8 ist,
was unter Linux Standard und auch unter Windows immer mehr im Kommen
ist, wird das °-Zeichen in zwei Bytes kodiert, d.h. in C ist

text[0] =  65
text[1] = 194
text[2] = 176
text[3] =  65

Die Nullterminierung wird unterschlagen, weil sie nicht in char[4] Platz
hat. Ein String ohne Nullterminierung führt zu beliebigen Problemen, die
auch zufälliger Art sein können (je nachdem, wie der Speicherinhalt
hinter der Variable test aussieht).

Damit sind wir sogar wieder ontopic :)

In C++ hingegen erhältst du die Fehlermeldung

1
initializer-string for array of chars is too long

Da wird einem wenigstens bewusst, dass das so nicht geht.

Joachim B. schrieb:
> #define GRAD 132
>
> char text[3] = { "°C" }; (zur Ausgabe auf die Usart)
>
> möchte ich Zeichen ersetzen zur Ausgabe auf das Display text[0]= GRAD

Abgesehen von der zu kleinen Dimensionierung von text kannst du das 2
Bytes umfassende °-Zeichen nicht einfach mit einem 1-Byte-Zeichen
überschreiben, weil ja immer noch das zweite Byte stehen bleibt und als
ungültiges Zeichen (bspw. als leeres Quadrat) ausgegeben wird.


Christopher J. schrieb:
> Hat jemand mal die passende Stelle aus dem Standard, also die die sagt,
> dass out-of-bounds access undefined behaviour ist?

Nicht erst der Access ist UB, sondern schon die Erzeugung des Pointers,
der für diesen Access verwendet werden soll. Siehe hier:

Jemand schrieb:
> 6.5.6 Additive operators

: Bearbeitet durch Moderator
von mh (Gast)


Lesenswert?

Christopher J. schrieb:
> Nop schrieb:
>> Das verhindert man, indem man sowas gar nicht erst schreibt,
>> sondern sich mal damit befaßt, was der C-Standard dazu meint.
>
> Hat jemand mal die passende Stelle aus dem Standard, also die die sagt,
> dass out-of-bounds access undefined behaviour ist?

Aus dem c11 draft N1570:
1
6.5.6 Additive operators
2
8 When an expression that has integer type is added to or subtracted from a pointer, the
3
result has the type of the pointer operand. If the pointer operand points to an element of
4
an array object, and the array is large enough, the result points to an element offset from
5
the original element such that the difference of the subscripts of the resulting and original
6
array elements equals the integer expression. In other words, if the expression P points to
7
the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and
8
(P)-N (where N has the value n) point to, respectively, the i+n-th and i−n-th elements of
9
the array object, provided they exist. Moreover, if the expression P points to the last
10
element of an array object, the expression (P)+1 points one past the last element of the
11
array object, and if the expression Q points one past the last element of an array object,
12
the expression (Q)-1 points to the last element of the array object. If both the pointer
13
operand and the result point to elements of the same array object, or one past the last
14
element of the array object, the evaluation shall not produce an overflow; otherwise, the
15
behavior is undefined. If the result points one past the last element of the array object, it
16
shall not be used as the operand of a unary * operator that is evaluated.

von Joachim B. (jar)


Lesenswert?

Yalu X. schrieb:
> Abgesehen von der zu kleinen Dimensionierung von text kannst du das 2
> Bytes umfassende °-Zeichen nicht einfach mit einem 1-Byte-Zeichen
> überschreiben, weil ja immer noch das zweite Byte stehen bleibt und als
> ungültiges Zeichen (bspw. als leeres Quadrat) ausgegeben wird.

das war nur eiin Beispiel, selbst wenn die "Dimensionierung von text" 
nicht zu klein ist kommts bei
"°C" richtig also das C an richtiger Stelle, während das "A°A" das letze 
A eben nicht richtig kommt.

Aber gut ich prüfe das nochmal, weil ich normalerweise hinter "°C" nicht 
mehr zugreife.

Wenn es doch passt dann ist mir mit einer 1 Byte längeren Hilfsvariable 
ja gedient!
Dann kann ich um diese Besonderheit drum rum programmieren.

Es nervt halt nur wenn sich immer Nebenbaustellen auftun die mit der 
Aufgabe nichts zu tun haben.

1. ist es gut das die IDE das automatisch berücksichtigt, also die 
Ausgabe seriell von ° meistert.
2. Es war mein Fehler das ich nicht bemerkte das da nun 2 Zeichen für 
°gebraucht werden.
3. ist es lösbar.

Also umsonst war der Thread nicht für mich und mit "element außerhalb 
array" nicht mal OT

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

Christopher J. schrieb:
> Ich kenne nur die Stelle 6.5.2.1 "Array Subscripting":
Das will hier keiner hören und sehen....

Arduino Fanboy wurde für blöd erklärt, und damit hat sichs dann.

von mh (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> Christopher J. schrieb:
>> Ich kenne nur die Stelle 6.5.2.1 "Array Subscripting":
> Das will hier keiner hören und sehen....
>
> Arduino Fanboy wurde für blöd erklärt, und damit hat sichs dann.

Du hast bis jetzt nicht erklärt, wieso dieser Paragraph dazu führt, dass 
es kein UB ist.

Beitrag #5929669 wurde von einem Moderator gelöscht.
von Nop (Gast)


Lesenswert?

mh schrieb:

> Du hast bis jetzt nicht erklärt, wieso dieser Paragraph dazu führt, dass
> es kein UB ist.

Das dürfte ihm auch schwer fallen, wenn man den letzten Satz tatsächlich 
mal liest:

"If both the pointer operand and the result point to elements of the 
same array object, or one past the last element of the array object, the 
evaluation shall not produce an overflow; otherwise, *the behavior is 
undefined*."

Deutlicher geht's doch nicht.

von Christopher J. (christopher_j23)


Lesenswert?

mh schrieb:
> Aus dem c11 draft N1570:6.5.6 Additive operators
[...]

Danke für den Hinweis. Da steht es tatsächlich Schwarz auf Weiß. Ich hab 
mal ein kurzes Beispiel zusammengeklöppelt (bzw. zusammenkopiert), bei 
dem der GCC ab Version 4.8 tatsächlich die Funktion "wegoptimiert":
1
#include <stdbool.h>
2
int table[4] = {1,2,3,4};
3
bool exists_in_table(int v)
4
{
5
    // return true in one of the first 4 iterations or UB due to out-of-bounds access
6
    for (int i = 0; i <= 4; i++) { // "<=" is obviously wrong here
7
        if (table[i] == v) {return true;}
8
    }
9
    return false;
10
}

wird mit dem GCC-ARM 8.2 mit -Os zu
1
exists_in_table(int):
2
        mov     r0, #1
3
        bx      lr
4
table:
5
        .word   1
6
        .word   2
7
        .word   3
8
        .word   4

D.h. der GCC liefert (mit -O2 oder -Os) bevorzugt eine "kaputte" 
Funktion, die immer "true" zurückgibt, anstatt eine kaputte Funktion zu 
liefern, die out-of-bounds Zugriff erlaubt. Dies ist mit -O0 oder -Og 
nicht der Fall.

Verändert man jedoch die Zeile
1
if (table[i] == v) {return true;}
zu
1
if (*(table+i) == v) {return true;}
so ist es egal ob man mit -Og oder -O2 kompiliert. Der GCC liefert immer 
den Code mit out-of-bounds Zugriff, d.h. für -O2
1
exists_in_table:
2
        mov     r3, #0
3
.L2:
4
        cmp     r3, #4
5
        bgt     .L6
6
        ldr     r2, .L7
7
        ldr     r2, [r2, r3, lsl #2]
8
        cmp     r2, r0
9
        beq     .L5
10
        add     r3, r3, #1
11
        b       .L2
12
.L6:
13
        mov     r0, #0
14
        bx      lr
15
.L5:
16
        mov     r0, #1
17
        bx      lr
18
.L7:
19
        .word   .LANCHOR0
20
table:
21
        .word   1
22
        .word   2
23
        .word   3
24
        .word   4

Andere Compiler die ich getestet habe (Clang, ICC) produzieren immer die 
Funktion mit dem out-of-bounds Zugriff.

Wer damit rumspielen möchte:
https://godbolt.org/z/KJXize

PS:
Für diesen Fall (d.h. für "if (table[i]==v)...") gibt der GCC 8.2 gar 
keine Warnung aus, nichtmal mit -Warray-bounds=2

: Bearbeitet durch User
von S. R. (svenska)


Lesenswert?

Joachim B. schrieb:
>> Wie wärs wenn du herausfindest was das Problem ist?
>
> keine Sorge, da bin ich bei, nur manchmal gibts hier auch gute Tipps.
> Mir die Schuld zuzuschieben ist leicht, hilft aber nicht wenn ein
> Verhalten in der IDE inkonsequent ist.

Das Verhalten in der IDE wird schon konsequent sein, die Frage ist, was 
dein Betriebssystem mit der IDE anstellt. Zeichencodierung ist ein 
ziemlich tiefes Loch...

Wenn du das komplett umgehen willst, dann nutze in deinem Quelltext 
einfach nur ASCII-Zeichen:
1
char str[] = " \xB0C";

Damit ist dein String garantiert und immer iso8859-1 codiert. Wenn 
dein serielles Terminalprogramm nun UTF-8 benutzt, wird es etwas 
falsches anzeigen.

Oder du codierst explizit UTF-8:
1
char str[] = " \xC2\xB0C";

Damit ist dein String garantiert und immer utf-8 codiert. Wenn dein 
serielles Terminalprogramm nun iso8859-1 benutzt, wird es etwas falsches 
anzeigen. Außerdem eignet sich utf-8 für einfach LCDs weniger gut als 
ein passender 8-bittiger Zeichensatz.

> Das macht die Analyse halt schwieriger.
>
> "°C" druckt seriell einwandfrei, ich erkenne das ° und kann es zum
> Display austauschen.
> "A°A" eben nicht, das nachfolgende A ist im Eimer

Du kannst ja mal statt der einzelnen Zeichen deren Hexwerte ausgeben. 
Dann solltest du recht schnell sehen, welche Bytes da nun über den Draht 
gehen.

von Joachim B. (jar)


Lesenswert?

S. R. schrieb:
> Du kannst ja mal statt der einzelnen Zeichen deren Hexwerte ausgeben.

hatte ich doch!
Beitrag "Re: Auf element außerhalb array zugreifen"

ob nun hex oder dez ist doch egal.

S. R. schrieb:
> Wenn du das komplett umgehen willst, dann nutze in deinem Quelltext
> einfach nur ASCII-Zeichen:char str[] = " \xB0C";

macht den Quelltext nicht gerade lesbarer.

von S. R. (svenska)


Lesenswert?

Joachim B. schrieb:
>> Wenn du das komplett umgehen willst, dann nutze in deinem Quelltext
>> einfach nur ASCII-Zeichen:char str[] = " \xB0C";
>
> macht den Quelltext nicht gerade lesbarer.

Aber dafür wenigstens unabhängig von deinem Editor. Du darfst das auch 
gerne hinter einem #define verstecken, wenn du möchtest.

Die Alternative wäre, dass du tatsächlich sowas wie "Text-Rendering" 
implementierst, dein Programm also tatsächlich weiß, was es auf das 
Display schreibt. Für kleine Controller in der westlichen Welt ist das 
vermutlich übertrieben, aber das wäre der richtige Weg...

von Markus F. (mfro)


Lesenswert?

S. R. schrieb:
> aber das wäre der richtige Weg...

der einfachste, bequemste (und ganz richtige) Weg wäre - zumindest 
wenn man GCC benutzt - dessen Fähigkeiten auszunutzen:

-finput-charset=<input charset>
-fexec-charset=<exec charset>

Wenn man seine Quelltext beispielsweise in utf-8 schreibt, der µC aber 
nur ISO 8859-1/Latin 1 bietet, kommt ersteres in <input charset> und 
letzteres in <exec charset>.

GCC sorgt dann (mithilfe von iconv) wie von Zauberhand selbst dafür, daß 
die richtigen Zeichen im binary landen.

von Joachim B. (jar)


Lesenswert?

S. R. schrieb:
> Aber dafür wenigstens unabhängig von deinem Editor. Du darfst das auch
> gerne hinter einem #define verstecken, wenn du möchtest.

du meinst so?
#define GRAD_CELSIUS "\xB0C"
strcat(str_ausgabe, GRAD_CELSIUS);

ja kann man machen

Markus F. schrieb:
> der einfachste, bequemste (und ganz richtige) Weg wäre - zumindest
> wenn man GCC benutzt - dessen Fähigkeiten auszunutzen:
> -finput-charset=<input charset>
> -fexec-charset=<exec charset>
>
> Wenn man seine Quelltext beispielsweise in utf-8 schreibt, der µC aber
> nur ISO 8859-1/Latin 1 bietet, kommt ersteres in <input charset> und
> letzteres in <exec charset>.
>
> GCC sorgt dann (mithilfe von iconv) wie von Zauberhand selbst dafür, daß
> die richtigen Zeichen im binary landen.

auch ein interessanter Weg, wäre nur zu klären mit was der Quelltext 
Editor arbeitet, aber ich verstehe nicht was der µC damit zu tun hat, 
der bietet mir erst mal kein "der µC aber nur ISO 8859-1/Latin 1 bietet"

Der Weg ist interessant aber mir zu hoch, ich gestehe.

Ich habe auch nicht unbedingt große Lust in der IDE rumzufummeln.
Da gibt es leichtere Wege zum gewünschten Ergebnis zu kommen.

Ich denke ich werde im Quelltext "*C" schreiben und den "*" beim 
Vorkommen von "*C" tauschen gegen das Zeichen "°" für das Display. Ist 
dann halt so.

: Bearbeitet durch User
von mh (Gast)


Lesenswert?

Joachim B. schrieb:
> Ich habe auch nicht unbedingt große Lust in der IDE rumzufummeln.
> Da gibt es leichtere Wege zum gewünschten Ergebnis zu kommen.

Meinst du das ernst? Du begehst lieber schwere Verbrechen gegen die 
Lesbarkeit deines Quelltextes, als 5 Minuten zu investieren, um deine 
IDE richtig einzustellen?

von Dirk B. (dirkb2)


Lesenswert?

Joachim B. schrieb:
> auch ein interessanter Weg, wäre nur zu klären mit was der Quelltext
> Editor arbeitet, aber ich verstehe nicht was der µC damit zu tun hat,
> der bietet mir erst mal kein "der µC aber nur ISO 8859-1/Latin 1 bietet"

Dem µC ist das auch egal - aber dir nicht. Denn du möchtest ja das 
°-Zeichen haben.

Welchen Code benutzt das Display (wenn vorhanden)?
Welchen Code benutzt das Terminal(programm), das am UART hängt?
(Kann man das evtl. einstellen)
Welchen Code benutzt der Editor/IDE?
(Kann man das evtl. auch einstellen, UTF-8 wäre ok, wenn du das im Griff 
hast.)

von Joachim B. (jar)


Lesenswert?

mh schrieb:
> Meinst du das ernst? Du begehst lieber schwere Verbrechen gegen die
> Lesbarkeit deines Quelltextes

na ja das ist geklärt wie man das verständlich und lesbar hinbekommt!
übertreib mal nicht so!

mh schrieb:
> als 5 Minuten zu investieren, um deine
> IDE richtig einzustellen?

solange die IDE schneller wechseln als ich meine Rechner werde ich doch 
nicht IDE umzustellen, es gibt Wichtigeres zu tun.
Informatiker und Dauerprogger mögen das IDE Umstellen doch leicht 
finden, für mich ist das ein blöde Nebenbaustelle ohne zu wissen was man 
damit auslöst. Ist ja nicht das erste Mal und nicht nur bei mir das wer 
irgendwie um OS Eigenheiten oder Sonstiges schmutzige HACKs anwenden 
muss. Über Patches statt Fehlerbehebung hast du bestimmt schon gelesen.

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

Joachim B. schrieb:
> werde ich doch
> nicht IDE umzustellen, es gibt Wichtigeres zu tun

Wenn das ach so Wichtige nicht mal soviel Raum läßt, zwei (!!) 
Compileroptionen zu setzen, warum hast Du dann überhaupt gefragt?

Daß das Problem bloß durch darüber reden weggeht. hast Du doch sicher 
nicht ernsthaft erwartet, oder?

von Dr. Sommer (Gast)


Angehängte Dateien:

Lesenswert?

Markus F. schrieb:
> Wenn das ach so Wichtige nicht mal soviel Raum läßt, zwei (!!)
> Compileroptionen zu setzen, warum hast Du dann überhaupt gefragt?

Normalerweise würde ich dir recht geben, dass das Setzen von 
Compileroptionen kein Hindernis sein sollte. Allerdings finde ich 
Quellcode, welcher nur mit einem bestimmten Zeichensatz korrekt 
kompilierbar ist, nicht so schön.

Es gibt aber die Möglichkeit, String-Literale als UTF8/16/32 anzulegen, 
welche dann unabhängig von der Kodierung des Quellcodes (solange diese 
dem Compiler richtig mitgeteilt wird; fast immer UTF-8) immer den 
richtigen Zeichensatz im Programm haben:

https://en.cppreference.com/w/c/language/string_literal

In C ist das leider nicht besonders nützlich. In C++ hingegen kann man 
solche Literale vom Compiler, während des Kompilierens, in einen 
beliebigen Zeichensatz (inkl. selbstdefinierte) transformieren, sodass 
das umgewandelte Ergebnis im Flash-Speicher landet und der Code dennoch 
lesbar bleibt.

Im Anhang ist ein Beispiel dazu. Da der AVR-GCC ja leider keine 
C++-Standard-Bibliothek mitliefert muss man (ziemlich hässlich) ein paar 
Standard-Hilfsklassen selbst definieren. Der Code ist zum Testen aber 
auch auf dem PC lauffähig.

Einen latin1-String kann man damit so definieren:
1
constexpr auto str1 PROGMEM = asLatin1(U"UE = Ü, OE = Ö, GRAD = °, SS = ß, Yen = ¥");

Diesen auszugeben und als Latin1 zu interpretieren sieht dann z.B. so 
aus:
1
 U  E     =     Ü  ,     O  E     =     Ö  ,     G  R  A  D     =     °...
2
55 45 20 3d 20 dc 2c 20 4f 45 20 3d 20 d6 2c 20 47 52 41 44 20 3d 20 b0...

Die Konvertierung Unicode->Latin1 ist zum Glück einfach, da die Zeichen 
128-255 (fast?) identisch sind. Die gezeigte Ausgabe sollte immer 
identisch sein, egal wie der Quelltext kodiert ist, solange der Compiler 
die Kodierung des Quelltexts kennt. Bei IDEs sollte das ja eigentlich 
immer automatisch korrekt sein.

Das Ganze funktioniert auch mit Arduino, welches ja sowieso schon C++ 
benutzt.

von Joachim B. (jar)


Lesenswert?

Markus F. schrieb:
> Wenn das ach so Wichtige nicht mal soviel Raum läßt, zwei (!!)
> Compileroptionen zu setzen, warum hast Du dann überhaupt gefragt?

weil ich die Problematik zuerst nicht verstand und mir Aufklärung 
erhoffte die ja auch gekommen ist.

> Daß das Problem bloß durch darüber reden weggeht. hast Du doch sicher
> nicht ernsthaft erwartet, oder?

nö aber es hat mir gezeigt wie ich damit umgehen möchte.

Statt ständig irgendwelche Nokia Zeichensätze in LIBs zu optimieren oder 
korrigieren, an IDEs rumpatchen oder ähnliches kümmere ich mich um was 
ich besser kann, löten z.B. 10µF an ESP32 von EN zu GND

von Markus F. (mfro)


Lesenswert?

Dr. Sommer schrieb:
> Allerdings finde ich
> Quellcode, welcher nur mit einem bestimmten Zeichensatz korrekt
> kompilierbar ist, nicht so schön.

Du kannst -finput-charset (meist) auch weglassen, dann findet GCC den 
auch aufgrund der Locale-Einstellung selber raus.

P.S.: ... und was TMP angeht, bin ich seit Jahren zwischen 
"beeindruckend, was da alles geht" und "brrrrr, diese Syntax" hin- und 
hergerissen.

: Bearbeitet durch User
von S. R. (svenska)


Lesenswert?

Joachim B. schrieb:
> Ich denke ich werde im Quelltext "*C" schreiben
> und den "*" beim Vorkommen von "*C" tauschen gegen
> das Zeichen "°" für das Display. Ist dann halt so.

Du tust mir Leid. Nimm wenigstens ein Zeichen, was du sonst nirgends 
benutzt und definiere das für dich um, z.B. '}', '$' oder '`'. Das ist 
dann zwar genauso hirntot, braucht aber weniger grässlichen Code drum 
herum.

von Dr. Sommer (Gast)


Lesenswert?

Markus F. schrieb:
> Du kannst -finput-charset (meist) auch weglassen, dann findet GCC den
> auch aufgrund der Locale-Einstellung selber raus.

Ja. Leider funktioniert das nur bei ASCII-kompatiblen Zeichensätzen. 
Wenn man z.B. den Code als UTF-16 kompiliert, kann man die 
Standard-Header nicht mehr inkludieren, weil die ASCII sind... Das ist 
das Witz an den Literalen mit Zeichensatz-Angabe (U"blub") - egal 
welchen Zeichensatz der Code hat, das kompilierte Programm sieht nur 
Text im gewünschten Zeichensatz (hier: UTF-32). Einen ganz ähnlichen 
Trick kann man machen, um die UTF-16-Little-Endian-Strings für 
USB-Deskriptoren zu generieren, ohne den Sourcecode auf UTF-16 umstellen 
zu müssen (was genanntes Header-Problem bewirken würde).

Markus F. schrieb:
> P.S.: ... und was TMP angeht, bin ich seit Jahren zwischen
> "beeindruckend, was da alles geht" und "brrrrr, diese Syntax" hin- und
> hergerissen.

Ja die ganze C++-Syntax ist nicht so schön. Sie sollte halt 
abwärtskompatibel zu C sein. Dafür kann man Dinge schön in Bibliotheken 
kapseln und dann hübsche APIs bauen, wie die gezeigte asLatin1-Funktion.

von Dirk B. (dirkb2)


Lesenswert?

Joachim B. schrieb:
> du meinst so?
> #define GRAD_CELSIUS "\xB0C"
> strcat(str_ausgabe, GRAD_CELSIUS);

so ganz geht das so nicht.

die Escapesequenz \x ist gierig. Die nimmt alle folgenden Hexziffern.
Da das C auch dazugehört wird es noch mit umgewandelt.

In C kann man auch Stringliterale aneinander ketten:
1
#define GRAD "\xB0"
2
sprintf(str_ausgabe, "%.1f"GRAD"C", temp);

von Joachim B. (jar)


Lesenswert?

Dirk B. schrieb:
> so ganz geht das so nicht.
>
> die Escapesequenz \x ist gierig. Die nimmt alle folgenden Hexziffern.
> Da das C auch dazugehört wird es noch mit umgewandelt.
>
> In C kann man auch Stringliterale aneinander ketten:
> #define GRAD "\xB0"
> sprintf(str_ausgabe, "%.1f"GRAD"C", temp);

danke hilfreich und konstruktiv!

von leo (Gast)


Lesenswert?

Joachim B. schrieb:
>> sprintf(str_ausgabe, "%.1f"GRAD"C", temp);
>
> danke hilfreich und konstruktiv!

Und das kann man auch noch leserlicher gestalten, indem man WS 
einstreut:
1
  printf("%.1f" GRAD "C\n", 22.5);
Dieser wird einfach entfernt. I.e. sehr nuetzlich, um lange Strings zu 
definieren.

leo

von Rolf M. (rmagnus)


Lesenswert?

Joachim B. schrieb:
> mh schrieb:
>> als 5 Minuten zu investieren, um deine
>> IDE richtig einzustellen?
>
> solange die IDE schneller wechseln als ich meine Rechner werde ich doch
> nicht IDE umzustellen, es gibt Wichtigeres zu tun.

Gut, wenn du lieber umständliche und unschöne Verrenkungen in deinem 
Code einbaust, um zu vermeiden, eine simple Einstellung anzupassen, 
kannst du das ja gerne tun. Dann brauchst du dich aber nicht darüber 
beschweren, dass du umständliche und unschöne Verrenkungen brauchst, 
damit es so tut, wie du willst.

von Joachim B. (jar)


Lesenswert?

S. R. schrieb:
> Joachim B. schrieb:
>> Ich denke ich werde im Quelltext "*C" schreiben
>> und den "*" beim Vorkommen von "*C" tauschen gegen
>> das Zeichen "°" für das Display. Ist dann halt so.
>
> Du tust mir Leid. Nimm wenigstens ein Zeichen, was du sonst nirgends
> benutzt und definiere das für dich um, z.B. '}', '$' oder '`'. Das ist
> dann zwar genauso hirntot, braucht aber weniger grässlichen Code drum
> herum.

OK, ich bin hirntot, habe aber meine Lösung gefunden
1
for(uint8_t linecounter = 0; linecounter<MAX_LINES; linecounter++) {
2
  if( strchr( (char*)&menu[HAUPT_SCREEN][linecounter][0], GRAD) ) {
3
    Serial.print("| "); Serial.print(leftstr((char*)&menu[HAUPT_SCREEN][linecounter][1], 11));
4
        Serial.print("°"); Serial.println("C|");
5
  } // if(strchr(menu[HAUPT_SCREEN][linecounter][0], GRAD))
6
  else {
7
    Serial.print("|"); 
8
    if( strlen(&menu[HAUPT_SCREEN][linecounter][0])==14 || strchr(&menu[HAUPT_SCREEN][linecounter][0], '%' ) )
9
      Serial.print(&menu[HAUPT_SCREEN][linecounter][0]);
10
      if(strchr(&menu[HAUPT_SCREEN][linecounter][0], '%' ))
11
        Serial.print(" ");
12
    else
13
      if(!strlen(&menu[HAUPT_SCREEN][linecounter][0]))
14
        Serial.print(leerzeile);
15
    Serial.println("|");         
16
  }
17
} // for(linecounter = 0; linecounter<MAX_LINES; linecounter++)

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Oh graus, wie furchtbar... Macht es dich irgendwie an 100x 
"menu[HAUPT_SCREEN][linecounter]" zu schreiben? Sind temporäre Variablen 
irgendwie verboten? Dazu noch die vermurkste Einrückung, sinnloses 
mehrfaches Aufrufen von strchr und strlen... Wie wäre es damit:
1
for(uint_fast8_t linecounter = 0; linecounter<MAX_LINES; ++linecounter) {
2
  const char* m = menu[HAUPT_SCREEN][linecounter];
3
  
4
  if(strchr (m, GRAD) ) {
5
    Serial.print("| ");
6
    Serial.print(m+1, 11);
7
    Serial.print("°");
8
    Serial.println("C|");
9
  } else {
10
    Serial.print("|");
11
    
12
    if (strchr (m, '%')) {
13
      Serial.print (m);
14
      Serial.print (' ');
15
    } else {
16
      size_t l = strlen (m);
17
      if (l == 14)
18
        Serial.print (m);
19
      else if (l == 0)
20
        Serial.print (leerzeile);
21
    }
22
    Serial.println("|");         
23
  }
24
}

Ganz davon abgesehen dass das nur funktioniert wenn nur ein einziges 
Gradzeichen da ist und dieses ganz am Anfang steht (wozu dann strchr?). 
Was der Blödsinn mit dem Prozentzeichen soll weiß wohl keiner. Und was 
ist "HAUPT_SCREEN" für ein tolles Denglisch?

von Joachim B. (jar)


Lesenswert?

Dr. Sommer schrieb:
> Oh graus, wie furchtbar... Macht es dich irgendwie an 100x
> "menu[HAUPT_SCREEN][linecounter]" zu schreiben? Sind temporäre Variablen
> irgendwie verboten?

keine Ahnung was die ARDUINO IDE so im Detail treibt

in AVR Studio hätte ich das sicher anders geschrieben, aber mir geht der 
SRAM aus, nein es macht mich nicht an öfter 
"menu[HAUPT_SCREEN][linecounter]" zu schreiben, aber es funktioniert, 
ist ja nur im Source und ich kannka auch c&p, als wer das öfter schreibt 
hat Compi eh nicht verstanden.

> Dazu noch die vermurkste Einrückung,

Ist auch nicht auf meinen Mist gewachsen aber in der IDE nützlich und 
hier im Forum quäle ich mich immer das richtig einzufügen.

> sinnloses mehrfaches Aufrufen von strchr und strlen... Wie wäre es damit:

schaue ich mir gerne an, gegen konstruktive Kritik habe ich nichts, kann 
ja nur dazulernen

> Ganz davon abgesehen dass das nur funktioniert wenn nur ein einziges
> Gradzeichen da ist und dieses ganz am Anfang steht (wozu dann strchr?).

Es ist nur ein einzelnes GRAD Zeichen auf dem Nokia Display, deswegen 
strchr, also erst mal in der Zeile suchen (war so eine Idee um mich 
nicht auf den Platz festzulegen, aber du hast Recht ich schreibe ab 
pos12 Serial.print raus weil es da nun mal ist.

Ich hätte auch strrchr nehmen können, witzigerweise ist das nicht 
C-Standard jedenfalls in meinem GCC, hatte ich nachgebaut, Quellen für 
strrchr gibts ja c-konform im Netz.

> Was der Blödsinn mit dem Prozentzeichen soll weiß wohl keiner.

muss das einer wissen? hätte ich hier auch rausnehmen können, aber nun 
gut wer neugierig ist soll belohnt werden % für den DHT22 humidity

und da alle = Zeichen untereinander stehen (sollen) aber % ein Zeichen 
kürzer ist als °C braucht es hinter % noch ein SPACE

Und was
> ist "HAUPT_SCREEN" für ein tolles Denglisch?

wenns dich stört kann man es auch zu "MAIN_SCREEN" oder "HAUPT_BILD" 
umbenennen, wer weiter keine Sorgen hat muss glücklich sein.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Joachim B. schrieb:
> in AVR Studio hätte ich das sicher anders geschrieben

Das benutzt doch den selben Compiler (AVR-GCC)...

Joachim B. schrieb:
> aber mir geht der
> SRAM aus,

was hat das damit zu tun? Die Variable wird selbstverständlich 
wegoptimiert bzw. in ein Register gelegt. Es könnte sogar effizienter 
werden, wenn "menu" ein Zeiger ist.

Joachim B. schrieb:
> aber es funktioniert,

Ist aber furchtbar zu lesen. Man muss ständig gucken ob das wirklich 
jedes Mal das Gleiche ist. Merke: Code wird 1x geschrieben und 100x 
gelesen..

Joachim B. schrieb:
> als wer das öfter schreibt
> hat Compi eh nicht verstanden.

Äh, was? Wer öfter den selben Code schreibt, macht was falsch! Der 
Compiler hilft dir dabei, Wiederholungen zu vermeiden, insbesondere in 
C++ (Arduino). Das Prinzip heißt "DRY" (don't repeat yourself). Wenn du 
den Ausdruck ändern willst, musst du ihn sonst überall ändern. Außerdem, 
warum immer das &? Warum manchmal der cast auf char*, manchmal nicht? 
Inkonsistent!

Joachim B. schrieb:
> aber in der IDE nützlich

In welcher IDE ist falsche Einrückung nützlich?

Joachim B. schrieb:
> hier im Forum quäle ich mich immer das richtig einzufügen.

Einfach Copy&Paste. Die Forensoftware macht das automatisch richtig, 
wenn man code-Tags nutzt.

Joachim B. schrieb:
> Ich hätte auch strrchr nehmen können, witzigerweise ist das nicht
> C-Standard

Doch.

Joachim B. schrieb:
> witzigerweise ist das nicht
> C-Standard jedenfalls in meinem GCC

Dann stimmt mit dem irgendwas nicht.

Warum iterierst du nicht einfach den String Zeichen für Zeichen, gibst 
Grad-Zeichen umgewandelt aus und den Rest so wie er ist? Dann kannst du 
eine beliebige Anzahl Grad-Zeichen umwandeln.

Joachim B. schrieb:
> und da alle = Zeichen untereinander stehen (sollen) aber % ein Zeichen
> kürzer ist als °C braucht es hinter % noch ein SPACE

Dann sollte man das genau an der Stelle platzieren, an der man auch das 
% einfügt. Und nicht irgendwann später prüfen ob da ein % ist. Stell dir 
vor du willst später noch irgendeinen anderen prozentualen Wert 
ausgeben. Dann wunderst du dich warum da plötzlich ein Leerzeichen 
erscheint...

von Joachim B. (jar)


Lesenswert?

Dr. Sommer schrieb:
> Joachim B. schrieb:
>> in AVR Studio hätte ich das sicher anders geschrieben
>
> Das benutzt doch den selben Compiler (AVR-GCC)...

du weisst aber das es da verschiedene Versionen gibt?
Die Behauptung alles GCC ist zwar richtig aber nicht zielführend bei 
unterschiedlichen Versionen!

> Joachim B. schrieb:
>> aber mir geht der
>> SRAM aus,
>
> was hat das damit zu tun? Die Variable wird selbstverständlich
> wegoptimiert bzw. in ein Register gelegt. Es könnte sogar effizienter
> werden, wenn "menu" ein Zeiger ist.

Ich bemerke halt Seiteneffekte und kämpfe noch mit Zeiger, das habe ich 
zugegeben noch nicht vollständig verstanden, ich weiss zwar das ein 
Array char zeiger[] ein Zeiger ist, aber wenn ich denüber 2 Funktinen 
weiterleiten will meckert der Compiler immer, wann dereferenziert man 
und wann nicht?

> Joachim B. schrieb:
>> aber es funktioniert,
>
> Ist aber furchtbar zu lesen. Man muss ständig gucken ob das wirklich
> jedes Mal das Gleiche ist. Merke: Code wird 1x geschrieben und 100x
> gelesen..

oh ich schreibe Code auch öfter, ich lerne täglich dazu und was bei 
einem Compiler gcc in der Arduino IDE mal funktionierte:

char *var_ptr = NULL;
var_ptr = malloc(10);

wird im ESP32 Compiler angemeckert!

const char* zu char*

das soll jemand verstehen....

der eine macht strxxx Funktionen zu unsigned char der andere zu char und 
einer meckert IMMER

> Joachim B. schrieb:
>> als wer das öfter schreibt
>> hat Compi eh nicht verstanden.
>
> Äh, was? Wer öfter den selben Code schreibt, macht was falsch!

mache ich doch nicht das öfter schreiben, lies mal genauer

> Der Compiler hilft dir dabei, Wiederholungen zu vermeiden, insbesondere in
> C++ (Arduino). Das Prinzip heißt "DRY" (don't repeat yourself). Wenn du
> den Ausdruck ändern willst, musst du ihn sonst überall ändern.

auch da gibt es suchen & ersetzen, nützlich wenn man es kennt

> Außerdem, warum immer das &? Warum manchmal der cast auf char*, manchmal nicht?
> Inkonsistent!

weil es irgendwann wo klemmte, soll ich mich wiederholen?
nun habe ich es ja auch stellenweise ohne &, kommt halt drauf an ob ich 
aufs 0te Element zugreifen will oder nicht.

> Joachim B. schrieb:
>> aber in der IDE nützlich
>
> In welcher IDE ist falsche Einrückung nützlich?

falsch ist deiner Meinung nach was du hier siehst, Forum != IDE Editor

> Joachim B. schrieb:
>> hier im Forum quäle ich mich immer das richtig einzufügen.
>
> Einfach Copy&Paste. Die Forensoftware macht das automatisch richtig,
> wenn man code-Tags nutzt.

nutze ich, aber weil ich nicht jedesmal händich das Editorfenster 
anfassen will an verschiedenen Compis, sitzt du immer vor demselben 
Compi mit 32" Monitor?

> Joachim B. schrieb:
>> Ich hätte auch strrchr nehmen können, witzigerweise ist das nicht
>> C-Standard
>
> Doch.

dann eben nicht in jedem Compiler implementiert!
Mag sein das du Ahnung hast, aber nicht überall

> Joachim B. schrieb:
>> witzigerweise ist das nicht
>> C-Standard jedenfalls in meinem GCC
>
> Dann stimmt mit dem irgendwas nicht.
>
> Warum iterierst du nicht einfach den String Zeichen für Zeichen, gibst
> Grad-Zeichen umgewandelt aus und den Rest so wie er ist? Dann kannst du
> eine beliebige Anzahl Grad-Zeichen umwandeln.

Einen Parser hatte ich mal vor 30 Jahren geschrieben für die Umwandlung 
von Quelltext PC1500 in Tokens die mit bin2wav geschriebe werden 
konnten.

Heute beschäftigt mich eher anderes.

> Joachim B. schrieb:
>> und da alle = Zeichen untereinander stehen (sollen) aber % ein Zeichen
>> kürzer ist als °C braucht es hinter % noch ein SPACE
>
> Dann sollte man das genau an der Stelle platzieren, an der man auch das
> % einfügt. Und nicht irgendwann später prüfen ob da ein % ist. Stell dir
> vor du willst später noch irgendeinen anderen prozentualen Wert
> ausgeben. Dann wunderst du dich warum da plötzlich ein Leerzeichen
> erscheint...

ja sowas passiert mir ab und an mal bei "meiner" Art der Programmierung, 
aber ich bin halt eher Hardwareman und lerne an so vielen Baustellen, 
darf aber auch das Ziel nicht aus den Augen verlieren, will ich das 
Projekt zum Laufen bekommen oder nur der perfekte Progger werden, aber 
das Projekt läuft nie?

trotzdem danke für konstruktive Tipps, ich kann bestimmt davon was 
mitnehmen und umsetzen.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Joachim B. schrieb:
> du weisst aber das es da verschiedene Versionen gibt?

Ach, ich dachte es gibt nur die Version 8.2.0. ... Die sind ziemlich 
abwärtskompatibel. C++03 und C99 sollten mit Arduino und Atmel Studio zu 
98% identisch gehen, bis auf kleine Verbesserungen beim Optimizer.

Joachim B. schrieb:
> ich weiss zwar das ein
> Array char zeiger[] ein Zeiger ist

Ein Array ist ein Array und kein Zeiger. Es wird zu einem Zeiger wenn 
man es übergibt oder zuweist, wie in meinem Beispiel mit der "m" 
Variable.

Joachim B. schrieb:
> aber wenn ich denüber 2 Funktinen
> weiterleiten will meckert der Compiler immer

Hä? Zeigen. Zeiger kann man beliebig oft übergeben.

Joachim B. schrieb:
> wann dereferenziert man
> und wann nicht?

Man dereferenziert nur genau dann, wenn man das Ziel des Zeigers lesen 
oder schreiben will.

Joachim B. schrieb:
> char *var_ptr = NULL;
> var_ptr = malloc(10);
>
> wird im ESP32 Compiler angemeckert!

Wenn das ein C Compiler ist, glaube ich das nicht. Der Fehler liegt 
woanders. Wenn es C++ ist (z.B. Arduino), dann muss da noch ein Cast 
hin:
1
var_ptr = static_cast<char*> (malloc (10));

wobei man in C++ sowieso lieber "new" nutzen sollte
1
var_ptr = new char [10];

Was aber leider beim AVR-GCC wohl nicht geht. Beim ESP vielleicht schon, 
kA.

Joachim B. schrieb:
> der eine macht strxxx Funktionen zu unsigned char der andere zu char und
> einer meckert IMMER

Was? Die Standard-C-String-Funktionen nutzen alle char* bzw. const 
char*.

Joachim B. schrieb:
> auch da gibt es suchen & ersetzen, nützlich wenn man es kennt

Nein, das gibt eine Katastrophe, wenn man zu viel ersetzt. Code muss man 
so schreiben, dass man Suchen & Ersetzen so gut wie nie braucht. Da du 
offenbar Anfänger bist, kannst du das den erfahreneren Programmierern 
ruhig glauben...

Joachim B. schrieb:
> weil es irgendwann wo klemmte, soll ich mich wiederholen?

Ja, zeige die exakte Stelle an er es klemmen soll.

Joachim B. schrieb:
> nun habe ich es ja auch stellenweise ohne &, kommt halt drauf an ob ich
> aufs 0te Element zugreifen will oder nicht.

& braucht man bei Arrays fast nie. Wenn du einen Zeiger auf das 7. 
Element brauchst, kannst du
1
&array[7]

machen, aber
1
array+7

ist viel schöner. Wenn man einen Zeiger auf das 0. Element braucht, 
reicht einfach "array". In meinem abgewandelten Code ist nirgendwo ein 
"&" oder ein Cast!

Joachim B. schrieb:
> falsch ist deiner Meinung nach was du hier siehst, Forum != IDE Editor

Dass schlecht eingerückter Code schlecht ist, ist allgemeiner Konsens. 
Auch im Forum darf man anderen Teilnehmern das Lesen erleichtern, indem 
man korrekt einrückt. Das ist, wie gesagt, dank Copy&Paste überhaupt 
kein Problem. Hast du den Code fürs Forum extra zerstückelt? Dein Code 
sieht aus, als würde er ganz anders funktionieren, als er tatsächlich 
ist! Da fehlen ein paar { und }, um ihn so funktionieren zu lassen, wie 
er aussieht.

Du schreibst:
1
if( strlen(&menu[HAUPT_SCREEN][linecounter][0])==14 || strchr(&menu[HAUPT_SCREEN][linecounter][0], '%' ) )
2
  Serial.print(&menu[HAUPT_SCREEN][linecounter][0]);
3
  if(strchr(&menu[HAUPT_SCREEN][linecounter][0], '%' ))
4
    Serial.print(" ");
5
else
6
  if(!strlen(&menu[HAUPT_SCREEN][linecounter][0]))
7
    Serial.print(leerzeile);

was so aussieht als wären da Klammern wie in
1
if( strlen(&menu[HAUPT_SCREEN][linecounter][0])==14 || strchr(&menu[HAUPT_SCREEN][linecounter][0], '%' ) ) {
2
  Serial.print(&menu[HAUPT_SCREEN][linecounter][0]);
3
  if(strchr(&menu[HAUPT_SCREEN][linecounter][0], '%' ))
4
    Serial.print(" ");
5
} else {
6
  if(!strlen(&menu[HAUPT_SCREEN][linecounter][0]))
7
    Serial.print(leerzeile);
8
}

aber tatsächlich so ist
1
if( strlen(&menu[HAUPT_SCREEN][linecounter][0])==14 || strchr(&menu[HAUPT_SCREEN][linecounter][0], '%' ) )
2
  Serial.print(&menu[HAUPT_SCREEN][linecounter][0]);
3
if(strchr(&menu[HAUPT_SCREEN][linecounter][0], '%' ))
4
  Serial.print(" ");
5
else
6
  if(!strlen(&menu[HAUPT_SCREEN][linecounter][0]))
7
    Serial.print(leerzeile);

Also etwas komplett anderes. Das ist sehr gefährlich - in 2 Monaten 
stolperst du selbst darunter.

Joachim B. schrieb:
> nutze ich, aber weil ich nicht jedesmal händich das Editorfenster
> anfassen will an verschiedenen Compis, sitzt du immer vor demselben
> Compi mit 32" Monitor?

Was ist ein "Compi"? Ein Compiler? Seit wann haben Compiler Monitore? 
Tippst du den Code vom einem Computer fehlerhaft an den anderen Computer 
ab? Warum kopierst du ihn nicht per USB-Stick, Netzwerk, Dropbox, ...

Joachim B. schrieb:
> dann eben nicht in jedem Compiler implementiert!

Dann ist das kein C(++)-Compiler. Außerdem ist das wenn schon in der 
Standardbibliothek implementiert. Hab gerade ausprobiert, mit Arduino 
gehts.

Joachim B. schrieb:
> Einen Parser hatte ich mal vor 30 Jahren geschrieben für die Umwandlung
> von Quelltext PC1500 in Tokens die mit bin2wav geschriebe werden
> konnten.

Das ist so simpel, dass das kaum den Begriff "Parser" verdient. Einfach 
nur eine olle Schleife... Wäre auch noch besser lesbar als deine 
verschachtelte Logik die nur in Spezialfällen funktioniert...

Joachim B. schrieb:
> will ich das
> Projekt zum Laufen bekommen oder nur der perfekte Progger werden, aber
> das Projekt läuft nie?

Wenn du schon im Forum die Experten fragst, kannst du auch den Rat 
annehmen...

von S. R. (svenska)


Lesenswert?

Joachim B. schrieb:
>> Du tust mir Leid. Nimm wenigstens ein Zeichen, was du sonst nirgends
>> benutzt und definiere das für dich um, z.B. '}', '$' oder '`'. Das ist
>> dann zwar genauso hirntot, braucht aber weniger grässlichen Code drum
>> herum.
>
> OK, ich bin hirntot, habe aber meine Lösung gefunden

Deine Lösung ist hirntot, nicht du.

> Serial.print("°"); Serial.println("C|");

Uuund: Wieder hast du eine Abhängigkeit vom Zeichensatz des Quelltextes 
zum Zeichensatz des Displays. Wieder nix gelernt, den gleichen Fehler 
wieder gemacht. Gratuliere.

von Joachim B. (jar)


Lesenswert?

Dr. Sommer schrieb:
> Joachim B. schrieb:
>> aber wenn ich denüber 2 Funktinen
>> weiterleiten will meckert der Compiler immer
>
> Hä? Zeigen. Zeiger kann man beliebig oft übergeben.

char var[] = {"huhu"};

void func1(char *);
void func2(char *);

func1(var); // bis hier kam ich ja im C-Kurs noch mit

aber dann wenn func1 func2 aufruft
klappt bei mir nie, egal ob mit cast oder nicht
dabei sollte doch einfach immer nur eine Adresse weitergereicht werden, 
die Adresse vom char ARRAY

> Joachim B. schrieb:
>> wann dereferenziert man
>> und wann nicht?
>
> Man dereferenziert nur genau dann, wenn man das Ziel des Zeigers lesen
> oder schreiben will.

so lernte ich das? aber wenn func1 func2 aufruft warum klappt das nicht?

> Joachim B. schrieb:
>> char *var_ptr = NULL;
>> var_ptr = malloc(10);
>>
>> wird im ESP32 Compiler angemeckert!
>
> Wenn das ein C Compiler ist, glaube ich das nicht. Der Fehler liegt
> woanders. Wenn es C++ ist (z.B. Arduino), dann muss da noch ein Cast
> hin:

nee genau anders rum, im Arduino unter #if defined(_AVR_) klappt
char *var_ptr = NULL;
var_ptr = malloc(10);
1
void setup() {
2
#if defined (ESP32)
3
  #pragma message "ESP32"
4
#elif defined (__AVR__)
5
  #pragma message "AVR"
6
#endif
7
8
char *var_ptr = NULL;
9
var_ptr = malloc(10);
10
}
11
12
void loop() {
13
  // put your main code here, to run repeatedly:
14
15
}
16
17
/*
18
 ergibt:
19
20
D:\atmel\__ARDUINO__\char_var\char_var.ino: In function 'void setup()':
21
D:\atmel\__ARDUINO__\char_var\char_var.ino:3:19: note: #pragma message: ESP32
22
   #pragma message "ESP32"
23
                   ^
24
char_var:9:17: error: invalid conversion from 'void*' to 'char*' [-fpermissive]
25
 var_ptr = malloc(10);
26
                 ^
27
exit status 1
28
invalid conversion from 'void*' to 'char*' [-fpermissive]
29
*/

und
1
void setup() {
2
#if defined (ESP32)
3
  #pragma message "ESP32"
4
#elif defined (__AVR__)
5
  #pragma message "AVR"
6
#endif
7
8
char *var_ptr = NULL;
9
var_ptr = malloc(10);
10
}
11
12
void loop() {
13
  // put your main code here, to run repeatedly:
14
15
}
16
17
/*
18
 ergibt:
19
20
F:\__ARDUINO__\char_var\char_var.ino: In function 'void setup()':
21
F:\__ARDUINO__\char_var\char_var.ino:5:19: note: #pragma message: AVR
22
   #pragma message "AVR"
23
                   ^~~~~
24
Der Sketch verwendet 444 Bytes (1%) des Programmspeicherplatzes. Das Maximum sind 32256 Bytes.
25
Globale Variablen verwenden 9 Bytes (0%) des dynamischen Speichers, 2039 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.
26
*/


> Was? Die Standard-C-String-Funktionen nutzen alle char* bzw. const
> char*.

siehst du auch du schreibst bzw. also ODER

> Da du
> offenbar Anfänger bist, kannst du das den erfahreneren Programmierern
> ruhig glauben...

was proggen angeht bin ich Einäugiger unter Blinden


> Joachim B. schrieb:
>> nun habe ich es ja auch stellenweise ohne &, kommt halt drauf an ob ich
>> aufs 0te Element zugreifen will oder nicht.
>
> & braucht man bei Arrays fast nie. Wenn du einen Zeiger auf das 7.
> Element brauchst, kannst du

bei mehrdimensionalen Arrays kann ich nicht einfach die [][] alle 
weglassen
Ich will ja aufs Richtige zugreifen, menu und da z.B. Zeile oder Spalte

> In meinem abgewandelten Code ist nirgendwo ein
> "&" oder ein Cast!

ich weiss das es möglich *(var+s+(z * s_laenge))= ist aber in meinem 
Verständnis tue ich mich leichter bei mehrdimensionalen Array bei 
&var[][] für die Adresse oder bei var[][]= zu bleiben


> Joachim B. schrieb:
>> nutze ich, aber weil ich nicht jedesmal händich das Editorfenster
>> anfassen will an verschiedenen Compis, sitzt du immer vor demselben
>> Compi mit 32" Monitor?
>
> Was ist ein "Compi"? Ein Compiler? Seit wann haben Compiler Monitore?
> Tippst du den Code vom einem Computer fehlerhaft an den anderen Computer
> ab? Warum kopierst du ihn nicht per USB-Stick, Netzwerk, Dropbox, ...

Computer

> Wenn du schon im Forum die Experten fragst, kannst du auch den Rat
> annehmen...

och mache ich schon, nur echte Experten sind selten

wie gesagt, dein Text:
> Wenn das ein C Compiler ist, glaube ich das nicht. Der Fehler liegt
> woanders. Wenn es C++ ist (z.B. Arduino), dann muss da noch ein Cast
> hin:

die Realität:

nee genau anders rum, im Arduino unter #if defined(_AVR_) klappt
char *var_ptr = NULL;
var_ptr = malloc(10);
1
void setup() {
2
#if defined (ESP32)
3
  #pragma message "ESP32"
4
#elif defined (__AVR__)
5
  #pragma message "AVR"
6
#endif
7
8
char *var_ptr = NULL;
9
var_ptr = malloc(10);
10
}
11
12
void loop() {
13
  // put your main code here, to run repeatedly:
14
15
}
16
17
/*
18
 ergibt:
19
20
D:\atmel\__ARDUINO__\char_var\char_var.ino: In function 'void setup()':
21
D:\atmel\__ARDUINO__\char_var\char_var.ino:3:19: note: #pragma message: ESP32
22
   #pragma message "ESP32"
23
                   ^
24
char_var:9:17: error: invalid conversion from 'void*' to 'char*' [-fpermissive]
25
 var_ptr = malloc(10);
26
                 ^
27
exit status 1
28
invalid conversion from 'void*' to 'char*' [-fpermissive]
29
*/

und
1
void setup() {
2
#if defined (ESP32)
3
  #pragma message "ESP32"
4
#elif defined (__AVR__)
5
  #pragma message "AVR"
6
#endif
7
8
char *var_ptr = NULL;
9
var_ptr = malloc(10);
10
}
11
12
void loop() {
13
  // put your main code here, to run repeatedly:
14
15
}
16
17
/*
18
 ergibt:
19
20
F:\__ARDUINO__\char_var\char_var.ino: In function 'void setup()':
21
F:\__ARDUINO__\char_var\char_var.ino:5:19: note: #pragma message: AVR
22
   #pragma message "AVR"
23
                   ^~~~~
24
Der Sketch verwendet 444 Bytes (1%) des Programmspeicherplatzes. Das Maximum sind 32256 Bytes.
25
Globale Variablen verwenden 9 Bytes (0%) des dynamischen Speichers, 2039 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.
26
*/

unter AVR gehts, unter ESP wird es angemeckert, also genau 
entgegengesetzt zu deiner Aussage

: Bearbeitet durch User
von S. R. (svenska)


Lesenswert?

Joachim B. schrieb:
>> Hä? Zeigen. Zeiger kann man beliebig oft übergeben.
>
> char var[] = {"huhu"};
>
> void func1(char *);
> void func2(char *);
>
> func1(var); // bis hier kam ich ja im C-Kurs noch mit
1
void func2(char* x) {
2
  printf("%s", x);
3
}
4
5
void func1(char* param) {
6
  func2(param);
7
}
8
9
int main() {
10
  func1(var);
11
}

>> Joachim B. schrieb:
>>> char *var_ptr = NULL;
>>> var_ptr = malloc(10);
>>>
>>> wird im ESP32 Compiler angemeckert!

Ja, weil malloc() ein void* zurückgibt.
In C++ wird nicht automatisch konvertiert.
1
int main() {
2
  var_ptr = (char*)malloc(10);
3
}

Fertig. Wenn der avr-gcc das akzeptiert und der esp32-gcc nicht, dann 
könnte das auch am Aufruf liegen. Man kann den gcc beliebig streng 
machen.

>> Was? Die Standard-C-String-Funktionen nutzen
>> alle char* bzw. const char*.
>
> siehst du auch du schreibst bzw. also ODER

Du weißt aber schon, was der Unterschied zwischen "char*" und "const 
char*" ist, oder? Und warum man grundsätzlich alle Funktionen "static" 
und alle Parameter "const" machen sollte, wenn es möglich ist.

>> Wenn du schon im Forum die Experten fragst, kannst du auch den Rat
>> annehmen...
>
> och mache ich schon, nur echte Experten sind selten

Du ignorierst also gute Ratschläge, weil sie deiner Meinung nach nicht 
von einem "echten Experten" stammten? Du kennst den Denkfehler "true 
scotsman"?

> unter AVR gehts, unter ESP wird es angemeckert, also genau
> entgegengesetzt zu deiner Aussage

Ich habe das Gefühl, dass du keine Ahnung hast, was du eigentlich tust. 
Eher nur so rumgemurkse bis es halbwegs tut. Ob der Code gut ist (oder 
sich halbwegs gut optimieren lässt), ob er verständlich ist oder nochmal 
woanders benutzt werden kann, scheißegal, es funktioniert.

Liege ich mit der Vermutung richtig?

von Dr. Sommer (Gast)


Lesenswert?

Joachim B. schrieb:
> aber dann wenn func1 func2 aufruft
> klappt bei mir nie, egal ob mit cast oder nicht

Dann zeig diesen Aufruf doch mal. Du kannst den Parameter direkt so wie 
er ist weiter übergeben.

Joachim B. schrieb:
> aber wenn func1 func2 aufruft warum klappt das nicht?

Wahrscheinlich ist dein Aufruf falsch.

Joachim B. schrieb:
> im Arduino unter #if defined(AVR) klappt

Da gibt's aber ne Warning:
1
warning: invalid conversion from 'void*' to 'char*'

weil es falsch ist. Arduino bügelt die Warnings nur weg. Beim ESP32 wird 
es korrekterweise als Fehler gezeigt. Korrekterweise kommt da in C++ 
(Arduino) immer ein Cast hin.

Joachim B. schrieb:
> siehst du auch du schreibst bzw. also ODER

Logisch... z.B. strcpy bekommt einen char* und einen const char* - 
ersterer wird überschrieben, zweiterer nicht.

Joachim B. schrieb:
> bei mehrdimensionalen Arrays kann ich nicht einfach die [][] alle
> weglassen

Das nicht, aber wenn du einen Zeiger auf ein Element eines Unter-Arrays 
haben willst, würde ich das letzte [] weglassen und ein "+" nehmen.

Joachim B. schrieb:
> unter AVR gehts, unter ESP wird es angemeckert, also genau
> entgegengesetzt zu deiner Aussage

Wie gesagt - es wird immer angemeckert, nur die Arduino-IDE stellt den 
Compiler auf nachlässig, damit die Anfänger länger nach den Fehlern 
suchen müssen. Nur in C ist so etwas erlaubt.

von Joachim B. (jar)


Lesenswert?

S. R. schrieb:
> Du weißt aber schon, was der Unterschied zwischen "char*" und "const
> char*" ist

ja so eingermaßen

> Du ignorierst also gute Ratschläge, weil sie deiner Meinung nach nicht
> von einem "echten Experten" stammten?

und wie unterscheidet man, man prüft die Aussagen auf Funktion :)

> Ich habe das Gefühl, dass du keine Ahnung hast, was du eigentlich tust.
> Eher nur so rumgemurkse bis es halbwegs tut. Ob der Code gut ist (oder
> sich halbwegs gut optimieren lässt), ob er verständlich ist oder nochmal
> woanders benutzt werden kann, scheißegal, es funktioniert.
>
> Liege ich mit der Vermutung richtig?

nö absolut falsch

witzigerweise funktioniert mein purer C-Code seit 1990, den ich vom PC 
unter DOS über Atari 68k bis heute unter AVR und nun auch ESP32 
einsetze.

Nur einige #define oder cast mussten geändert werden aus UBYTE wurde 
uint8_t aus ULONG wurde uint16_t

> Ich habe das Gefühl, dass du keine Ahnung hast

da könnte ich nun schmollen denn KEINE Ahnung ist ein starkes Stück.

Alle sind angeblich immer besser, nur hat jeder aber wirklich jeder 
Programmierer seine Eigenheiten und Stärken, hier sind viele 
Klugschnacker unterwegs, wirklich guten Code gibt es selten, da z.B. in 
meinen Augen PeDa Dannegger und Fleury.

Sonst habe ich selten so kompatiblen Code gesehen wie bei den beiden

: Bearbeitet durch User
von Dr. Sommer (Gast)


Angehängte Dateien:

Lesenswert?

S. R. schrieb:
> Uuund: Wieder hast du eine Abhängigkeit vom Zeichensatz des Quelltextes
> zum Zeichensatz des Displays. Wieder nix gelernt, den gleichen Fehler
> wieder gemacht. Gratuliere.

Im Anhang aus Spaß mal eine für Arduino erweiterte Variante meines o.g. 
Code zum statischen Codieren von Strings. Wenn man diesen Header in 
einem Arduino-Sketch inkludiert, kann man so etwas schreiben:
1
Serial.print(PGM_STR_ENCODED(asLatin1, U"Grad ° UE Ü"));

Dieser String wird vom Compiler nach Latin-1 umkodiert und dann als 
Latin-1 fix in den Flash gelegt, völlig unabhängig davon, welche 
Kodierung die Quelltext-Datei hat. Das serielle Terminal muss dann 
natürlich auf Latin-1 gestellt werden. Der String wird nie komplett in 
den RAM kodiert, und dann direkt 1:1 ausgegeben, ähnlich als hätte man 
geschrieben
1
Serial.print(F("blabla"));

Wobei die Kodierung dann aber von Editor & Compiler abhängig wäre.

Einzelne Zeichen gehen einfacher, weil man kein Array & Flash-Ablage 
braucht:
1
Serial.print(asLatin1(U'°'));

Es geht aber auch (äquivalent):
1
Serial.print(PGM_STR_ENCODED(asLatin1, U'°'));

von S. R. (svenska)


Lesenswert?

Joachim B. schrieb:
>> Du ignorierst also gute Ratschläge, weil sie deiner Meinung nach nicht
>> von einem "echten Experten" stammten?
>
> und wie unterscheidet man, man prüft die Aussagen auf Funktion :)

Dazu muss man aber wissen, wie man das korrekt macht...

Wenn mir mein Autoschrauber erzählt, was an meinem Auto gemacht werden 
muss, dann prüfe ich nicht jede Aussage händisch bis ins Detail nach.

Joachim B. schrieb:
> witzigerweise funktioniert mein purer C-Code seit 1990, den ich vom PC
> unter DOS über Atari 68k bis heute unter AVR und nun auch ESP32
> einsetze.

Das finde ich jetzt erstaunlich, denn das bedeutet, dass du es in 30 
Jahren nicht geschafft hast, mehr als die absoluten Grundlagen zu 
lernen.

Joachim B. schrieb:
>> Ich habe das Gefühl, dass du keine Ahnung hast
>
> da könnte ich nun schmollen denn KEINE Ahnung ist ein starkes Stück.

Zumindest hast du in diesem Thread gezeigt, dass dir massiv 
theoretisches Wissen fehlt und du eher wenig Interesse daran hast, es 
dir anzueignen.

von Joachim B. (jar)


Lesenswert?

S. R. schrieb:
> Dazu muss man aber wissen, wie man das korrekt macht...

das ist doch nur so ein Klugschnack, ich habe den Code gezeigt der genau 
Dr. Sommer seiner Aussage widersprach.

und was kommt von dir?

S. R. schrieb:
> Wenn mir mein Autoschrauber erzählt, was an meinem Auto gemacht werden
> muss, dann prüfe ich nicht jede Aussage händisch bis ins Detail nach.

eben und ich auch nicht

ich war recht zufrieden mit der newDS1307 LIB

suche seit Tagen den Absturzgrund am ESP32 und habe nun die LIB als 
Schuldige entdeckt.

Die adafruit_GFX LIB funktioniert prima am ESP32 und am AVR ABER sie 
verballert 600 Byte SRAM mehr (1/3 vom SRAM als ein 328p hat) als die 
LCD Basic, die mir aber mit #include avr sofort zeigte das sie am ESP32 
nicht mag.

Zurück zur newDS1307 LIB die hat prima auch mit der DS3231 funktioniert, 
nur für die Temperatur musste ich eigenen Code schreiben, nutzte das die 
Fleury AVR Lib I2C, auch am Arduino.

so nun muss ich mal schauen wo es an der newDS1307 LIB klemmt.

Alle LIB Schreiber haben erst mal meine Hochachtung aber es nervt wenn 
JEDER grundlegende Dinge anders macht.

Der Eine will alle µC gleich bedienen ohne Rücksich auf den SRAM 
Verbrauch, nur was nutzt ein LIB die sich mal ebn 1/3 vom SRAM mehr 
genehmigt als eine schlanke Variante?
Es ist doch doof wenn nur noch die Beispiele funktionieren.

Auch Profiprogger, ja LADYada machen Fehler, da gibt es Beispiele wo 
setup am Anfang steht, aber Funktionen aufgerufen werden die am Anfang 
noch nicht bekannt sind.

schauen wir uns doch mal 2 Inits an

#include <LCD5110_Basic.h>
LCD5110 myGLCD(8,9,10,11,12);
extern uint8_t SmallFont[];
void setup()
{
  myGLCD.InitLCD();
  myGLCD.setFont(SmallFont);
  myGLCD.clrScr();
  myGLCD.print("ABCDEFGHIJKLM", CENTER, 16);
}


alles klar mit myGLCD.print(char* text, spalte, zeile) gehts los
aber kein .begin(); dafür .InitLCD();

und dann die Ada
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 5, 4, 3);
void setup()
{
display.begin();
display.setContrast(50);
display.clearDisplay();   // clears the screen and buffer
display.setTextSize(1);
display.setTextColor(BLACK);
display.setCursor(0,0);
display.println("Hello, world!");
display.display(); // show
}

klar beides ist möglich, aber woher soll ICH wissen welcher 
Programmierstil der Bessere oder Richtige ist?

S. R. schrieb:
> Das finde ich jetzt erstaunlich, denn das bedeutet, dass du es in 30
> Jahren nicht geschafft hast, mehr als die absoluten Grundlagen zu
> lernen.

wo gibt es die?

Ist das nicht von JEDEM Tutor oder Lehrer abhängig

wie man sieht, es gibt keine klare Linie!
erst Recht kein Allheilmittel, nur viel Kritik wenn man es anders macht.

Egal wie "MIES" ich programmiere, als Programmierer wurde ich nie 
eingestellt, ich soll Aufgaben bewältigen, dazu gehörte nie PERFEKTER 
Code ohne zu wissen was das ist.

Man plagt sich eh schon genug mit Nebenbaustelllen rum, ich will einfach 
nur LIBs benutzen ohne mir goße Gedanken zu machen läuft das auf dem 
Zielsystem?

Nur echte Spezialisten wissen immer mehr von immer weniger, bis sie 
alles von NICHTS wissen.

von Dr. Sommer (Gast)


Lesenswert?

Joachim B. schrieb:
> klar beides ist möglich, aber woher soll ICH wissen welcher
> Programmierstil der Bessere oder Richtige ist?

Ob die Funktion jetzt begin oder init heißt ist noch kein 
Programmierstil. Das ist ziemlich egal. Eigentlich ist das ziemlich 
Arduino spezifisch, und da ist eh alles ziemlich durcheinander. 
Normalerweise kommt sowas sowieso in den Konstruktor.

Joachim B. schrieb:
> Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 5, 4, 3);

Warum nicht einfach:
1
Adafruit_PCD8544 display (7, 6, 5, 4, 3);

Joachim B. schrieb:
> wo gibt es die?

In Büchern.

Joachim B. schrieb:
> Ist das nicht von JEDEM Tutor oder Lehrer abhängig

Das ist kein Argument es nicht zu lernen...

Joachim B. schrieb:
> ich will einfach nur LIBs benutzen ohne mir goße Gedanken zu machen
> läuft das auf dem Zielsystem?

Mit der Logik lernst du das nie und du plagst dich auf alle Ewigkeit mit 
C Grundlagen herum.

Joachim B. schrieb:
> Nur echte Spezialisten wissen immer mehr von immer weniger, bis sie
> alles von NICHTS wissen.

So ein Blödsinn. Man braucht Spezialisten für spezielle Probleme. Und 
viele Spezialisten haben auch eine Menge Ahnung von benachbarten 
Gebieten... für vernünftige Beherrschung von C muss man kein Spezialist 
sein.

Joachim B. schrieb:
> ich habe den Code gezeigt der genau Dr. Sommer seiner Aussage
> widersprach.

Nur dass er das eben nicht tut, du hast nur die Warnungen ignoriert...

von S. R. (svenska)


Lesenswert?

Joachim B. schrieb:
> Alle LIB Schreiber haben erst mal meine Hochachtung aber
> es nervt wenn JEDER grundlegende Dinge anders macht.

Was vielleicht daran liegt, dass Arduino keine Vorgaben macht und die 
Bibliotheken nicht nur von kompetentem Personal geschrieben werden.

Das machen auch Holzhammerfrickler.

> Der Eine will alle µC gleich bedienen ohne Rücksich auf den SRAM
> Verbrauch, nur was nutzt ein LIB die sich mal ebn 1/3 vom SRAM mehr
> genehmigt als eine schlanke Variante?

Du meinst also, man sollte keine Bibliotheken anbieten, die für AVR und 
ESP32 gleich funktionieren? Oder irre ich da?

> klar beides ist möglich, aber woher soll ICH wissen welcher
> Programmierstil der Bessere oder Richtige ist?

Das ist nicht deine Entscheidung.

Du hast dich entschieden, irgendwelche Bibliotheken zu benutzen, also 
musst du mit den Entscheidungen derjenigen leben, die die mal 
geschrieben haben.

Es steht dir frei, bessere Bibliotheken zu schreiben, aber: Dazu 
müsstest du die Grundlagen kennen.

>> Das finde ich jetzt erstaunlich, denn das bedeutet, dass du es in 30
>> Jahren nicht geschafft hast, mehr als die absoluten Grundlagen zu
>> lernen.
>
> wo gibt es die?

In Büchern und in der Dokumentation.

> Ist das nicht von JEDEM Tutor oder Lehrer abhängig

Es ist bezeichnend, dass du Fachwissen als "vom Lehrer oder Tutorial" 
abhängig machst.

Im Übrigen gibt es einen Unterschied zwischen "Grundlagen der Sprache" 
und "effiziente Anwendung der Sprache". Ersteres ist notwendig, aber 
nicht hinreichend - wer an den Grundlagen scheitert, wird es nie 
ordentlich hinkriegen.

> wie man sieht, es gibt keine klare Linie!

Nein, aber du schließt von "niemand ist perfekt" auf "alle sind 
unfähig". Auch, wenn sich nicht alle daran halten - auch nicht alle 
Experten - gibt es einen Grundkonsenz an Dingen, die man machen oder 
lassen sollte.

> Man plagt sich eh schon genug mit Nebenbaustelllen rum,
> ich will einfach nur LIBs benutzen ohne mir goße Gedanken
> zu machen läuft das auf dem Zielsystem?

Ja, das merkt man. Bloß nichts verstehen müssen. Bloß nichts lernen.

von Joachim B. (jar)


Lesenswert?

Dr. Sommer schrieb:
> Joachim B. schrieb:
>> ich habe den Code gezeigt der genau Dr. Sommer seiner Aussage
>> widersprach.
>
> Nur dass er das eben nicht tut, du hast nur die Warnungen ignoriert.

ach egal, es gibt eine Lösung, einige gehen anders an die Dinge als ich, 
ist auch OK.

S. R. schrieb:
> Ja, das merkt man. Bloß nichts verstehen müssen. Bloß nichts lernen.

nicht OK scheint mir andere niederzumachen um sich selbst zu erhöhen, 
ist das nicht ärmlich?

Ich lese das hier öfter und gestehe das es mir auch mal passiert, aber 
deswegen ist es nicht gut.

Ich bin schon wieder weiter und finde meine Lösung ohne in den 
Compileroptionen rumzuändern.

Manche Voreinstellung ist ja nicht immer sinnlos, ein absolut perfekt 
eingestellter Compiler kann nur noch nerven ohne was am Ergebnis 
zwingend zu verbessern, er erreicht nur eines das man nie fertig wird 
oder die Lust verliert.

Kleine Bugs oder Unzulänglichkeiten findet man überall es regt mich auf 
wenn ich dafür bezahlen musste und sogar noch den Schaden habe, aber 
wenn meine kleine DHT22 Anzeige abschmiert merke ich das suche den 
Fehler und behebe ihn wenns nervt, ist kein Beinbruch.

Wenn mich aber Pakete nicht erreichen weil das Internet Webformular 
Adressfenster 4 Zeilen a 40 Zeichen hat, aber auf dem Paketaufkleber nur 
3 Zeilen a 20 Zeichen gedruckt werden, DAS regt mich auf.

Das waren also Profiprogrammierer die damit Geld verdienen?

von S. R. (svenska)


Lesenswert?

Joachim B. schrieb:
>> Ja, das merkt man. Bloß nichts verstehen müssen. Bloß nichts lernen.
>
> nicht OK scheint mir andere niederzumachen um sich selbst
> zu erhöhen, ist das nicht ärmlich?

Du hast alle Antworten, die nicht direkt auf dein Problem passten und 
sofortige Lösung versprachen, übergangen.

Dass du nicht willens bist, dich tiefer mit der Materie zu befassen, 
hast du selbst gesagt.

von Joachim B. (jar)


Lesenswert?

S. R. schrieb:
> Du hast alle Antworten, die nicht direkt auf dein Problem passten und
> sofortige Lösung versprachen, übergangen.

ach ist das so?

du solltest Lotto spielen als Hellseher!

Das rechtfertigt in deinen Augen andere niederzumachen, man bist du 
ärmlich!

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.