Forum: Compiler & IDEs sizeof ergibt 2 für array mit 9 16bit werten


von J. T. (chaoskind)


Lesenswert?

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
2
void NaechsterTon (volatile uint16_t SortierArray[], volatile uint16_t Unsortiert[])  
3
{
4
  // Bit0 auf 1 = es wurde etwas getauscht, Bit0 auf 0 = es wurde nichts getauscht
5
  volatile uint8_t SortierFlags = 0;  
6
  //Zähler, wieviele Stellen müssen in der inneren Schleife pro Durchlauf noch verglichen werden
7
  volatile uint8_t n = 0;        
8
  //Zähler wieviele Stellen in der inneren Schleife im aktuellen Durchlauf schon verglichen wurden
9
  volatile uint8_t i = 0;        
10
11
  /*hier werden die Elemente unsortiert gespeichert, um Ende nachprüfen zu können, von welchem
12
   *Urprungselement der kleinste Wert kam*/
13
  
14
  Unsortiert = SortierArray;      
15
          
16
  n = sizeof (SortierArray)/2

und hier wird die Funktion aufgerufen
1
//Uebergabearray mit Spieldauern beladen
2
      UebergabeArray[0] = Melodie0.Gesamtdauer;
3
      UebergabeArray[1] = Melodie1.Gesamtdauer;
4
      UebergabeArray[2] = Melodie2.Gesamtdauer;
5
      UebergabeArray[3] = Melodie3.Gesamtdauer;      
6
      UebergabeArray[4] = Melodie4.Gesamtdauer;
7
      UebergabeArray[5] = Melodie5.Gesamtdauer;
8
      UebergabeArray[6] = Melodie6.Gesamtdauer;
9
      UebergabeArray[7] = Melodie7.Gesamtdauer;
10
      UebergabeArray[8] = 0;
11
      
12
      //Sortierfunktion aufrufen, um die Gesamtdauer mit dem kleinesten Wert rauszusuchen,
13
      NaechsterTon(UebergabeArray, UebergabeArray);

: Verschoben durch User
von Schmiedl (Gast)


Lesenswert?

Ein Array wird als Pointer übergeben.
Auf einem AVR ist sizeof(pointer) == 2

von J. T. (chaoskind)


Lesenswert?

Und wie kann ich dann die Anzahl der Elemente in einem Array bestimmen?

von Teo D. (teoderix)


Lesenswert?

n = sizeof (&SortierArray)/2

von J. T. (chaoskind)


Lesenswert?

n = sizeof (&SortierArray)/2 damit wird n = 1....

von Jasch (Gast)


Lesenswert?

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.

von Leo B. (luigi)


Lesenswert?

sizeof wird zur kompilezeit ausgewertet... wenn du deiner Funktion nicht 
übergibst wie lang das array ist, wird sie es nicht herausfinden können.

von Bastler (Gast)


Lesenswert?

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.

von Fritz (Gast)


Lesenswert?

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.

von J. T. (chaoskind)


Lesenswert?

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

von J. T. (chaoskind)


Lesenswert?

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

von Bastler (Gast)


Lesenswert?

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.

von J. T. (chaoskind)


Lesenswert?

und wie bekomme ich es dann hin, das da 9 und nicht 1 steht?
1
/* array9.c */
2
#include <stdio.h>
3
#include <stdlib.h>
4
5
int main(void) {
6
   int zahlen[] = {3,6,3,5,6,3,8,9,4,2,7,8,9,1,2,4,5};
7
8
   printf("Anz. Elemente : %d\n", sizeof(zahlen) / sizeof(int));
9
   return EXIT_SUCCESS;
10
}

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.

Muss ich dem Array quasi ein "Abschlusselement" mitgeben? und dann 
Abzählen das wievielte es ist?

: Bearbeitet durch User
von Marius S. (lupin) Benutzerseite


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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
void sub(int *a);
2
void sub(int a[]);
3
void sub(int a[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.

von J. T. (chaoskind)


Lesenswert?

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_t i = 0;
2
uint16_t array[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.

: Bearbeitet durch User
von Walter (Gast)


Lesenswert?

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

von Rolf M. (rmagnus)


Lesenswert?

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?

von Martin S. (sirnails)


Lesenswert?

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.

von Bastler (Gast)


Lesenswert?

Einfach so machen:
1
 void NaechsterTon (volatile uint16_t SortierArray[], volatile uint16_t unsortiert, uint16_t anzahl);
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".

von J. T. (chaoskind)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

j. t. schrieb:
> und wie bekomme ich es dann hin, das da 9 und nicht 1 steht?
>
>
1
/* array9.c */
2
> #include <stdio.h>
3
> #include <stdlib.h>
4
> 
5
> int main(void) {
6
>    int zahlen[] = {3,6,3,5,6,3,8,9,4,2,7,8,9,1,2,4,5};
7
> 
8
>    printf("Anz. Elemente : %d\n", sizeof(zahlen) / sizeof(int));
9
>    return EXIT_SUCCESS;
10
> }
11
>
>
> so macht man das laut:
> http://openbook.galileocomputing.de/c_von_a_bis_z/011_c_arrays_004.htm
>

Das ist auch richtig so und da wird auch 17 an der Ausgabe erscheinen.

Aber du solltest den Ausdruck besser als
1
     .... sizeof(zahlen)/sizeof(*zahlen)
oder wenn dir die Dereferenzierung suspekt ist
1
     .... sizeof(zahlen)/sizeof(zahlen[0])
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
void foo( 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
void bar()
2
{
3
  int b;
4
5
  foo( &b );
6
}
oder ob die Adresse zu einem Array gehört
1
void baz()
2
{
3
  int values[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.

von J. T. (chaoskind)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von J. T. (chaoskind)


Lesenswert?

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;

von Daniel A. (daniel-a)


Lesenswert?

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_t x = 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

von Karl H. (kbuchegg)


Lesenswert?

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_t foo1;
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.

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

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!

von J. T. (chaoskind)


Lesenswert?

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"

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

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.

von J. T. (chaoskind)


Lesenswert?

Ist für nächstes mal notiert =)

von J. T. (chaoskind)


Lesenswert?

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

von Peter II (Gast)


Lesenswert?

j. t. schrieb:
> Hab ich das richtig verstanden, dass dann auch *ptr+2 und ptr[2]
> äquivalent sind?

ja,
wenn man will kann man auch 2[ptr] schreiben.

von J. T. (chaoskind)


Lesenswert?

jau danke =), nun sind alle Klarheiten beseitigt

von Peter II (Gast)


Lesenswert?

ich glaube aber hier muss man klammern

*(ptr+2)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von J. T. (chaoskind)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von J. T. (chaoskind)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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

von J. T. (chaoskind)


Lesenswert?

ahja natürlich:

void NaechsterTon (volatile uint16_t SortierArray[], volatile uint16_t 
Unsortiert[])

von Yalu X. (yalu) (Moderator)


Lesenswert?

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:
1
  SortierArray += 2*SortierArray = 123;

Merkst du etwas? ;-)

von J. T. (chaoskind)


Lesenswert?

AAAAHHHHHHHHHH danke :D

ich hasse diese fiesen Semikolonisten. Die übersieht man so leicht. Mals 
sehen was er mit macht.

von J. T. (chaoskind)


Lesenswert?

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

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

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;

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Daniel A. (daniel-a)


Lesenswert?

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
void x(){
2
int r = &&l;
3
goto r;
4
label l:
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
1
const char* x[] = {
2
 "ab",
3
 "cdef"
4
};
5
6
**x; // ist x[0][0] ist 'a'

von J. T. (chaoskind)


Lesenswert?

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.

: Bearbeitet durch User
von J. T. (chaoskind)


Lesenswert?

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

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

Am besten ist es eh, mindestens die Parameter
1
-Wall -Wexta -std=c99 -pedantic
anzugeben.

von J. T. (chaoskind)


Lesenswert?

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

von Yalu X. (yalu) (Moderator)


Lesenswert?

Schau mal hier:

  http://www.atmel.com/webdoc/atmelstudio/atmelstudio.Projects.GCC_projectOptions.html
  http://www.atmel.com/webdoc/atmelstudio/atmelstudio.Projects.GCC_projectOptions.CompilerOptions.html

Rolf Magnus schrieb:
> -Wall -Wexta -std=c99 -pedantic

Kleine Korrektur: Es muss -Wextra heißen.

Anstelle von -std=c99 kann man auch gleich -std=c11 nehmen, dann ist man
up-to-date :)

von Bronco (Gast)


Lesenswert?

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_t  meinByte;   
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.

von J. T. (chaoskind)


Lesenswert?

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.

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.