Moin moin, der Titel sagt eigentlich schon alles.
ich habe eine Funktion die per Bubblesort sortieren soll, der übergebe
ich ein Array mit den zu sortierenden Elementen. Bisher hab ich die
Elemente auch noch volatile, ausserhalb der Funktion, um Ende
vergleichen zu können von welcher Position das kleinste Element nun
stammt.
Nun wollte ich das flexibler gestalten, und das Array einfach 2mal
vorhalten, einmal unsortiert einmal zu sortierend. Ausserdem wollte ich
die Größe des übergebenen Arrays mit sizeof ermitteln statt wie bisher
mit 9 festen Werten zu arbeiten. Aber sizeof gibt mir 2 anstatt, wie
doch bei 9Elementen a 16bit zu erwarten wäre, 18 zurück. Kann mir einer
von euch dazu n Tip geben?
(es geht darum, mein Floppy-Musik Gedöns auf mehrere Kanäle zu
erweitern, und es muss ja entschieden werden, wann welches Laufwerk
welchen Ton spielt, und vor allem welches Laufwerk als nächstes einen
Tonwechsel zu machen hat)
Ich betrachte die Werte im Atmels Studio 6.2 im Simulator.
hier der entsprechende Auschnitt
1
//Platz0-7 für die 8 Gesamtdauern der Melodien, Platz8 als Platz zum tauschen
j. t. schrieb:> Und wie kann ich dann die Anzahl der Elemente in einem Array bestimmen?
Also was Du in der Funktion versucht hast ist schonmal ganz schlecht.
Anzahl der Elemente in einem Array: sizeof(array) / sizeof(array[0]),
weil das halt auch funktioniert wenn Du den Typ der Arrayelemente
änderst und die Annahme, ein 16-Bit-Typ wäre 2 C-Bytes groß, grundlegend
falsch ist.
In eine Funktion must Du halt die Anzahl als Parameter reingeben, hat
Schmiedl ja schon erklärt warum nicht funktioniert was Du versucht hast.
Wenn man die Array-Größe explizit durch ein "Null"-Element festlegt,
dann muß die Funktion dieses eben suchen. Alternativ gibt man halt die
Länge mit.
Der Funktionsparameter selbst ist lediglich ein Pointer auf das erste
Array-Element, mehr nicht. Größe auf AVR i.d.R. 2 Byte.
j. t. schrieb:> n = sizeof (&SortierArray)/2 damit wird n = 1....
sizeof (*SortierArray)
Und "Dereferenzierung" googeln. Für die Anzahl der Elemente wäre dann
noch durch die Größe zu teilen.
aber das Ergebnis kommt doch aus dem Simulator, und da läuft ja ein
fertig kompiliert und assembliertes Programm, oder nicht? Und da wird
mir n halt mit 2 angezeigt..
@fritz, das mit dem Sternchen hat auch 1 ergeben... das hatte ich schon
versucht, als das Stichwort Pointer fiel... Danke für das Stichwort, das
werd ich mir mal zu Gemüte führen.
Der "Array-Parameter" ist aber mit [] deklariert, hat also gar keine
Größenangabe. Und daß mit und ohne Dereferrenzierung 1 rauskommt, liegt
nur daran, daß sowohl Pointer als auch int16_t eben 2 Byte lang sind.
j. t. schrieb:> und da ist das Array auch einfach ohne Größenangabe.
Der Unterschied ist, dass das Array direkt in der Funktion definiert
wird.
Daher weiß der Compiler schon wie viele Elemente das Array enthält.
Dein Übergabeparameter lautet:
volatile uint16_t SortierArray[]
Wozu das volatile? Ist unnötig.
Warum schreibst du den Übergabeparameter als Array?
Da gibt es keinen Grund für, es sei denn du gibst die Größe mit an, also
z.B.:
uint16_t SortierArray[16]
Dann wüsste der Compiler, dass das Array, welches übergeben wird, 16
Elemente besitzt. Ob das sizeof(...) Makro dann die Array-Größe zurück
gibt weiß ich jetzt auch nicht (müsste man mal ausprobieren).
Ohne Angabe der Array-Größe ist es das selbe, als ob du einen Pointer
als Übergabeparameter verwendest, also:
uint16_t *SortierArray
Pointer werden immer als einfache Adresse auf einen Speicherbereich
übergeben. Ohne Angabe der Größe. Deshalb schließen Strings in C auch
immer mit 0 ab. Andere Programmiersprachen lösen das anders und dort
wird z.B. im ersten Element des Arrays die Länge gespeichert.
Du kannst entweder die Größe als zweiten Parameter übergeben oder die
Größe innerhalb deines Arrays als Wert speichern oder einen Wert
definieren, welcher das Ende des Arrays signalisiert.
Bastler schrieb:> Der "Array-Parameter" ist aber mit [] deklariert, hat also gar keine> Größenangabe.
Selbst wenn er mit Größenangabe deklariert wäre (also bspw, mit [10]),
wäre seine Größe immer noch 2, da er nach wie vor ein Pointer ist.
Folgende drei Schreibweisen sind exakt gleichbedeutend:
1
voidsub(int*a);
2
voidsub(inta[]);
3
voidsub(inta[10]);
Die Größenangabe im letzten Beispiel hat somit ausschließlich
Dokumentationscharakter. Man kann sie verwenden, um den Nutzer der
Funktion darauf hinzuweisen, dass diese ein Array mit genau 10 Elementen
erwartet. Ein Vergleich dieser Größe mit der des übergebenen Arrays
durch den Compiler erfolgt aber normalerweise nicht.
Ich lese grad einiges über Pointer, und so manches wird klarer =).
Ich hab noch fast alle Variablen als volatile deklariert, weil sie sonst
beim debuggen in der Watch (eine Funktion vom AtmelStudio, in der man
Variablen sammeln und ihre Werte angucken kann) nicht angezeigt werden.
Wieso wird mir ein Fehler angezeigt, wenn ich :
1
uint8_ti=0;
2
uint16_tarray[8];
3
4
array=&0;
5
*array=42;
wenn ein Pointer immer 2byte groß ist, müsste doch auch ein Zeiger auf
eine 8bit Variable 2byte groß sein? Könnte dann aber ungerade sein, und
alle 16bit variablen hätten dann alle gerade, oder alle ungerade
Adressen?
P.S. Könnte man da mit +/- 1 was erreichen`?
P.P.S. Ausprobiert und es geht. Also zumindest kompiliert er das jetzt.
Gleich mal sehen, ob ich mit -1 was zu tiefes oder das richtige
anspreche.
j. t. schrieb:> array = &0;
was willst Du damit erreichen?
Eine Konstante hat keine Adresse, ich kann mir nicht vorstellen dass der
Compiler da nicht meckert
j. t. schrieb:> int main(void) {> int zahlen[] = {3,6,3,5,6,3,8,9,4,2,7,8,9,1,2,4,5};> so macht man das laut:> http://openbook.galileocomputing.de/c_von_a_bis_z/011_c_arrays_004.htm>> und da ist das Array auch einfach ohne Größenangabe.
Ja, das ist einer der Parts, wo man versucht hat, C intuitiver zu
gestalten, was leider völlig in die Hose gegangen ist. Du hast hier zwei
Sonderfälle rausgegriffen. Ein Array muss immer eine Größe haben. Wenn
du ein Array definierst und auch gleich initialisierst, kann man die
explizit hingeschriebene Größe aber auch weglassen, da sie der Compiler
aus der Anzahl an Elementen im Initialisierer ermitteln kann. Das ist
bei obigem Code der Fall.
Zu deiner Funktion: Man kann Arrays niemals direkt an eine Funktion
übergeben. Wenn du das versuchst, wird immer nur ein Zeiger auf das
erste Element übergeben. Diese Schreibweise
j. t. schrieb:> void NaechsterTon (volatile uint16_t SortierArray[], volatile uint16_t> Unsortiert[])
ist eine spezielle Variante davon. Auch hier werden nur Zeiger übergeben
und keine Arrays. Die Schreibweise sagt dem Leser nur, daß da ein Array
dahinter steht, aber wie Yalu schon geschrieben hat, ist das für den
Compiler exakt das gleiche, als wenn du da volatile uint16_t* übergeben
hättest.
Daher sieht es in beiden Fällen wie ein Array ohne Größenangabe aus,
aber es sind in Wirklichkeit zwei ganz unterschiedliche Dinge.
j. t. schrieb:> wenn ein Pointer immer 2byte groß ist, müsste doch auch ein Zeiger auf> eine 8bit Variable 2byte groß sein?
Das schreibt C zwar nicht zwingend vor, aber es ist in der Regel so.
> Könnte dann aber ungerade sein, und alle 16bit variablen hätten dann alle> gerade, oder alle ungerade Adressen?
Ich verstehe nicht, was du damit meinst. Es gibt Prozessoren, wo
Variablen aus 2 Bytes immer gerade Adressen haben müssen. Das Stichwort
ist "Alignment". Der AVR gehört aber nicht dazu. Dem ist es wurscht, ob
eine 16-Bit-Variable an einer geraden oder einer ungeraden Adresse
anfängt.
> P.S. Könnte man da mit +/- 1 was erreichen`?
Was willst du denn damit errreichen?
> P.P.S. Ausprobiert und es geht. Also zumindest kompiliert er das jetzt.> Gleich mal sehen, ob ich mit -1 was zu tiefes oder das richtige> anspreche.
Was soll "das richtige" denn sein?
j. t. schrieb:> Bisher hab ich die> Elemente auch noch volatile
TIPP: Das AVR Studio bzw. der genutzte Compiler erkennt ungenutzte
Variablen. Daher: Codeoptimierung ausschalten. Dann braucht es auch das
Volatile nicht.
j. t. schrieb:> Nun wollte ich das flexibler gestalten, und das Array einfach 2mal> vorhalten, einmal unsortiert einmal zu sortierend.
TIPP: Auf dem PC mag das heute anders sein, aber auf einem µC ist
Speicher wertvoll. Also: Statt das Array zweimal zu halten, erzeuge
lieber ein Array, in dem nur Indizes gespeichert werden. Dieses Array
lässt sich dann gut über den Inhalt direkt adressieren.
j. t. schrieb:> Ausserdem wollte ich> die Größe des übergebenen Arrays mit sizeof ermitteln
Das kannst Du getrost vergessen. Arrays sind in C/C++ eine Katastrophe.
Unter Windows hat man daher die SafeArrays eingeführt - also
Datensturkturen, die Informationen über das Array beinhalten. In C/C++
ist das nur ein zusammenhängender Block im Speicher mit der Größe
n*sizeof(Datentyp);. Lass eine Zählvariable mitlaufen, die die aktuelle
Länge kennt, dann hast Du das Problem umgangen. Sizeof(array) geht in
C/C++ nicht, da es nur die Größe des Datentyps in Byte wiederspiegelt.
Da ist dann gesagt, wieviele in unsortiert stehen werden und wieviel
Platz in SortierArray mindestens Platz haben wird. Beides muß
natürlich der Aufrufer sicherstellen. Bei kleinerem RAM im AVR und
weniger als 256(257) Tönen, darf's auch eine 8-Bit Anzahl sein. Spart
etwas "Register".
Na das ist ja nochmal einiges zusammengekommen.
Die +/-1 Geschichte in der Adresse hatte ich mir gedacht, um auch mit
nem 16bit-Pointer 8bit Variablen ansprechen zu können.... dafür wäre
dann wohl typcasting das richtige stichwort??
schreiben. Denn dann ist der Ausdruck komplett davon unabhängig, aus
welchem Datentyp sich das Array zusammensetzt.
Voraussetzung ist allerdings, das das Array auch tatsächlich als Array
vorliegt. In einer Funktion, der du ein Array "übergibst", ist das nicht
gewährleistet. Eine Funktion
1
voidfoo(int*data)
2
...
bekommt lediglich die Anfangsadresse des Arrays in Form eines Pointers.
Die Funktion kann prinzipiell nicht feststellen, ob dieser Pointer auf
eine einzelne Variable zeigt, so wie in
1
voidbar()
2
{
3
intb;
4
5
foo(&b);
6
}
oder ob die Adresse zu einem Array gehört
1
voidbaz()
2
{
3
intvalues[5];
4
5
foo(values);
6
}
Das hier immer alles richtig läuft, das obliegt dir, dem Programmierer,
dafür Sorge zu tragen. Aber so wild ist das auch wieder nicht in der
täglichen Praxis.
Martin Schwaikert schrieb:> TIPP: Das AVR Studio bzw. der genutzte Compiler erkennt ungenutzte> Variablen. Daher: Codeoptimierung ausschalten. Dann braucht es auch das> Volatile nicht.
danach hatte ich neulich schonmal kurz gesucht, konnte aber auf die
schnelle nichts finden, wo ich die Optimierungen einstellen konnte. Ich
weiß, das es im studio 4 noch einfach mit nem Häkchen ging, den man
setzen konnte.
Aber bis hierher euch allen ersteinmal vielen Dank für die zahlreichen
und sehr hilfreichen Antworten =)
MfG Chaos
j. t. schrieb:> dafür wäre> dann wohl typcasting das richtige stichwort??
Nein.
Das ist Quatsch. Du brauchst da nichts casten.
Du musst unterscheiden zwischen der Pointer Variablen selber und dem
worauf der Pointer zeigt!
In der Pointer definition sind 2 Angaben enthalten
1
uint8_t*pPtr;
da steht, dass pPtr ein Pointer ist. Damit ist klar, dass diese Variable
für sich selbst 16 Bit brauchen wird. Weil es ja ein Pointer ist.
Und da steht weiters, dass das, worauf der Pointer zeigt (also das was
man an der im Pointer gespeicherten Adresse vorfinden wird, wenn man
dort nachsieht), ein uint8_t ist.
1
pPtr
2
+----------+
3
| o---------------------------+
4
+----------+ |
5
v
6
+-----+
7
| |
8
+-----+
pPtr hat 16 Bit. Das worauf es zeigt, hat 8 Bit.
Wie groß ein Pointer ist, wieviel Platz der brauchen wird, das weiß der
Compiler schon alleine.
Aber was ist denn im folgenden (konstruierten) Fall?:
uint8_t foo1;
uint16_t foo2;
uint16_t *foopnt;
foo1 = 12;
foo2 = 32;
foopnt = &foo1;
*foopnt = 18;
Wieso wird da ein Fehler gezeigt? 18 liegt doch locker im Bereich einer
8bit variable? In so einem Fall müsste ich das also so machen?:
uint8_t foo1;
uint8_t *foopnt8
uint16_t foo2;
uint16_t *foopnt16;
j. t. schrieb:> Na das ist ja nochmal einiges zusammengekommen.>> Die +/-1 Geschichte in der Adresse hatte ich mir gedacht,
Alignement hat nichts mit +/-1 zu tun, die Adresse der Daten wird nicht
verändert. Es handelt sich nur um Abstände (padding) welche zwischen
struct membern eingefügt werden
> um auch mit> nem 16bit-Pointer 8bit Variablen ansprechen zu können.... dafür wäre> dann wohl typcasting das richtige stichwort??
Typecasting würde ich wenn immer möglich vermeiden, ein beispiel warum:
((uint8_t*)x)[0] ist von der endianness des systems abhängig, d.h.
1
uint16_tx=256;
2
((uint8_t*)x)[0];// ist 0 bei Little-endian und 1 be big-endian
Stadessen besser gleich überall uint8_t verwenden, dann ist auch kein
Typecasting nötig, oder mit shiftoperationen die einzelnen bytes
Ansprechen, shiftoperationen sind nicht von der endianness des systems
abhängig.
1
(x>>0)&0xff// unteres byte
2
(x>>8)&0xff// oberes byte
3
4
x=(x&(~0xff<<0))|((wert<<0)&(0xff<<0))// ersetes byte auf wert setzen
5
x=(x&(~0xff<<8))|((wert<<8)&(0xff<<8))// zweites byte auf wert setzen
j. t. schrieb:> Wieso wird da ein Fehler gezeigt?
Bitte gewöhn dir an, dass Fehlermeldungen einen Sinn haben. Dieser Sinn
versteckt sich im Text der Fehlermeldung.
'Ein Fehler wird angezeigt' ist recht nichtssagend. Du gehst ja auch
nicht zum Doktor und sagst: es tut weh, mach mal.
1
uint8_tfoo1;
2
3
...
4
5
uint16_t*foopnt;
6
7
foopnt=&foo1;
foopnt ist ein Pointer der auf einen unsigned int zeigen kann, der 16
Bit gross ist. foo1 ist nicht 16 Bit groß.
> In so einem Fall müsste ich das also so machen?:
Genau.
Das worauf ein Pointer zeigt bzw. zeigen kann, ist integraler
Bestandteil eines Pointerdatentyps. Und das ist auch gut so. Du willst
nicht haben, dass du nach belieben mit Pointern um dich schmeissen
kannst und keinen interessierts. Da könnte man sich so Dinge wie
Datentypen auch gleich sparen, wenn sie eh nichts bewirken und keine
Konsequenzen haben.
j. t. schrieb:> Aber was ist denn im folgenden (konstruierten) Fall?:>> uint8_t foo1;> uint16_t *foopnt;> foopnt = &foo1;> *foopnt = 18;
Ein schönes beispiel für eine Speicherzugriffsverletzung:
foo1 ist 1 byte gross
1
foopnt=&foo1;
lässt foopnt auf den speicher von foo1 zeigen, das ist:
a) undefined behavour wegen aliasing+optimizing
b) der kompiler glaubt er zeige auf 2 bytes, wo nur eins ist
1
*foopnt=18;
Und hier wurde foo1 und das folgende byte mit 18 überschrieben, foo1
kann 0 oder 18 sein, und das überschriebene folgende byte kann alles
sein, danach kann das Programm alles mögliche tun!
Ja also dann, ich glaube ich hab endlich den Sinn und Zweck von den
bisher so ominösen Pointern verstanden =).
Nochmal vielen Dank euch allen, ihr wart eine große Hilfe
Daniel A. schrieb:> Und hier wurde foo1 und das folgende byte mit 18 überschrieben, foo1> kann 0 oder 18 sein, und das überschriebene folgende byte kann alles> sein, danach kann das Programm alles mögliche tun!
und ob foo1 dann 0 oder 18 ist, hängt dann vom endian ab?
@ karl-heinz:
klar recht hast du, aber in diesem Fall dachte ich, die Fehlermeldung
wäre klar, das sie ja schon aus dem fehlerhaften Konzept hervorgeht.
Natürlich hat er hier etwas alla "du versuchst grad nen 8bittigen in nen
16bittigen zu schmeißen"
j. t. schrieb:> @ karl-heinz:> klar recht hast du, aber in diesem Fall dachte ich, die Fehlermeldung> wäre klar, das sie ja schon aus dem fehlerhaften Konzept hervorgeht.
EIn 'Konzept' das nur in deinem Kopf existiert.
Ich hingegen muss erst mal deine COdezeilen studieren, muss rausfinden
was eigentlich falsch ist und daraus schliessen was deine Überlegung
gewesen sein könnte.
Erleichtere mir die Arbeit, in dem du mich wenigstens nicht nach dem
Fehler im Code suchen lässt, sondern mir den Hinweis gibst welche Zeile
fehlerhaft ist und was der Compiler dazu gemeint hat.
Karl Heinz schrieb:> Aber du solltest den Ausdruck besser als ....> sizeof(zahlen)/sizeof(*zahlen)> oder wenn dir die Dereferenzierung suspekt ist ....> sizeof(zahlen)/sizeof(zahlen[0])> schreiben. Denn dann ist der Ausdruck komplett davon unabhängig, aus> welchem Datentyp sich das Array zusammensetzt.
Hierzu doch noch eine abschließende Frage:
Hab ich das richtig verstanden, dass dann auch *ptr+2 und ptr[2]
äquivalent sind? Natürlich vorausgesetzt, das Array hat die Größe, oder
an der Stelle liegen andere sinnvolle Daten. Es muss ja nicht alles ein
Array sein, was ein Pointer ist =)
Peter II schrieb:> ich glaube aber hier muss man klammern>> *(ptr+2)
So isses.
Daher kann man übrigens auch statt "&array[2]" schreiben "array + 2".
Wenn man sich da als C-Programmierer erstmal dran gewöhnt hat, findet
man diese Schreibweise sogar übersichtlicher. ;-)
j. t. schrieb:> Hab ich das richtig verstanden, dass dann auch *ptr+2 und ptr[2]> äquivalent sind?
Jetzt bin ich verblüfft.
Gratuliere. Und das meine ich ganz ehrlich.
Tatsächlich ist in C die Operation a[i] genau so definiert, dass sie vom
Compiler zuallererst in die Form *(a+i) überführt werden muss, ehe sie
dann weiter bearbeitet wird.
Freut mich sehr, danke dir =)
Lob von dir ist glaub ich seltenes Gut.
Nachher kommt nochmal ne Frage zur Pointerarritmetik, nun muss ich
erstmal weg
MfG Chaos
j. t. schrieb:> Nachher kommt nochmal ne Frage zur Pointerarritmetik
so wie ich dich einschätze, lieferst du auch gleich die Antwort und die
Antwort wird richtig sein.
Denn im Grunde, wenn du die Äquivalenz von a[i] und *(a+i) rausgefunden
hast, hast du auch schon fast die komplette Pointerarithmetik
verstanden. Denn wenn du das nicht hättest, würdest du die Äquivalenz
nicht verstehen können.
Also ich versuche es mal zu formulieren:
ich habe testhalber einmal :
1
SortierArray=&SortierArray+2;
2
//SortierArray += 2
3
*SortierArray=123;
das kompiliert er fehlerfrei. Ich habe dann im ram ab Adresse 0x084c das
Sortierarray liegen, 64 00 65 00 66 00...... Sortierarray[0]-[7] sind
mit 100-107 initialisiert. Das stimmt also schonmal. Wenn ich das nun
laufen lasse, schreibt er mir an Adresse 0x084a 7b 00 also meine 123.
Sollte er das nicht eigentlich tun, wenn ich "SortierArray =
&SortierArray - 1" schreibe?
und was ich noch viel weniger verstehe, wenn ich aus dem obigen
folgendes mache:
1
SortierArray=&SortierArray;
2
SortierArray+=2
3
*SortierArray=123;
wirft er mir den Fehler:
Error 2 invalid operands to binary * (have 'int' and 'volatile
uint16_t *') C:\Users\Ichich\Documents\Atmel
Studio\6.2\MmMF-Musik_mit_Mehreren_Floppys\MmMF-Musik_mit_Mehreren_Flopp
ys\MmMF-Musik_mit_Mehreren_Floppys.c 237 5
MmMF-Musik_mit_Mehreren_Floppys
und stellt den Cursor aber auf die Zeile: *SortierArray = 123;....
Dann hab ich etwas von doppelten De/Referenzierungen gelesen, und es mit
SortierArray = &&SortierArray versucht, was aber auch einen Fehler
geworfen hat.
Als nächstes ist mir eingefallen, das es im ersten Versuch mit
SortierArray = &SortierArray[0] geklappt hatte, und aufgrund der "von
mir entdeckten" Äquivalenz hab ichs dann direkt mal mit SortierArray =
&*SortierArray + 2 versucht, und siehe da, es funktioniert.
Daraus ergeben sich nun meine 2 Fragen:
Was ist der Unterschied zwischen:
SortierArray = &SortierArray + 2; und SortierArray = &SortierArray;
SortierArray += 2;
???
und Frage2:
Was hat es denn mit diesen doppelten De/Referenzierungen nun auf sich?
Es sind ja im Endeffekt wie ich es verstehe, Zeiger auf Zeiger. Aber wie
setze ich sowas praktisch ein?
j. t. schrieb:> Also ich versuche es mal zu formulieren:>> ich habe testhalber einmal :> SortierArray = &SortierArray + 2;
Datentypen!
Ohne Kentniss der Datenytpen kann man dazu gar nichts sagen
j. t. schrieb:> und was ich noch viel weniger verstehe, wenn ich aus dem obigen> folgendes mache:>> SortierArray = &SortierArray;> SortierArray += 2> *SortierArray = 123;>> wirft er mir den Fehler:> Error 2 invalid operands to binary * (have 'int' and 'volatile> uint16_t *'
Fügt man die beiden fraglichen Zeilen zu einer zusammen, dann sieht das
so aus:
oohh mein Gott, das ist mir direkt peinlich. Nun macht er genau das, was
ich mir vorgestellt hab, was er machen wird.
Ich glaube, nun hab ich es tatsächlich verstanden.
Wie schon gesagt, nochmal vielen Dank euch, vor allem an KarlHeinz für
die Erläuterungen und Yalu fürs gefundene fehlende Semikolon, falls du
mal eins brauchst, du hast eins gut bei mir =)
P.S. also eins um ein fehlendes zu ersetzen, nicht ein Guthaben von
einem fehlendem :D
Yalu X. schrieb:> Fügt man die beiden fraglichen Zeilen zu einer zusammen, dann sieht das> so aus:> *SortierArray += 2*SortierArray = 123;
In welchem universum???
Es würde so aussehen: *(uint16_t*)(&SortierArray+2) = 123;
Das ist eine Speicherzugrifsverletzung.
SortierArray = &SortierArray;
1. bei | Type* var | ist der typ des ergebnis von | &var | vom type |
Type** | kurz ein pointer auf einen pointer auf ein element vom typ Type
Der pointer SortierArray zeigt nun auf den pointer von SortierArray,
weil der pointer SortierArray auf seine eigene adresse, &SortierArray,
gesetzt wurde. Hier ist der Fehler! Das & hat hier Nichts zu suchen,
lösche die zeile einfach.
> SortierArray += 2;> *SortierArray = 123;
Das ist korrekt. Es entspricht fast *(SortierArray+2) = 123;
Daniel A. schrieb:> Yalu X. schrieb:>> Fügt man die beiden fraglichen Zeilen zu einer zusammen, dann sieht das>> so aus:>> *SortierArray += 2*SortierArray = 123;>> In welchem universum???
In dem Universum, wo Semikola äußerst seltene Rohstoffe sind ;-)
> SortierArray = &SortierArray;>> 1. bei | Type* var | ist der typ des ergebnis von | &var | vom type |> Type** | kurz ein pointer auf einen pointer auf ein element vom typ Type
So ist es. Hätte j. t. den Compiler mit -Wall in den Meckermodus
versetzt, wäre zurecht eine Warnung wegen der impliziten Konvertierung
zwischen verschiedenen Pointer-Typen (von uint16_t** nach uint16_t*)
ausgegeben worden. Deswegen ist es fast immer von Vorteil, die Warnungen
mit -Wall zu aktivieren. In den seltenen Fällen, wo man tatsächlich
zwischen verschiedenen Pointer-Typen konvertieren will, kann man dem
Compiler diese Absicht damit kundtun, dass man einen expliziten Cast
verwendet:
1
SortierArray=(uint8_t*)&SortierArray;
Aber im vorliegenden Fall hat man diese Absicht definitiv nicht.
j. t. schrieb:> SortierArray = &&SortierArray versucht, was aber auch einen Fehler> geworfen hat.
Vergiss das && ! Das ist absolut nutzlos und gefärlich. Seine einzige
nützliche anwendung besteht bei gcc beim referenzieren eines labels...
1
voidx(){
2
intr=&&l;
3
gotor;
4
labell:
5
}
http://blog.llvm.org/2010/01/address-of-label-and-indirect-branches.html
Das ist aber leider nicht portabel!!
> Daraus ergeben sich nun meine 2 Fragen:>> Was ist der Unterschied zwischen:>> SortierArray = &SortierArray + 2; und SortierArray = &SortierArray;> SortierArray += 2;
SortierArray += 2; -> SortierArray = SortierArray + 2;
>>> und Frage2:> Was hat es denn mit diesen doppelten De/Referenzierungen nun auf sich?> Es sind ja im Endeffekt wie ich es verstehe, Zeiger auf Zeiger. Aber wie> setze ich sowas praktisch ein?
doppelte referenzierung = bitte vergessen
doppelte dereferenzierung -> arrays aus pointern
Zumindest funktioniert es nun mit:
SortierArray = &*SortierArray;
SortierArray += 2;
*SortierArray = 123;
nach SortierArray +2 sind in der Watch alle Werte um 4byte verschoben,
während sie sich im Speicher selbst nicht bewegt haben. Als nächstes
wird dann der alte SortierArray[2]Wert, der nun SortierArray[0] geworden
ist mit 123 dezimal überschrieben.
heben sich & und * nicht auf?
dann sollte auch SortierArray = SortierArray klappen, und damit versteh
ich den Einwand das es überflüssig ist. Das war auch nur als Versuch
gedacht, alternativ hätte ich hier ja auch auf ein anderes Array
verweisen können.
Wäre dann:
SortierArray = &*AnderesArray;
richtig, oder:
SortierArray = &AnderesArray;
?
Hab gerade das Studio ausgemacht und wollte schlafen gehen. müde.
bis morgen und gute nacht allerseits
j. t. schrieb:> heben sich & und * nicht auf?
Ja.¹
> dann sollte auch SortierArray = SortierArray klappen,
So ist es.
> und damit versteh ich den Einwand das es überflüssig ist.
Sehr schön :)
> Wäre dann:>> SortierArray = &*AnderesArray;>> richtig, oder:>> SortierArray = &AnderesArray;> ?
Das erstere. Oder du lässt die Zeile eben gleich ganz weg, da sie, wie
du ja bereits erkannt hast, sowieso überflüssig ist.
Und wie ich oben schon geschrieben habe: Aktivierst du die Warnungen mit
-Wall, wird dich der Compiler im zweiten (fehlerhaften) Fall darauf
hinweisen, dass da möglicherweise etwas nicht stimmt.
> bis morgen und gute nacht allerseits
dto.
——————————————
¹) Zumindest in C. In C++ gibt es Fälle, wo &*variable durchaus einen
Sinn ergibt.
Kannst du mir auch noch sagen, wo ich die Parameter einstelle? Das sind
ja eigentlich Parameter aus dem Makefile, aber im Atmel Studio komme ich
mit dem ja ganicht in Kontakt. Ich bin nun grad neulich mehr oder
weniger zufällig über die Einstellung für die Optmierungen gestoßen,
also vermute ich mal, irgendwo in deren Dunstkreis müsste ich auch die
anderen Parameter setzen können.?
Noch ein kleiner Tip:
Es gibt Leute, die werfen gerne die Begriffe "Adresse" und "Pointer"
durcheinander.
- Eine Adresse ist eine Zahl, die angibt, wo etwas im Speicher liegt.
- Ein Pointer ist ein Datentyp (bzw. eine Instanz dieses Datentypes),
der eine Adresse aufnehmen kann.
Bsp:
1
uint8_tmeinByte;
2
uint8_t*meinPointer1=&meinByte;
3
uint8_t*meinPointer2=meinPointer1;
meinByte belegt Speicher (1 Byte).
meinPointer1 belegt Speicher (beim AVR 2 Byte).
meinPointer2 belegt Speicher (beim AVR 2 Byte).
Der Ausdruck "&meinByte" ist eine Konstante (die vom Compiler+Linker
festgelegt wird) und belegt keinen Speicher!
Mit
1
2
uint8_t*meinPointer1=&meinByte;
weist Du dem Pointer "meinPointer1" die Konstante "&meinByte" zu, die
der Compiler+Linker später festlegen wird.
Mit
1
2
uint8_t*meinPointer2=meinPointer1;
weist Du dem Pointer "meinPointer2" den Inhalt des Pointers
"meinPointer1" zu.
Jau danke dir Bronco,
aber das ist mir inzwischen klar geworden, wobei ich sagen muss, dass
das jedoch genau mein Problem war, zu verstehen was da passiert. Ich bin
auch laufend durcheinandergekommen, ob nun gerade eine Adresse oder ein
Wert gemeint war. Aber nun ist das klar.
Wobei ich bald schon die nächste Frage hab, dafür werd ich aber ein
neues Thema aufmachen. Die wird dann eher allgemein
Programmierungsbezogen sein, als direkt auf C.