Forum: Compiler & IDEs array Zeigerarithmetik


von Lars H. (larsfcfa)


Lesenswert?

Hallo,

die Pointer bereiten mir etwas Probleme (.

Folgende Deklaration:
1
const __flash uint8_t (Data_init)[3][5]=
2
{
3
  {1,1,2,2,5};
4
  {2,2,2,2,5};
5
  {2,1,2,4,5};
6
};
7
uint8_t Data[5];
Nun sollen die Teilelemente einem neuen Array (1D) zugewiesen werden
1
for (i=0; i<3; i++){
2
    *Data= &Data_init[i][0];
3
    ....
4
}

Ich hatte es so verstanden, dass ein Zeiger auf das Element 0 dem 
Pointer auf das neue Array zugewiesen werden muss.
Klappt so aber nicht. Wo ist mein Gedankenfehler?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Lars H. schrieb:
> const __flash uint8_t (Data_init)[3][5]=

Wozu die runden Klammern?

Lars H. schrieb:
> Ich hatte es so verstanden, dass ein Zeiger auf das Element 0 dem
> Pointer auf das neue Array zugewiesen werden muss.

Möchtest du Pointer übernehmen oder nur uint8_t-Zahlen? Dein 
"Data"-Array enthält einfach nur uint8_t Elemente. Die kannst du einfach 
direkt zuweisen:
1
for (i=0; i<3; i++){
2
    Data[i] = Data_init[i][0];
3
}

Das kopiert jeweils das 0. Element der Unter-Arrays nach Data. Keine 
Zeiger-Arithmetik, kein explizites Dereferenzieren oder Adressen nehmen. 
Data ist allerdings zu groß, die letzten beiden Elemente werden so nicht 
beschrieben. Oder was möchtest du erreichen?

: Bearbeitet durch User
von Lars H. (larsfcfa)


Lesenswert?

Runde Klammern können weg.

Ich möchte aus dem const-Array (__flash) wahlweise jeweils 5 
uint8_t-Werte in mein Variablen-Array übernehmen, also immer eine ganze 
Zeile (je 5 Elemente).
Damit sollte die Dimension eigentlich passen.

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

In C können keine Arrays mit = zugewiesen werden.
Dies geht nur mit integralen Typebn oder structs.

*Data ist mit Data[0] identisch.
(Der Compiler macht intern aus Data[i] ein *(Data+i) )

Daher bleibt dir nur die Schleife oder die Funktion memcpy (oder eine 
spezielle Anpassung für flash)

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Lars H. schrieb:
> jeweils 5
> uint8_t-Werte

Und warum zählst du dann bis 3?

Du möchtest also das innere Array kopieren. So kopierst du das 0. innere 
Array:
1
for (size_t i=0; i<5; ++i){
2
    Data[i] = Data_init[0][i];
3
}
Oder einfacher mit memcpy:
1
memcpy (Data, Data_init[0], sizeof(Data));

Oder wenn du doch was mit Zeigern machen möchtest, kannst du Data als 
Zeiger deklarieren und auf die Zeile zeigen lassen. Dadurch werden keine 
Daten kopiert, sondern Data verweist einfach auf das existierende 
Unter-Array:
1
const __flash uint8_t* Data;
2
3
Data = Data_init[0];

So kannst du aber natürlich die Inhalte nicht modifizieren, da keine 
Kopie im RAM angelegt wird.

Die Semikola in der Initialisierung von Data_init sind übrigens auch 
verkehrt...

: Bearbeitet durch User
von Lars H. (larsfcfa)


Lesenswert?

Hallo,

also noch mal kurz mein Verständnis:
-erster Index Zeilen, 2. Index Spalten
-da eine const im Flash nicht beschrieben werden kann, muss memcpy 
genommen werden
-
1
// 4 Zeilen 5 Spalten
2
int Matrix[4][5] = { {10,20,30,40,50},
3
                     {15,25,35,45,55},
4
                     {20,30,40,50,60},
5
                     {25,35,45,55,65}};
6
uint8_t Data[5];
7
memcpy (Data, Matrix[1], sizeof(Data))

Ergibt in Data:  15  0 25  0 35
sollte ergeben:  15 25 35 45 55
Offenbar wird zwischen 2 Elmenten immer eine 0 mit eingefügt- warum?

alternativ
1
int Matrix[4][5] = { {10,20,30,40,50},
2
                     {15,25,35,45,55},
3
                     {20,30,40,50,60},
4
                     {25,35,45,55,65}};
5
uint8_t* DataZ;
6
DataZ = Data_init[1];
ergibt nur einen Pointer, ich möchte aber die Werte ins neue Array 
übertragen (durch verschieben des Zeigers auf Zeile 1 von Matrix)

Wo ist mein Gedankenfehler?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Lars H. schrieb:
> -erster Index Zeilen, 2. Index Spalten

Das ist nicht festgelegt, das kannst du auch anders herum 
interpretieren. Bei dieser Schreibweise stimmt es aber genau so:

Lars H. schrieb:
> int Matrix[4][5] = { {10,20,30,40,50},
>                      {15,25,35,45,55},
>                      {20,30,40,50,60},
>                      {25,35,45,55,65}};

Lars H. schrieb:
> Offenbar wird zwischen 2 Elmenten immer eine 0 mit eingefügt- warum?

Weil die Elemente der Matrix "int" sind, aber "Data" vom Typ uint8_t 
ist! memcpy prüft sowas nicht. Klassische Falle in C, und ist auch nicht 
wohldefiniert, d.h. am PC wäre das Ergebnis anders. Wenn du C++ 
verwendest kannst du das mit std::copy sauberer & sicherer machen:
1
std::copy (std::begin(Data_init[1]), std::end(Data_init[1]), std::begin(Data));

Wenn "Data_init" im Flash ist via __flash, geht memcpy wohl nicht. Wenn 
du statt __flash PROGMEM verwendest kannst du memcpy_P nehmen.

: Bearbeitet durch User
von N. M. (mani)


Lesenswert?

Lars H. schrieb:
> Offenbar wird zwischen 2 Elmenten immer eine 0 mit eingefügt- warum?

Du kopierst ein int auf uint8_t.
Nimm die For Schleife und caste den int auf uint8_t (wenn dir bewusst 
ist was bei negativen ints passiert).

Edit: zu langsam.

: Bearbeitet durch User
von Lars H. (larsfcfa)


Lesenswert?

Ja stimmt, danke (dummer Fehler...)

Wie ist es aber mit der 2. Variante, wo ich die Daten gar nicht 
"sinnlos" kopieren muss, sondern nur den Zeiger umsetze.

-geht das mit einer const so überhaupt?
-wie müsste ich das genau machen, wenn Matrix kein const wäre?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Lars H. schrieb:
> -geht das mit einer const so überhaupt?

Ja, der Zeiger muss halt auch als "const" markiert werden, damit du 
darüber nicht versehentlich versuchst zu schreiben, also eben so:
1
const uint8_t* Data;
2
Data = Matrix[1];

> -wie müsste ich das genau machen, wenn Matrix kein const wäre?

Genauso, nur ohne const:
1
uint8_t* Data;
2
Data = Matrix[1];

von Dirk B. (dirkb2)


Lesenswert?

Lars H. schrieb:
> Wie ist es aber mit der 2. Variante, wo ich die Daten gar nicht
> "sinnlos" kopieren muss, sondern nur den Zeiger umsetze.
>
> -geht das mit einer const so überhaupt?

Ja.
const  char * Data; heißt "declare Data as pointer to const char"

Der Zeiger selber ist nicht const.

Soll der Zeiger const sein, wäre dass dann char * const  Data; "declare 
Data as const pointer to char"

Kann man auf https://cdecl.org ausprobieren (kennt nur kein stdint.h)

Das const schützt nicht vorm überschreiben.
Der Compiler kann anders optimieren und gibt Warnungen, wenn schreiben 
beim compilieren versucht wird.

von Lars H. (larsfcfa)


Lesenswert?

Danke!

Ich habe gerade gemerkt, ich brauche in diesem Fall memcpy_P (wegen 
_flash)

Allerdings ist das als Funktionsaufruf auch nicht effizient, gibt es das 
auch inline?

: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Lars H. schrieb:
> Allerdings ist das als Funktionsaufrauf auch nicht effizient, gibt es
> das auch inline?

Der Compiler kann das falls sinnvoll automatisch inlinen. Um die paar 
Takte muss man sich erstmal keine gesteigerten Sorgen machen...

von Oliver S. (oliverso)


Lesenswert?

Niklas G. schrieb:
> Der Compiler kann das falls sinnvoll automatisch inlinen.

Das ist eine Assembler-Funktion, da wird der nicht allzu viel machen 
können. Im Gegensatz zu memcpy hat der die _P-Version m.W. nicht 
built-in.

Niklas G. schrieb:
> Um die paar
> Takte muss man sich erstmal keine gesteigerten Sorgen machen...

So ist es.

Oliver

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.