C -> Makro für Pointer Adresse

OP #6020674
Lesenswert?

Stefanus F. schrieb:
> Dafür braucht man doch kein Makro!
>
> PORTD = value;

Ist mir soweit bewusst.
Folgendes.. Ich habe eine Struktur.. In der habe ich mehrere IO Adresse 
drin. Von unterschiedlichen Ports..

Ich möchte mit einem Makro auf diese Adressen schreiben.
z.B
1
uint8_t *ptr = (uint8_t*)0x32; // PORTD
2
*ptr |= (1<<0;

Das mit dem Zeiger deklarieren würde ich gerne schicker durch ein Makro 
verpacken..
Gast #6020682
Lesenswert?

z.B. wenn unsigned char ein geeigneter Typ für "Port-D" ist und die 
Adresse les und schreibbar ist:
1
#define PORTD (*((unsigned char*)0x32))
2
...
3
    PORTD=3;
4
    ...
5
    if(PORTD==3)

meist will man aber noch mehr mit einem Port machen, wie Bit-Zugriff 
etc.

Wenn nur lesend, dann z.B.
1
#define PORTD (*((const unsigned char*)0x32))

da Ports meist wirkliche IO beeinhalten, die sich ändert, d.h. jeder 
schreib-LeseZugriff durchgeführt werden sollte, mit volatile:
1
#define PORTD (*((volatile const unsigned char*)0x32))
2
#define PORTD (*((volatile unsigned char*)0x32))
Gast #6020690
Lesenswert?

wenn nur schreibend, dann verhindern, dass der Ausdruck so wie er da 
steht irgendwo zugewiesen werden kann, im einfachsten Fall einen dummy 
Block davor.
1
#define PORTD {}(*((volatile unsigned char*)0x32))
Damit fallen aber all jene auf die Schnautze, die einzelne Statements 
nach if etc. nicht klammern. Darum selber noch if dabei, dann ist das 
egal
1
#define PORTD if(0){}else(*((volatile unsigned char*)0x32))
Das liest zwar immer noch, wenn man einfach nur PORTD; schreibt, aber 
eine Zuweisung "x=PORTD" ist nicht möglich.
Gast #6020829
Lesenswert?

Naj H. schrieb:
> Ich wollt es eigentlich so haben..
> Makro(Adresse) = Wert;
1
#define Makro(Adresse) (*((volatile unsigned char*) (Adresse)))
2
#define PORT(Adresse)  (*((volatile unsigned char*) (Adresse)))
3

4
   Makro(0x32)=7;
5
   PORT( 0x32)=7;
Das wäre aber relativ obskur. Was machst Du damit?
Beitrag #6021005 wurde von einem Moderator gelöscht.
OP #6021346
Lesenswert?

A. S. schrieb:
> Naj H. schrieb:
>> Ich wollt es eigentlich so haben..
>> Makro(Adresse) = Wert;
>
>
1
> #define Makro(Adresse) (*((volatile unsigned char*) (Adresse)))
2
> #define PORT(Adresse)  (*((volatile unsigned char*) (Adresse)))
3
> 
4
>    Makro(0x32)=7;
5
>    PORT( 0x32)=7;
6
>
> Das wäre aber relativ obskur. Was machst Du damit?

Genau das habe ich gesucht.
Habe dazu noch eine Frage..
Wenn ich jetzt
1
#define IOTAB_PORT( ADDRx )  (*((volatile uint8_t*) (ADDRx)))
Verwende merkt der Compiler folgendes an:
1
cast to pointer from integer of different size

Wieso? Wenn ich es wie folgt verwende, kommt keine Warnung..
1
#define IOTAB_PORT( ADDRx )  (*((volatile uint8_t*) (ADDRx+0)))
Gast #6021515
Lesenswert?

Naj H. schrieb:
> Warum klappt es dann wenn ich die Adresse + 0 rechne?

Weil

brainfuck_hater schrieb:
> Könnte es sein, dass sich hinter PORTD ein Makro verbirgt?

Recht hat.du musst einen echten Aufruf Posten, und dabei das Argument 
von IOTAB_PORT mit angeben. Also wenn es keine Zahl ist, dann wie es 
definiert ist.

Naj H. schrieb:
> Genau das habe ich gesucht.

Aber wohl kaum gewollt.
Gast #6021588
Lesenswert?

Naj H. schrieb:
> Wenn ich jetzt
>
> #define IOTAB_PORT( ADDRx )  (*((volatile uint8_t*)(ADDRx)))
>
> Verwende merkt der Compiler folgendes an:
> cast to pointer from integer of different size
>
> Wieso? Wenn ich es wie folgt verwende, kommt keine Warnung..
>
> #define IOTAB_PORT( ADDRx )  (*((volatile uint8_t*) (ADDRx+0)))

Compiler und Version? Und ist das wirklich der echte Code?


Eric B. schrieb:
> Weil 0x32 nur ein byte ist, und kein integer in der Große eines
> Pointers.

Nein, das ist nicht richtig.

0x32 ist ein Integer-Literal ohne Suffix. Daher bekommt es als Typ den 
ersten Typ aus der Liste [int, long int], in den es reinpasst, und das 
wäre hier 'int'.

Wie groß 'int' ist, hängt vom Compiler ab...


Eric B. schrieb:
> Also, die Adresse zuerst auf unsigned int casten:

Nein, das ist genau so falsch.

Es gibt kein Garantie, dass in ein 'unsigned int' ein Pointer reinpasst. 
Dafür gibt es die beiden Typen 'intptr_t' und 'uintptr_t'. In diese 
passt ein Pointer garantiert rein.

Hier sollte man also zweckmäßigerweise auf intptr_t casten, bevor man in 
einen Pointer castet. Also:
1
#define IOTAB_PORT(ADDRx)  (*((volatile uint8_t*) ((intptr_t)(ADDRx))))
Gast #6021616
Lesenswert?

Vlad T. schrieb:
>> Warum klappt es dann wenn ich die Adresse + 0 rechne?
>
> weil durch den Additionsoperator die operanden nach int promoted werden

Nein. Wir wissen nicht, was die "Adresse" ist. 0x32 ist nur vermutet. 
Vermutlich ist es ein Pointer auf einen Integer. Das +0 wirkt dann als 
dereferenzierung.

Spekulationen macht aber keinen Sinn, solange der Up den Code nicht 
postet.
#6021647
Lesenswert?

A. S. schrieb:
> Nein. Wir wissen nicht, was die "Adresse" ist. 0x32 ist nur vermutet.
> Vermutlich ist es ein Pointer auf einen Integer. Das +0 wirkt dann als
> dereferenzierung.

Nein. Das + bewirkt wie bereits erklärt eine Intergerpromotion, deshalb 
geht die Warnung weg.

Dereferenzierung ist was ganz anderes (andere Baustelle) und geschieht 
mit einem Sternchen (im vorliegenden Fall das Sternchen ganz links) oder 
man könnte es auch mit eckigen Klammern dereferenzieren:


#define IOTAB_PORT(ADDRx)  ((volatile uint8_t*)((intptr_t)(ADDRx)))[0]


Das ist ein Syntaxzucker der hier aber eher deplaziert wirken würde und 
besser an anderer Stelle glänzt, den benutzt man eher wenn man mit 
Arrays arbeitet und Pointeraddition und anschließendes Dereferenzieren 
(also der Zugriff auf das n-te Arrayelement) in einem Schritt kompakt 
hinschreiben will.
#6021931
Lesenswert?

A. S. schrieb:
> Wenn Du das Argument kennst. Wir kennen es nicht. Es gibt keinen Code
> dazu vom TO, nur "ja, genau so was" und die Erkenntnis, dass er etwas
> (für einen Anfänger) komisches vor hat.

Jetzt hör auf zu lamentieren.

Das ist aus der Fehlermeldung ganz klar zu erkennen.

Naj H. schrieb:
> cast to pointer from integer of different size

"from integer" heißt, er hat einen Integer-Literal oder eine Integer 
Variable an der entsprechenden Stelle.

"of different size" sagt, dass es != 16bit war.

Ist unerheblich ob er ein Literal, was der Compiler als 8bit-wert 
behandelt hat, oder eine Variable mit 8bit Typ, übergeben hat. Der 
Fehler ist derselbe und ziemlich eindeutig.

Ich glaube, es lehnt sich keiner zu weit aus dem Fenster, wenn man 
annimmt, dass er keine 32 oder 64bit Zahl/Variable übergeben hat.
Zumal es dann weitere Fehler gehagelt hätte.
Gast #6022435
Lesenswert?

Naj H. schrieb:
> Zombie schrieb:
>> Naj H. schrieb:
>>> Ich habe eine Struktur.. In der habe ich mehrere IO Adresse
>>> drin. Von unterschiedlichen Ports..
>>
>> Und warum speicherst du die Adresse da als int und nicht direkt als
>> volatile uint8_t*?
>
> Ich wollte wissen was jetzt im Endeffekt mehr Speicher benötigt.

Naja. Aber wenn Du n Adressen unterscheiden willst, dann muss die 
entsprechende Variable mindestens n verschiedene Werte annehmen können.
Das wiederrum heisst, dass die Variable mindestens log2(n) Bits haben 
muss.
Es spielt dabei überhaupt keine Rolle, ob das Bitmuster nun als Zeiger 
interpretiert wird oder als Integer. Der Informationsgehalt muss immer 
derselbe sein. Also auch die Anzahl der Bits.

Ein anderes Beispiel: Wenn Du k verschiedene Zeichen unterscheiden 
können willst (also etwa 'A'-'Z', 'a'-'z', '0'-'9' und die Satzzeichen, 
also etwa den ASCII-Zeichensatz), dann sind das 128 verschiedene 
Zeichen. Um die als Bitmuster darstellen zu können, brauchst Du 
log2(128) = 7 Bits.
Es ist völlig egal, ob Du diese Zeichen mit Ihrer Ordngungszahl (z.B. 
0x31 = '1') oder als Zeichenliteral '1' hinschreibst. Das sind nur 
verschiedene Interpretationen des Bitmusters.
Der Informationsgehalt, die Menge an Information, muss immer die selbe 
blieben.
Gast #6022474
Lesenswert?

Naj H. schrieb:
> Also macht es jetzt kein Unterschied ob ich das mit dem Marko mache oder
> direkt in der Struktur mit einem Zeiger?

Nun, es macht in jedem Fall keinen Unterschied in Bezug auf den dafür 
nötigen Speicher.

Überhaupt machen Makros in nur einer Hinsicht einen Unterschied. 
Nämlich in der Lesbarkeit und Wartbarkeit von Code. Makros sind ja 
"einfach nur" Textersatz.

Falls Du aber ein Verfahren ausdenkst, mit dem Speicher gespart wird - 
mal abgesehen von diesem Fall, in dem das offenbar nicht möglich ist -, 
dann spielt es keine Rolle ob Du dafür Makros verwendest oder nicht.

Das sind zwei verschiedene Kategorien.
Das eine, also Verwendung von Makros ist eine "Schreibweise", das 
andere, eine angepasste sparsame Repräsentation zu erdenken und 
Operationen darauf, sind "Algorithmen".
#6022522
Lesenswert?

Naj H. schrieb:
> Also macht es jetzt kein Unterschied ob ich das mit dem Marko
> mache oder
> direkt in der Struktur mit einem Zeiger?

wenn du deine Ports indirekt adressieren willst, nimm Pointer, die du 
genauso definierst, wie der Cast im Port-Zugriffsmacro (vor allem das 
volatile ist wichtig).

Das ist der saubere Weg. Mit Zusatzwissen könnte man da ein paar 
einzelne Bytes sparen, aber das lohnt sich erst, wenn du wirklich 
merkst, dass es knapp wird. Dafür stellt man seinem zukünftigem Ich 
eventuell gemeine Fallen (siehe von Peda aufgeführtes Beispiel). Daher 
am besten den Port-Zugriff in einer inline-Funktion, die deine Struktur 
nimmt, kapseln und konsequent diese Funktion benutzen. Der Compiler 
optimiert den Zugriff schon weg.

Wenn du wirklich viele Objekte deiner Struktur hast, und dich die Bytes 
wirklich schmerzen, könnte man natürlich die Ports durch Nummern 
abstrahieren und eine Pointertabelle anlegen.

Das Würde in etwa so aussehen:
1
typedef enum ePorts{
2
 Port_A = 0,
3
 Port_B,
4
 Port_C,
5
 ...
6
 Port_F, 
7
 Port_Count
8
}Ports;
9

10
typedef volatile uint8* PortPointer;
11

12
PortPointer getPort(Ports i_p){
13
  static const PortPointer s_PortLookup[Port_Count] = {
14
    [Port_A] = &PORTA,
15
    [Port_B] = &PORTB,
16
    [Port_C] = &PORTC,
17
    //...
18
    [Port_F] = &PORTF,
19
  };
20

21
  return s_portLookup[i_p];
22
}
23

24

25
typedef struct sMyS{
26
  int8_t  action;
27
  uint8_t port; // for values see Ports enumeration
28
}MyS;
29

30
void doSmth(const MyS* i_d){
31
 // ...
32
 *getPort((Ports) i_d->port) = 5;
33
 // ...
34
}

Das hätte den Vorteil, dass du die Struktur auch noch als Bitset 
definieren könntest und nur 3bit für die Portnummern verwenden könntest.
Allerdings heißt das auch, dass das Programm etwas langsamer wird, da 
der Compiler bei Zugriffen erst die Bits rauspopeln muss, wodurch auf 
der anderen Seite der Flashverbrauch geringfügig steigt.
1
typedef struct sMyS{
2
  signed   action : 5;
3
  unsigned port   : 3; // for values see Ports enumeration
4
}MyS;

Bevor man zu solchen Mitteln greift, wäre es wahrscheinlich aber eh 
ratsamer, den nächstgrößeren Controller zu benutzen.
OP #6023670
Lesenswert?

Vlad T. schrieb:
> Bevor man zu solchen Mitteln greift, wäre es wahrscheinlich aber eh
> ratsamer, den nächstgrößeren Controller zu benutzen.

Das sieht echt interessant aus! Muss ich mir mal genauer anschauen was 
da so passiert. Danke ;)

