Hallo
Nachdem ich bisher nur Assembler programmiert habe, mache ich grade
meine ersten Versuche mit C.
Mein Experimentier-System besteht aus einem AT90S8515 mit
akkugepuffertem externem 32k-SRam.
Das SRam liegt im Speicherbereich $8000-$FFFF und lässt sich über einen
Zeiger ansprechen.
Leider ist es so, das einige Speicherinhalte beim Ein- bzw. Ausschalten
verändert werden. Um der sache auf den Grund zu gehen wollte ich mir
zuerst mal eine Funktion erstellen, die den Inhalt des SRams überprüft
und mir die fehlerhaften Adressen und deren Inhalte via RS232 zum PC
sendet.
Mein Problem ist nun folgendes.
Ich möchte den Inhalt des Zeigers, also die Adresse auf die er grade
zeigt via RS232 zum PC übertragen.
Dazu muss ich diesen 2 Byte Zeiger aber auf 2 einzelne Bytes aufteilen
um sie nacheinander übertragen zu können.
Und genau daran bin ich bisher gescheitert. Ich bekomme entweder
Warnungen wegen fehlendem Typecasting oder unterschiedlicher
Variablenlängen. Mittlerweile habe ich da schon Stunden dran rumgetippt.
Ohne Erfolg :(
Wie kann ich den Inhalt dieses 2 Byte Zeigers auf 2 einzelne uint8_t
Variablen aufteilen, so das ich Hi und Lo Byte der Adresse einzeln habe.
uint16_t* p16;
uint8_t* p8;
uint8_t high, low;
p16 = deine_speicheradresse;
p8 = p16; // Bytezeiger zeigt auf gleiche Adresse wie Wordzeiger
memcpy(p8, &high, 1); // ein Byte von der Adresse auf die p8 zeigt in
die Variable high kopieren
p8++; // Zeiger ein Byte weiter
memcpy(p8, &low, 1);
Thorsten schrieb:> p16 = deine_speicheradresse;> p8 = p16; // Bytezeiger zeigt auf gleiche Adresse wie Wordzeiger>> memcpy(p8, &high, 1); // ein Byte von der Adresse auf die p8 zeigt in> die Variable high kopieren> p8++; // Zeiger ein Byte weiter> memcpy(p8, &low, 1);
naja nicht wirklich schön, dann ist es abhängig von der Platform in
welcher Reihenfolge die Bytes kommen.
p16 = deine_speicheradresse;
uint8_t low = p16 & 0xff;
uint8_t high = p16>>8 & 0xff;
Hans Imglück schrieb:> Wie kann ich den Inhalt dieses 2 Byte Zeigers auf 2 einzelne uint8_t> Variablen aufteilen, so das ich Hi und Lo Byte der Adresse einzeln habe.
Oliver S. schrieb:> Hans Imglück schrieb:>> Wie kann ich den Inhalt dieses 2 Byte Zeigers auf 2 einzelne uint8_t>> Variablen aufteilen, so das ich Hi und Lo Byte der Adresse einzeln habe.>>
1
>intp=(uint16_t)pointer;
2
>volatileuint8_tlowbyte=(uint8_t)p;
3
>volatileuint8_thighbyte=(uint8_t)p<<8;
4
>
>> Oliver
Wollen wir die Richtung der '<<' nochmal überdenken? ;-)
norbert@Entwicklung:/tmp$ gcc -Wall -Wextra -Wconversion -o x x.c
x.c: In function ‘main’:
x.c:16:15: warning: conversion to ‘uint8_t’ from ‘long unsigned int’ may
alter its value [-Wconversion]
x.c:18:28: warning: conversion to ‘uint8_t’ from ‘long unsigned int’ may
alter its value [-Wconversion]
‘long unsigned int’ nur weil ich's gerade auf 'nem 64bit System
getrieben habe.
Sonst wär's wahrscheinlich ‘unsigned int’.
Bezgl. -Wconversion: Hat der gcc früher (seinerzeit, zu meiner Zeit also
einer schlimmen Zeit) per default bei -Wall gemacht, heute muß man es
explizit angeben. Würde mich interessieren welcher Weltmeister auf die
Idee gekommen ist, die Warnungen in neueren Versionen schlapper zu
machen.
/* Kreuzzug für Warnungsfreie Compilerläufe ;-) */
Ach so, so geht's ohne Schlag auf den Hinterkopf vom compiler:
lowbyte = (uint8_t)(uint64_t)p;
highbyte = (uint8_t)((uint64_t)p >> 8);
bzw. auf einem 16bit System (ungetestet):
lowbyte = (uint8_t)(uint16_t)p;
highbyte = (uint8_t)((uint16_t)p >> 8);
Die Warnung dürfte auch weggehen, wenn man noch mit 0xFF verodert, oder?
Das fände ich dann die schönere Variante, weils dann auch funktioniert,
wenn der Datentyp auf der linken Seite mehr als 8 Bit hat.
Hans schrieb:> Die Warnung dürfte auch weggehen, wenn man noch mit 0xFF verodert, oder?
Nö.
Nur weil du mit irgendwas veroderst wird aus einem 16 Bit Wert nicht
irgendwie magisch ein 8 Bit Wert.
Hans schrieb:> Die Warnung dürfte auch weggehen, wenn man noch mit 0xFF verodert, oder?> Das fände ich dann die schönere Variante, weils dann auch funktioniert,> wenn der Datentyp auf der linken Seite mehr als 8 Bit hat.>>
1
void*p;
2
>uint8_tlowbyte=(uint16_t)p&&0xFF;
3
>uint8_thighbyte=((uint16_t)p>>8)&&0xFF;
Nö, das klappt nicht. Nach p and 0xff bleibt der Ausdruck uint16_t.
Sauber ist es erst, wenn ein cast aus dem 16bit breiten Zeiger einen
16bit breiten uint16_t macht. Dann fummelt man sich das passende Byte
heraus (das ist jetzt immer noch 16 bit breit) und castet das dann auf
8bit.
Karl Heinz schrieb:> Nur weil du mit irgendwas veroderst wird aus einem 16 Bit Wert nicht> irgendwie magisch ein 8 Bit Wert.
Das nicht, aber der Wert geht dann sicher in 8 Bit, daher kein "may
alter its value" mehr.
Gerade probiert:
Hans schrieb:> Karl Heinz schrieb:>> Nur weil du mit irgendwas veroderst wird aus einem 16 Bit Wert nicht>> irgendwie magisch ein 8 Bit Wert.>> Das nicht, aber der Wert geht dann sicher in 8 Bit, daher kein "may> alter its value" mehr.>> Gerade probiert:
Interessant.
Das hätte ich jetzt nicht gedacht, dass der gcc das bei der Generierung
der Warnung ins Kalkül zieht.
Wieder was gelernt. Danke.
x.c:18:44: warning: conversion to ‘uint8_t’ from ‘long unsigned int’ may
alter its value [-Wconversion]
In Zeile 17 stellt der Compiler mit absoluter Sicherheit fest, das der
Ausdruck auf der rechten Seite nur 8bit breit sein kann. KEINE Warnung.
In Zeile 18 wird der gleiche Ausdruck (8bit) mit einem anderen 8bit Wert
ODER verknüpft. Jetzt gibt's wieder die Warnung.
Versteh's wer will...
Norbert schrieb:> In Zeile 17 stellt der Compiler mit absoluter Sicherheit fest, das der> Ausdruck auf der rechten Seite nur 8bit breit sein kann. KEINE Warnung.>> In Zeile 18 wird der gleiche Ausdruck (8bit) mit einem anderen 8bit Wert> ODER verknüpft. Jetzt gibt's wieder die Warnung.>> Versteh's wer will...
Finde ich jetzt nicht so merkwürdig. Er prüft halt innerhalb eines
Ausdrucks, ob der Wertebereich überschritten wird, aber wenn Variablen
hinzugezogen werden, geht er halt nicht deren ganze Entstehungsgeschiche
zurück, um festzustellen, welchen Wertebereich die überhaupt haben
können.
An der Stelle ist lowbyte halt einfach ein uint64_t und damit potenziell
zu groß.
Rolf Magnus schrieb:> Finde ich jetzt nicht so merkwürdig. Er prüft halt innerhalb eines> Ausdrucks, ob der Wertebereich überschritten wird, aber wenn Variablen> hinzugezogen werden, geht er halt nicht deren ganze Entstehungsgeschiche> zurück, um festzustellen, welchen Wertebereich die überhaupt haben> können.> An der Stelle ist lowbyte halt einfach ein uint64_t und damit potenziell> zu groß.
Ja, ok, ist jetzt kein Showstopper. Wenn man es wie gehabt ordentlich
castet ist es ja auch einwandfrei. Ich wunderte mich auch nur über die
etwas eigentümliche Generierung von Warnungen.
Was meiner Meinung nach viel schlimmer ist:
1
intmain(void){
2
uint16_twert1;
3
uint8_twert2;
4
5
wert1=12345;
6
wert2=wert1;
7
8
returnwert1+wert2;
9
}
So etwas generiert beim gcc heutzutage trotz "-Wall -Wextra" keinerleiWarnungen, das ist doch nicht gesund.
Ich weiss ja nicht wann man das beim gcc rausgenommen hat, aber vor
einiger Zeit gab's für solch einen Sourcecode noch die Kelle vom
Compiler.
Johann L. schrieb:> Wenn einen die Warnungen stören kann man Casts dazu tun:
Da waren wir aber schon um 18:49h ;-)
Die Frage ist eher, warum generiert der Compiler keine Warnungen bei
wirklich üblen Dingen? (siehe 21:48h)
Früher tat er das jedoch. (Wenn ich mich recht erinnere)
Norbert schrieb:> Die Frage ist eher, warum generiert der Compiler keine Warnungen bei> wirklich üblen Dingen? (siehe 21:48h)
Wahrscheinlich weil es in normalem Code ständig vorkommt. Genau wie
Zuweisungen und Vergleiche zwischen signed und unsigned. Da jedes Mal
explizit casten zu müssen ist auch nicht optimal.
Imo sollten Casts nur bei "ungewöhnlichen" Typumwandlungen wie hier
zwischen Zeiger und Integer oder Zeigern verschiedenen Typs nötig sein
und nicht bei impliziten Umwandlungen wie Integern verschiedener Breite.
Denn wenn man sich deshalb notgedrungen angewöhnt, bei jeder Zuweisung
zu casten, dann verliert irgendwann das Typsystem seine Wirkung, da man
die wirklichen Fehler nicht mehr sieht.
Klar, mit viel Disziplin kann man den Großteil vermeiden. Wenn man viel
mit fremden Code zu tun hat, ists aber leider nicht immer so einfach
machbar.
Hallo
Das ging ja flott.
Ich habs mal mit der Lösung von Peter II probiert. Hat auch fast auf
Anhieb gefunzt.
Aber ohne Typcasting gabs trotzdem noch ne Warnung.
Mit den Zeilen unten gehts ohne Warnungen, und funtionieren tuts auch :)
Danke an alle.
//--------------------------------------------------------------------
#define EXRAM_START 0x8000 // Externes Ram. Anfangsadresse
// Globale Variable
uint8_t *ramadr = NULL; // Zeiger auf EX-Ram Adresse
uint8_t high, low;
ramadr = (uint8_t*) EXRAM_START;
low = (uint16_t)ramadr & 0xff;
high = (uint16_t)ramadr>>8 & 0xff;
Hans schrieb:> Wahrscheinlich weil es in normalem Code ständig vorkommt. Genau wie> Zuweisungen und Vergleiche zwischen signed und unsigned. Da jedes Mal> explizit casten zu müssen ist auch nicht optimal.>> Imo sollten Casts nur bei "ungewöhnlichen" Typumwandlungen wie hier> zwischen Zeiger und Integer oder Zeigern verschiedenen Typs nötig sein> und nicht bei impliziten Umwandlungen wie Integern verschiedener Breite.> Denn wenn man sich deshalb notgedrungen angewöhnt, bei jeder Zuweisung> zu casten, dann verliert irgendwann das Typsystem seine Wirkung, da man> die wirklichen Fehler nicht mehr sieht.>> Klar, mit viel Disziplin kann man den Großteil vermeiden. Wenn man viel> mit fremden Code zu tun hat, ists aber leider nicht immer so einfach> machbar.
OK, ich präzisiere noch einmal:
Wenn ich dem gcc mit den Optionen -Wall -Wextra meinem Wunsch nach
Warnungen bei schlampiger Programmierung Ausdruck verleihe, dann will
ich bei Zuweisungen wie zB. int16_t -> uint16_t und ähnlichen
fragwürdigen Konstrukten gewarnt werden. Sollte das hin- und herhüpfen
zwischen verschiedenen Typen tatsächlich gewollt sein, dann caste ich.
Wenn ich jedoch diese Art der Programmierung ohne explizite casts
irrtümlicherweise für sauber und akzeptabel halte, so kann ich jederzeit
die entsprechenden Warnungen abschalten.
Aber -Wall -Wextra ist meines Erachtens nach ein ziemlich
unmissverständlicher Wunsch nach Informationen.
Wie gesagt, früher wurde man vom gcc bei so etwas gewarnt.
Norbert schrieb:> Johann L. schrieb:>> Wenn einen die Warnungen stören kann man Casts dazu tun:>> Da waren wir aber schon um 18:49h ;-)
Ja, allerdings war Deine Version wesentlich "unportabler". Du hattest
noch den Code unterschieden, ob er für ein 64- oder 16-Bit-System zu
compilieren ist. Als ich das eben las, schoss mir direkt in den Kopf,
dass Dein uint64_t- bzw. uint16_t-Cast ja genau die "natürliche Breite"
des Zielsystems trifft.
Genau dort wäre ein uint-Pointer-Cast ohne Angabe der Bitbreite das
einzig sinnvolle gewesen... was dann Johann auch gemacht hat. ;-)
Frank M. schrieb:> Ja, allerdings war Deine Version wesentlich "unportabler".
Allerdings ging es bei der Fragestellung nicht im Geringsten um
Portabilität, sondern um Warnungen wenn keine casts eingesetzt werden.
...und darauf bezogen sich alle von den verschiedenen Postern bisher
geschriebenen Beispiele.
Der Portabilitätsaspekt ist erst viel später eingeführt worden um das
Thema zu verwässern und vom eigentlichen Problem abzulenken. ;-)
Um es vielleicht noch einmal von der 'anderen' Seite zu beleuchten:
Die Einführung von (uintptr_t) um verschiedene Pointerbreiten zu
unterstützen hilft bei der Aufgabenstellung des Themenerstellers nicht
im Geringsten.
Dann müsste nämlich auch noch eine Überprüfung der Pointerbreite
stattfinden, die Aufteilung nicht nur in Lo/Hi Byte (16bit) sondern
vielleicht in Lo/Hi/VeryHi/ExtremelyHi (32bit) sowie deren Äquivalente
für alles >32bit Breite - das spar' ich mir hier mal - stattfinden.
Darum ging es hier aber nicht!
Um Warnungen ging's ja auch nicht.
-Wall -Wextra aktiviert nicht alle Warnungen, und manche Warnungen
können vom Optimierungsgrad abhängen (selten) weil ohne diese die
entsprechenden Analysen nicht durchgeführt werden weil die teuer sind.
Ein Beispiel ist VRP.
Eine Übersicht über GCC-Warnungen ewrhälst du mit
gcc --help=warnings
Eine Auflistung, was in Sammel-Optionen wie -Wall enthalten ist, kann
dem Handbuch entnommen werden:
GCC-Manual → GCC Command Options → Options to Request or Suppress
Warnings