Forum: Compiler & IDEs Zeiger in Hi u. Lo Byte aufteilen


von Hans I. (obiwahn)


Lesenswert?

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.

von Thorsten (Gast)


Lesenswert?

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);

von Peter II (Gast)


Lesenswert?

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;

von Amateur (Gast)


Lesenswert?

Der Atmel-Assembler hat dafür ein paar vordefinierte Schlüsselwörter.

von Amateur (Gast)


Lesenswert?


von Peter II (Gast)


Lesenswert?

Amateur schrieb:
> Der Atmel-Assembler hat dafür ein paar vordefinierte Schlüsselwörter.

er will aber weg von ASM hin zu C.

von Oliver S. (oliverso)


Lesenswert?

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
int p = (uint16_t)pointer;
2
volatile uint8_t lowbyte = (uint8_t) p;
3
volatile uint8_t highbyte = (uint8_t) p << 8;

Oliver

von Norbert (Gast)


Lesenswert?

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
> int p = (uint16_t)pointer;
2
> volatile uint8_t lowbyte = (uint8_t) p;
3
> volatile uint8_t highbyte = (uint8_t) p << 8;
4
>
>
> Oliver

Wollen wir die Richtung der '<<' nochmal überdenken? ;-)

von Oliver S. (oliverso)


Lesenswert?

;)

Oliver

von Hans (Gast)


Lesenswert?

Das volatile ist auch unnötig.
1
void* p;
2
uint8_t lowbyte  = (uint16_t) p;
3
uint8_t highbyte = (uint16_t) p >> 8;

von Norbert (Gast)


Lesenswert?

Hans schrieb:
> Das volatile ist auch unnötig.
>
>
1
void* p;
2
> uint8_t lowbyte  = (uint16_t) p;
3
> uint8_t highbyte = (uint16_t) p >> 8;

Wollen wir die casts noch einmal überdenken? ;-)

von Karl H. (kbuchegg)


Lesenswert?

Norbert schrieb:
> Hans schrieb:
>> Das volatile ist auch unnötig.
>>
>>
1
void* p;
2
>> uint8_t lowbyte  = (uint16_t) p;
3
>> uint8_t highbyte = (uint16_t) p >> 8;
>
> Wollen wir die casts noch einmal überdenken? ;-)

Done.
Sind in Ordnung, solange sizeof(void*) gleich 2 ist.

: Bearbeitet durch User
von Norbert (Gast)


Lesenswert?

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 ;-) */

von Norbert (Gast)


Lesenswert?

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);

von Hans (Gast)


Lesenswert?

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_t lowbyte  =  (uint16_t) p       && 0xFF;
3
uint8_t highbyte = ((uint16_t) p >> 8) && 0xFF;

von Karl H. (kbuchegg)


Lesenswert?

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.

von Norbert (Gast)


Lesenswert?

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_t lowbyte  =  (uint16_t) p       && 0xFF;
3
> uint8_t highbyte = ((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.

von Hans (Gast)


Lesenswert?

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:
1
#include <stdio.h>
2
#include <stdint.h>
3
4
int main(void)
5
{
6
  int i;
7
  int* p = &i;
8
9
  uint8_t lowbyte  =  (uint64_t) p       & 0xFF;
10
  uint8_t highbyte = ((uint64_t) p >> 8) & 0xFF;
11
12
  printf("p = %p, lowbyte = %#x, highbyte = %#x\n", p, lowbyte, highbyte);
13
14
  return 0;
15
}

Compiliert ohne Warnungen:
1
hans@hans:~/c$ gcc -Wall -Wextra -Wconversion -o x x.c
2
hans@hans:~/c$ ./x
3
p = 0x7fffbbc278d0, lowbyte = 0xd0, highbyte = 0x78
4
hans@hans:~/c$

P.S.: Es muss natürliche eine bitweise Verundung sein ...

von Karl H. (kbuchegg)


Lesenswert?

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.

von Norbert (Gast)


Lesenswert?

Fand ich auch interessant, jetzt geht's aber noch weiter:
1
    uint8_t lowbyte;
2
    uint8_t highbyte;
3
4
    lowbyte = (uint64_t)p & 0xff;                       /* ze16 */
5
    highbyte = (((uint64_t)p >> 8) & 0xff);             /* ze17 */
6
    highbyte = (((uint64_t)p >> 8) & 0xff) | lowbyte;   /* ze18 */

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

von Rolf Magnus (Gast)


Lesenswert?

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

von Norbert (Gast)


Lesenswert?

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
int main(void) {
2
    uint16_t wert1;
3
    uint8_t wert2;
4
5
    wert1 = 12345;
6
    wert2 = wert1;
7
8
    return wert1+wert2;
9
}

So etwas generiert beim gcc heutzutage trotz "-Wall -Wextra" keinerlei 
Warnungen, 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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wenn einen die Warnungen stören kann man Casts dazu tun:
1
#include <stdint.h>
2
3
uint8_t lo, hi;
4
5
void split (void *p)
6
{
7
    lo = (uint8_t) (uintptr_t) p;
8
    hi = (uint8_t) ((uintptr_t) p >> 8);
9
}

von Norbert (Gast)


Lesenswert?

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)

von Hans (Gast)


Lesenswert?

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.

von Hans I. (obiwahn)


Lesenswert?

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;

von Norbert (Gast)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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

von Norbert (Gast)


Lesenswert?

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

von Norbert (Gast)


Lesenswert?

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!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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

: Bearbeitet durch User
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.