Hi!
Der Datentyp eines Zeigers soll ja dem des Wertes entsprechen, auf den
er zeigt. Welchen Datentyp muss ich nun verwenden wenn ich ein Array
habe in dem wiederum Zeiger drin sind?
char *zeiger1, *zeiger2;
const datentyp *zeigerarray[] = { zeiger1, zeiger2 };
Und wie verhält es sich bei mehrdimensionalen Arrays? Angenommen ich
habe zwei Dimensionen. Dann ist die erste Dimension ein Zeiger-array,
deren Zeiger wieder auf andere Zeiger-Arrays zeigen. Und wann und wo
muss man casten?
kann ich in ein array auch mehrere Datentypen einspeichern? Z.b. Zeiger
und Variablen?
unsigned char variable = 128;
const datentyp multiarray[] = { *zeiger1, *zeiger2, variable };
Wie verhält sich das ganze, wenn ich auf einen Port zeigen möchte?
datentyp *port;
port = &PORTB;
einfach nur so?
lg PoWl
> Welchen Datentyp muss ich nun verwenden wenn ich ein Array> habe in dem wiederum Zeiger drin sind?
Zeiger auf Arrays gibt es zwar, werden aber eher selten verwendet. Man
nimmt stattdessen einen Zeiger auf dessen erstes Element, in diesem Fall
also einen Zeiger auf einen Zeiger.
> char *zeiger1, *zeiger2;> const datentyp *zeigerarray[] = { zeiger1, zeiger2 };
Wenn das ein Zeiger auf ein Array werden sollte, auf welches Array
sollte er denn zeigen? Das dahinter ist nur ein Initialisierer. Du
willst also vermutlich gar keinen Zeiger, sondern einfach nur ein Array:
const char *zeigerarray[] = { zeiger1, zeiger2 };
(Array aus Zeigern auf const char).
> Und wie verhält es sich bei mehrdimensionalen Arrays?
Eigentlich gibt es in C gar keine mehrdimensionalen Arrays.
> Angenommen ich habe zwei Dimensionen. Dann ist die erste Dimension ein> Zeiger-array, deren Zeiger wieder auf andere Zeiger-Arrays zeigen.
Kommt drauf an. So kann man ein mehrdimensionales Array in C nachbilden.
Vor allem bei dynamischer Allkokierung, bei der erst zur Laufzeit die
Dimensionen bekannt sind, muß man das so machen. Bei statischen Arrays
oder bei dynamischen, wo nur eine Dimension variabel ist, nimmt man der
Einfachheit halber eher direkt Arrays aus Arrays.
> Und wann und wo muss man casten?
Nirgends.
> kann ich in ein array auch mehrere Datentypen einspeichern? Z.b. Zeiger> und Variablen?
Zeiger sind auch Variablen (sofern sie einen Namen haben).
> unsigned char variable = 128;> const datentyp multiarray[] = { *zeiger1, *zeiger2, variable };
Nein, geht nicht. Du kannst einen generischen Zeiger verwenden, also
einen Zeiger auf void. Der kann auf beliebige Sachen zeigen. Allerdings
mußt du dir dann selber merken, welcher Typ das war und vor dem Zugriff
in diesen Typ casten.
> Wie verhält sich das ganze, wenn ich auf einen Port zeigen möchte?
Dann muß der Zeiger-Zieltyp eben derselbe sein wie der deines Ports.
> datentyp *port;> port = &PORTB;
Ich rate jetzt einfach mal, daß es sich vermutlich um einen AVR handeln
soll. Dann wäre der Typ uint8_t.
Paul Hamacher wrote:
> Der Datentyp eines Zeigers soll ja dem des Wertes entsprechen, auf den> er zeigt. Welchen Datentyp muss ich nun verwenden wenn ich ein Array> habe in dem wiederum Zeiger drin sind?>> char *zeiger1, *zeiger2;> const datentyp *zeigerarray[] = { zeiger1, zeiger2 };
1
char*zeigerarray[];
> Und wie verhält es sich bei mehrdimensionalen Arrays?
1
char*za[][10];
> Angenommen ich> habe zwei Dimensionen. Dann ist die erste Dimension ein Zeiger-array,> deren Zeiger wieder auf andere Zeiger-Arrays zeigen.
Nein, es wird zwar immer wieder behauptet, dass mehrdimensionale
C-Arrays eigentlich eindimensionale Arrays mit Pointern auf die
weiterend Dimensionen sind, aber das stimmt einfach nicht.
> Und wann und wo> muss man casten?
Gar nicht.
> kann ich in ein array auch mehrere Datentypen einspeichern? Z.b. Zeiger> und Variablen?
Mit Tricks ja. Als Anfänger solltest du das lassen, du blickst ja schon
bei den Grundlagen nicht durch.
> unsigned char variable = 128;> const datentyp multiarray[] = { *zeiger1, *zeiger2, variable };
> Wie verhält sich das ganze, wenn ich auf einen Port zeigen möchte?
Lass es sein.
avr-gcc / avr-lib: Mit entsprechenden Makros bekommst du eine
Portadresse in einem unit16_t Datentypen, also einem 16-Bit Integer, und
nicht etwa einem Pointer. Anfangen kannst du damit wenig.
> datentyp *port;> port = &PORTB;
> avr-gcc / avr-lib: Mit entsprechenden Makros bekommst du eine> Portadresse in einem unit16_t Datentypen, also einem 16-Bit Integer,> und nicht etwa einem Pointer. Anfangen kannst du damit wenig.
Deswegen verstehe ich auch nicht, warum du das schreibst.
>> datentyp *port;>> port = &PORTB;> uint16_t port = _SFR_ADDR(PORTB);
Dann hat er aber eben keinen Zeiger.
>> einfach nur so?>> Nein.
Doch, für datentyp = volatile uint8_t funktioniert das prima (das
volatile hatte ich in meinem Vorposting vergessen):
volatile uint8_t *port;
port = &PORTB;
Danke für die Antworten! Ist mir schon alles etwas schlüssiger. Diese
union-Geschichte verwirrt mich allerdings noch und ich frage mich wo man
lernt so etwas anzuwenden? In meinen C-Büchern und im GCC-Tutorial
stehen vielleicht die Grundlagen aber nicht wie man damit in der Form
umgehen kann.
Mein Problem ist: Ich habe einen PORT, ein DDR und zwei unsigned chars.
Und das ganze Paket 3 mal.
Mit dem Inhalt einer Variablen möchte ich nun bestimmen welches dieser
Pakete in meiner Funktion verwendet werden soll.
Quasi folgendermaßen:
[0][0] PORTB
[0][1] DDRB
[0][2] 394
[0][3] 102
[1][0] PORTD
[1][1] DDRD
... usw..
und jetzt mit einer variable so in der Art drauf zugreifen:
array[variable][0] = 0xFF;
Nachdem was ihr geschrieben habt wird das also unheimlich kompliziert?
Also lieber die PORTs, DDRs und Zahlen in jeweils ein separates array
speichern?
[0] = PORTB
[1] = PORTD
...
[0] = DDRB
[1] = DDRD
...
[0] = 394
[1] = 102
... usw...
lg PoWl
Paul Hamacher wrote:
> Also lieber die PORTs, DDRs und Zahlen in jeweils ein separates array> speichern?
Nein, nicht Arrays.
Das Sprachelement, welches dir erlaubt unterschiedliche Datentypen
zu gruppieren, ist eine Struktur. Selbst dann, wenn alle Struktur-
elemente den gleichen Typ haben, aber nur einen logischen
Zusammenhzang, fährst du mit einer Struktur immer besser.
Also bastelst du dir erst mal eine Struktur, die dir einen solchen
Datensatz beschreibt.
EIn Datenelement ist bei die Zusammenstellung aus Port, DDR, Wert1
und Wert2 (Für Wert1 und Wert2 solltest du noch sinnvolle Namen
benutzen, ich konnte aus dem Zusammenhang nicht erkennen wofür
die beiden Zahlen stehen)
1
structPortDaten
2
{
3
volatileuint8_t*Port;
4
volatileuint8_t*Ddr;
5
uint16_tWert1;
6
uint16_tWert2;
7
};
Damit hast du jetzt eine Möglichkeit, wie du einen Datensatz
{ PORTB, DDRB, 314, 104 }
als eine Einheit betrachten kannst. Wenn du mit einem struct PortDaten
um dich wirfst, dann ist das immer diese Einheit aus diesen 4 Werten.
Und nun möchtest du ein Array aus derartigen Strukturen bauen. Du
willst also viele derartige Portdaten haben
1
structPortDatenDaten[100];
Der Wert1 vom 1. Element (bei 0 angefangen zu zählen, weil es
ja ein Array ist), kriegt zb. so seinen Wert
1
Daten[1].Wert1=200;
Und um den Port im 5. Element zu setzen, schreibst du
1
Daten[5].Port=PORTB;
Und um die Daten des 6. ELements an ihrem Port auszugeben
würde man schreiben
1
Daten[6].Port=Daten[6].Wert2;
ABer sowas würde man wahrscheinlich schon mal in einer Funktion
verstecken. Die Funktion bekommt nur den Datensatz übergeben
und erledigt dann das Setzen des DDR und die Ausgabe an den Port
1
voidOutData(structPortDatenData)
2
{
3
Data.Ddr=Wert1;
4
Data.Port=Wert2;
5
}
Aufgerufen wird es dann so
1
....
2
3
OutData(Daten[7]);
Nicht alles ist ein Array. Arrays sind nur dann angebracht,
wenn du gleichartige Dinge speichern musst. Gleichartig heißt
in dem Fall weniger 'vom gleichen Datentyp' sondern mehr:
'Das sind logisch gesehen gleichartige Dinge'. Eine Portbezeichnung,
das DDR Register sowie die auszugebenden Werte sind logisch gesehen
keine gleichartigen Dinge. Also ist ein Array nicht angebracht,
sondern man fasst diese Elemente zunächst zu einer Struktur
zusammen und schafft sich so logisch gleichartige Sachen.
Ein anderes Beispiel: Du willst Daten (Mehrzahl von Datum, also
3. März, 5. April, etc.) speichern. Nun: Tag, Monat, Jahr sind alles
nur Zahlen. Heißt das jetzt, daß du dir nur ein Zahlenarray
machst? Nein! Tag, Monat und Jahr sind logisch gesehen verschiedene
Dinge. Also mache ich mir zunächst mal einen neuen Datentyp dafür,
der mir diese 3 Dinge zusammengruppiert. Das macht auch deshalb Sinn,
weil ich ja in erster Linie mit Daten um mich werfen will und nicht
mit Tagen, Monaten oder Jahren.
1
structDatum
2
{
3
uint8_tTag;
4
uint8_tMonat;
5
uint16_tJahr;
6
};
Und das ist dann meine Basiseinheit, mit der ich weiter arbeite.
Anderes Beispiel. Du willst Personendaten speichern. Jede Person
hat einen Vornamen und einen Nachnamen. Sind doch alles nur
Strings. Mache ich mir deshalb ein Stringarray? Nein, natürlich
nicht! Ich mache mir zunächst eine Gruppierung, die mir Vorname
und Nachname gruppiert und zu einer Person zusammenfasst
1
structPerson{
2
charVorname[30];
3
charNachname[30];
4
};
Jetzt hat die Person ev. noch einen Geburtstag. Holla, da
kann ich ja den struct Datum von da oben benutzen. Der macht
doch genau das: Ein Tagesdatum speichern.
1
structPerson{
2
charVorname[30];
3
charNachname[30];
4
structDatumGeburtstag;
5
};
Jetzt will ich vielleicht die Werte für Peter speichern, der bei
mir als Arbeiter angestellt ist. (Das wird man zwar so nicht machen,
aber es ist ja nur ein Beispiel um die Zugrifssyntax zu zeigen)
1
structPersonArbeiter;
2
3
strcpy(Arbeiter.Vorname,"Peter");
4
strcpy(Arbeiter.Nachname,"Mustermann");
5
Arbeiter.Geburtstag.Tag=22;
6
Arbeiter.Geburtstag.Monat=3;
7
Arbeiter.Geburtstag.Jahr=1963;
oder ich bau mir Funktionen, die die Werte wieder ausgeben:
1
voidOutDate(structDatumDate)
2
{
3
// hier könnte man mehr Logik hineinstecken, die zb. den
Um, dann ev. ein Array von max. 100 Personen zu haben, die
ich in einer Personallistenfunktion ausgeben will
1
structPerson[100];
2
intNrPersonen;
3
4
...
5
6
voidPersonalliste()
7
{
8
inti;
9
10
for(i=0;i<NrPersonen;++i)
11
OutPerson(Personen[i]);
12
}
Wenn du Literatur hast, dann studier mal das entsprechende
Kapitel über Strukturen und auch wie man verhindern kann,
dass ständig Datensätze bei der Funktionsübergabe kopiert
werden müssen.
Ich bin erstaunt, das sollte man ins Tutorial verlinken, ein riesiges
dankeschön, dass du dir so eine Mühe gemacht hast! Im Ernst, hast du dir
mal überlegt Bücher zu schreiben? Ich könnte dich uneingeschränkt
empfehlen.
Hab mir erst die hälfte durchgelesen und werde jetzt weiterlesen.
zu meiner verteidigung muss ich sagen: Ich bin PHP und JavaScript
gewohnt. In PHP kann man recht schlampig mit Arrays umgehen und in
JavaScript ist soweit alles ein riesiger Objekt/Arrayhaufen.
lg
Paul Hamacher
Nochmals danke für die ausführliche Beschreibung!
Habe das eben mal ausprobiert:
1
structlight
2
{
3
volatileuint8_t*port;
4
volatileuint8_t*ddr;
5
constuint8_tportpin;
6
uint8_tpwm_value;
7
};
8
9
structlightchannels[3];
Für folgende Zeile krige ich jedoch eine Fehlermeldung:
1
channels[0].pwm_value=10;
error: expected '=', ',', ';', 'asm' or '__attribute__' before '.' token
Hab ich was falsch geschrieben?
Habe hier übrigens ein C-Buch in dem die Struct-Schreibweise auch so
drinsteht. In meinem µC-Buch im C-Abschnitt wird das allerdings
irgendwie anders gemacht:
1
struct{
2
uint8_tbla;
3
uint8_tblub;
4
}variable;
damit hat man dann scheinbar gleich ne variable definiert:
variable.bla = 10;
oder
Paul Hamacher wrote:
> ist das das gleiche wie
Im Prinzip ja.
Mittels
1
structfoo
2
{
3
intbla;
4
};
legst du nur fest, wie so eine Struktur aussieht.
Um eine Variable davon anzulegen, schreibst du
1
structfoomyVar;
Das ist die 'Langform'.
Das kann man jetzt auch etwas verkürzen und zusammenziehen.
1
structfoo
2
{
3
intbla;
4
}myVar;
definiert in einem Zug, wie die Struktur aussieht und definiert
gleichzeitig eine Variable dieses Typs.
Muss man jetzt viel mit Strukturen arbeiten, dann gibt es in C
eine kleine Unschönheit.
Die Deklaration
1
structfoo
2
{
3
intbla;
4
};
deklariert foo nicht automatisch als neuen Datentyp. Daher muss
man überall wo foo verwendet werden soll immer struct foo schreiben.
Auf Dauer ist das lästig. Dafür gibt es aber den typedef.
Mittels typedef kann man für einen vorhandenen Datentyp einen
2-ten Namen festlegen
1
typedefunsignedintmUint;
würde beispielsweise vereinbaren, dass der Datentyp unsigned int
auch unter dem Namen mUint verfügbar sein soll.
Folgerichtig kann man schreiben
1
typedefstructfoomFoo;
um damit festzulegen, dass der Datentyp struct foo auch unter
dem alternativen Namen mFoo verfügbar sein soll. Damit ist
man dann die ständige Schreibere von struct im Code los.
1
structfoomyVarA;
2
mFoomyVarB;
Die beideb Variablen myVarA und myVarB sind vom exakt gleichen
Datentyp, nur ist die Schreibweise bei myVarB kürzer.
(Wir erinnern uns: mFoo ist ja nur ein anderer Name für struct foo.
mFoo ist kein eigener Datentyp, der von struct foo unabhängig wäre)
Und natürlich kann man das dann auch zusammenfassen
1
tyepdefstructfoo
2
{
3
intbla;
4
}mFoo;
definiert in einem Zug, wie eine struct foo aussieht und legt
über den typedef gleichzeitig einen zweiten Namen für struct foo
fest.
********************
Überprüf nochmal, ob an dieser Stelle auch tatsächlich alle Variablen
bekannt sind. Ich rate mal: Du hast den
struct light channels[3];
in einem Header File drinnen, hast aber vergessen das Header File
zu inkludieren. Damit weiss der Compiler nicht dass channels[3]
in Wirklichkeit eine Struct Variable ist und mäkelt den . an.
Ich bin jedes mal erneut erstaunt über die Qualität deiner Beiträge! ;-)
1
#include<avr/pgmspace.h>
2
3
structlight
4
{
5
volatileuint8_t*port;
6
volatileuint8_t*ddr;
7
constuint8_tportpin;
8
uint8_tpwm_value;
9
};
10
11
structlightchannels[3];
12
13
channels[0].pwm_value=10;
14
15
16
intmain(void)
17
{
18
while(1);
19
}
Habe mein Programm mal aufs Minimum reduziert aber der Fehler tritt
trotzdem auf.
Wenn ich übrigens die pgmspace.h nicht include bekomme ich noch einen
Fehler für die Zeile
1
volatileuint8_t*port;
expected ':', ',', ';', '}' or '__attribute__' before '*' token
Paul Hamacher wrote:
> struct light> {> volatile uint8_t *port;> volatile uint8_t *ddr;> const uint8_t portpin;> uint8_t pwm_value;> };>> struct light channels[3];>> channels[0].pwm_value = 10;
Wie andere schon gesagt haben:
Das ist eine Zuweisung und Zuweisungen können nur in Funktionen
sein. Da das aber nicht in einer Funktion ist -> Fehler.
Aber ich rate mal weiter.
Du willst eine Initialisierung ! Eine Initialisierung ist
aber immer Bestandteil der Variablendefinition!
1
structlightchannels[3]=
2
{
3
{PORTB,DDRB,10},
4
{PORTC,DDRC,20},
5
{PORTD,DDRD,15}
6
};
Aufpassen: Auch wenn hier ebenfalls ein '=' Zeichen benutzt wird,
so ist das doch etwas fundamental anderes als das was du hast.
Du hast eine Zuweisung. Einer Variablen kann man beliebig oft
etwas zuweisen. Bei einer Zuweisung wird ein bereits vorhandener
Wert überschrieben.
In einer Variablendefinition ist das aber immer eine Initialisierung
(auch wenn die Schreibweise mittels '=' ähnlich ist). Eine
Variable kann nur einmal initialisiert werden. Bei einer
Initialisierung bekommt die Variable das erste mal einen gültigen
Wert.
(*) Letztere Unterscheidung von Überschreiben und erster Wert
ist eigentlich nur für C++ interessant. Es schadet aber nichts,
wenn man sich diese Denkweise gleich zu eigen macht.
> Wenn ich übrigens die pgmspace.h nicht include bekomme ich noch einen> Fehler für die Zeile
1
volatileuint8_t*port;
> expected ':', ',', ';', '}' or '__attribute__' before '*' token
Für uint8_t und Konsorten musst Du aber auch die inttypes.h einbinden.
Da die pgmspace.h intern ebenfalls die Typen aus der inttypes.h braucht,
inkludiert sie die selber, falls das noch nicht geschehen ist. Deshalb
taucht der Fehler auch nicht auf, wenn Du pgmspace.h einbindest.
Hm, ich bin mir selbst nicht darüber im klaren was ich möchte.. ich sag
es hier mal auf deutsch, nicht auf C:
Es ist nicht geplant, dass *port, *ddr und portpin irgendwann im Laufe
des Programms nochmal verändert werden. Die werden also am Anfang
festgelegt. Folglich werden sie am Anfang wohl ganz normal im Speicher
angelegt und von dort dann auch wieder gelesen.
Brauch ich dann überhaupt irgendwelche Zusätze so wie const oder
volatile? Const funktioniert ja hier eh nicht, da die Werte sich ja
nicht verändern und sowieso im SRAM gespeichert werden müssen um dann
über das Strukturarray channels per index abrufbar sein zu können.
lg PoWl
Paul Hamacher wrote:
> Es ist nicht geplant, dass *port, *ddr
Wie willst du dann deine Ausgaben machen, wenn sich *port
nie verändern darf. :-)
port ist ein Pointer auf den Ausgabeport.
Also ist *port dann der Wert, der am Ausgabeport ausgegeben wird.
Und der wird sich in den allermeisten Fällen während eines
Programmablaufs ändern. Sonst könnte man ja den ganzen Port
durch einen Draht zu entweder Vcc oder GND ersetzen :-)
Wenn
ptr * ptrVariable;
ein Pointer ist (ptr sei irgendein Datentyp), dann ist
*ptrVariable, der Wert auf den der Pointer zeigt.
ptrVariable
+-------+ +------+
| o----------------->| |
+-------+ +------+
Gendanklich verfolgt also der * vor dem Pointernamen ganz einfach
nur den Pfeil und holt den Wert von der Speicherstelle auf die
der Pfeil zeigt (oder speichert dort einen neuen Wert im Falle
einer Zuweisung)
In deinem Fall erhebt sich die Frage: Was soll den konstant sein?
Der Pointer selbst (also das Kästchen an dem der Pfeil startet und
damit der Pfeil selbst) oder das Kästchen an dem der Pfeil endet.
Je nachdem gibt es unterschiedliche Schreibweisen:
uint8_t * const ptrVariable;
Hier ist der Pfeil konstant. Der Pointer wird einmal initialisiert
und zeigt dann immer auf die gleiche Speicherstelle.
uint8_t const * ptrVariable;
const uint8_t * ptrVariable;
Hier ist das worauf der Pfeil zeigt konstant. Der Pointer (der Pfeil)
kann allerdings auf andere Kästchen zeigen, wenn ihm etwas zugewiesen
wird.
Geh einfach die Deklaration von rechts nach links durch. const
wirkt immer auf das Teil links von ihm. In
uint8_t * const
steht unmittelbar links vom const der Pointer-Stern. Also ist auch
der Pointer konstant. Der Pointer selbst (der Pfeil) kann nicht
mehr verändert werden.
uint8_t const *
Hier steht unmittelbar links vom const das uint8_t, also der Wert
auf den der Pointer zeigt. Also ist folgerichtig dieser Wert
konstant. Der Pointer kann sich aber ändern (weil ja rechts vom
* kein const steht)
Kleine Ausnahmeregelung: wenn links vom const nichts mehr steht,
dann wirkt das const auf das nächste Element rechts von ihm.
Daher sind
uint8_t const MyVar; und
const uint8_t MyVar;
identisch.
Frage zum Verständnis: Was mag wohl ein
uint8_t const * const pPtr;
aussagen?
PS: Für volatile gilt genau das gleiche wie für const.
Ich glaube das kommt meinem Anliegen dann schon viel näher :-)
Und hier noch die Lösung der Aufgabenstellen:
Das deklariert einen konstanten Pointer pPtr der auf einen konstanten
8-Bit Wert zeigt :-)
Danke vielmals! Dieser Thread ist bei mir ganz oben in der
Favoritenliste.
lg PoWl