Vlad T. schrieb:
> Das hätte den Vorteil, dass du die Struktur auch noch als Bitset
> definieren könntest und nur 3bit für die Portnummern verwenden könntest.

Wie meinste das? Würdest Du bitte mal ein Beispiel bereitstellen?
Gast #6024527
Lesenswert?

Meine Meinung:
- Macros machen die meisten Probleme in C und C++.
- Anfänger sollten keine Macros verwenden.

Da werden Macros geschrieben, um nur ein paar Tasten Tipparbeit zu 
sparen. Die Seiteneffeke kommen dann Monate später, wenn man noch mal 
ein ++ an den Macroparameter hängt.

Warum nicht eine Funktion schreiben. Das ist sauber, und der Compiler 
kann wunderbar die Syntax und Semantic checken. Und ein guter Compiler 
wird die Funktion ausrollen, wenn es z.B. nur eine Zuweisung ist. Da ist 
dann noch nicht einmal ein Performanceverlust.

Und selbst 2019 gibt es noch Bücher zu C++, die man als Anfänger 
durcharbeiten könnte.
OP #6024567
Lesenswert?

Teo D. schrieb:
> Naj H. schrieb:
>> Ich habe immer noch nicht gecheckt was Du mit dem Bitset meintest.. Wie
>> würde das denn aussehen?
>
> "Bitfelder" wäre der üblichere Begriff dafür:
> http://www.c-howto.de/tutorial/strukturierte-datentypen/bitfelder/

Mir ist schon bewusst was ein Bitfeld und was Strukturen sind!
Was mir jedoch nicht ganz klar ist. Für was braucht es diese Struktur in 
dem Beispiel?
1
typedef struct sMyS{
2
  int8_t  action;
3
  uint8_t port; // for values see Ports enumeration
4
}MyS;
5

6
void doSmth(const MyS* i_d){
7
 // ...
8
 *getPort((Ports) i_d->port) = 5;
9
 // ...
10
}

Das ist das was ich nicht verstehe..
Was soll "action" & "port" bewirken..
OP #6024650
Lesenswert?

Teo D. schrieb:
> Naj H. schrieb:
>> Mir ist schon bewusst was ein Bitfeld und was Strukturen sind!
>
> Na dann nix für ungut!
>
> Naj H. schrieb:
>> Das ist das was ich nicht verstehe..
>> Was soll "action" & "port" bewirken..
>
> "action.., action..." Dito ?-/

Bist Du im wahren Leben auch so "lustig" unterwegs?

Ich würde das ganze jetzt so verstehen ->
1
typedef struct sMyS{
2
  int8_t  action;
3
  uint8_t port; // for values see Ports enumeration
4
}MyS;
5

6

7
void doSmth( MyS *obj )
8
{
9
  switch(obj->action)
10
   case 1<<0: // Ausgänge setzen
11
   case 1<<1: // Ausgänge löschen
12
}
Gast #6024655
Lesenswert?

Mal ehrlich, wenn man so lange darüber diskutieren muss, dann kann es 
keine gute Idee sein.

Als Machbarkeitsstudie im Hobby mag es noch OK sein.

Im Job programmiert man aber so, dass andere den Quelltext verstehen und 
weiter entwickeln können. Da ist für solche Diskussionen kein Platz.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren