Forum: Mikrocontroller und Digitale Elektronik C Array copy by reference


von hambi (Gast)


Lesenswert?

Ich habe ein uint8_t array mit einer Grösse von 16 Byte. Ich möchte nun 
ein zweites Array initialisieren, welches auf die letzen 8 Byte zeigt, 
ohne die Information der Grösse zu verlieren. Also anstatt

uint8_t grosses_array[16];
uint8_t* kleines_array = grosses_array+8;

will ich etwas in der Art:

uint8_t grosses_array[16];
uint8_t kleines_array[8] = grosses_array+8;

so dass sizeof(kleines_array) == 8 ist. Ist das in C möglich, oder 
bleibt mir nichts anders übrig als das ganze Array entweder zu kopieren, 
oder die Grösse in einer separaten Variable zu speichern?

von Johnny B. (johnnyb)


Lesenswert?

hambi schrieb:
> oder die Grösse in einer separaten Variable zu speichern

Entweder so oder mittels #define (Konstante).

von Einer K. (Gast)


Lesenswert?

memcpy() ?

von hambi (Gast)


Lesenswert?

Johnny B. schrieb:
> hambi schrieb:
>> oder die Grösse in einer separaten Variable zu speichern
>
> Entweder so oder mittels #define (Konstante).

Besten Dank für deine Antwort, werde ich wohl so machen.

Arduino Fanboy D. schrieb:
> memcpy() ?

Täusche ich mich oder kopiert das die Daten? Ich will nicht für die 
gleiche Information zweifachen Speicher belegen, deshalb möchte ich es 
als Referenz haben.

von Einer K. (Gast)


Lesenswert?

hambi schrieb:
> deshalb möchte ich es
> als Referenz haben.

Referenzen gibt es in C, in der Form, nicht.
Du meinst eher Zeiger.

von Bernd K. (prof7bit)


Lesenswert?

Du könntest ein Union machen mit dem großen Array und einenm struct aus 
zwei kleinen Arrays. So ungefähr:
1
union {
2
    uint8_t gross[16];
3
    struct {
4
        uint8_t dummy[8];
5
        uint8_t klein[8];
6
    };
7
} foo;

von Oliver S. (oliverso)


Lesenswert?

Da Arrays in C ja sowieso keine Größeninfo besitzen, ist die Frage, was 
du genau bezweckst.

Aunsonsten werfe ich mal eine union in den Ring.

Oliver

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

In C++ könnte man eine Referenz umcasten. Ist aber ziemlich hässlich.
1
#include <iostream>
2
#include <cstdint>
3
#include <algorithm>
4
#include <iterator>
5
6
int main () {
7
  uint8_t grosser_hai [16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
8
  
9
  uint8_t (&kleiner_hai) [8] = reinterpret_cast<uint8_t (&) [8]> (grosser_hai[8]);
10
  
11
  std::cout << sizeof (kleiner_hai) << std::endl;
12
  std::copy (std::begin (kleiner_hai), std::end (kleiner_hai), std::ostream_iterator<int> { std::cout, ", " });
13
  std::cout << std::endl;
14
  
15
}

von Bernd K. (prof7bit)


Lesenswert?

Oliver S. schrieb:
> Da Arrays in C ja sowieso keine Größeninfo besitzen,

Zur Compilezeit ist die noch da, deshalb liefert da sizeof() auch noch 
was anderes als bei einem als normalen Pointer deklarierten Pointer.

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

... noch eine variante!

union {
   uint8_t g[16];
   uint8_t kl[8];
   uint8_t kh[8];
} array;


mt

von Bernd K. (prof7bit)


Lesenswert?

Apollo M. schrieb:
> ... noch eine variante!
>
> union {
>    uint8_t g[16];
>    uint8_t kl[8];
>    uint8_t kh[8];
> } array;
>
> mt

Die letzten beiden sind identisch. Alle union-member fangen immer bei 
offset 0 an.

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Bernd K. schrieb:
> Die letzten beiden sind identisch. Alle union-member fangen immer bei
> offset 0 an.

ok, stimmt - guter hinweis. doch wieder nicht richtig nachgedacht.


mt

von Oliver S. (oliverso)


Lesenswert?

Da braucht’s noch ein struct, dann klappt das.

Oliver

von zitter_ned_aso (Gast)


Lesenswert?

hambi schrieb:
> Ich will nicht für die
> gleiche Information zweifachen Speicher belegen


Zeiger benutzen?
1
 uint8_t arr[16]={......};
2
3
 uint8_t* arr_last_bits[8] = {arr+8, arr+9, arr+10, .....};
und die Laenge dann mit
1
size_t len_arr_last_bits = sizeof(arr_last_bits)/sizeof(arr_last_bits[0])

ermitteln

von zitter_ned_aso (Gast)


Lesenswert?

Oliver S. schrieb:
> Da Arrays in C ja sowieso keine Größeninfo besitzen


sizeof(arr)/sizeof(arr[0]) ?

von DPA (Gast)


Lesenswert?

Noch einige interessante Verwendungen von Pointern auf Arrays & von 
multidimensionalen Arrays als Funktionsargumenten mit variabler 
dimension:
1
#include <stdint.h>
2
#include <stdio.h>
3
4
uint8_t grosses_array[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
5
uint8_t (*const kleines_array)[8] = (uint8_t(*const)[8])&grosses_array[8];
6
7
void example2d(int n, int m, uint8_t e[n][m]){
8
  // AHTUNG: kein sizeof auf e anwenden!
9
  for(int i=0; i<n; i++)
10
    for(int j=0; j<m; j++)
11
      printf("e[%d][%d] = %d\n",i,j,(int)e[i][j]);
12
}
13
14
void example(uint8_t e[8]){
15
  // AHTUNG: kein sizeof auf e anwenden!
16
  for(int i=0; i<8; i++)
17
    printf("e[%d] = %d\n",i,(int)e[i]);
18
}
19
20
int main(){
21
  printf("sizeof(*kleines_array) = %zu\n", sizeof(kleines_array));
22
  puts("");
23
  for(int i=0; i<8; i++)
24
    printf("(*kleines_array)[%d] = %d\n",i,(int)(*kleines_array)[i]);
25
  puts("");
26
  example(grosses_array+8);
27
  puts("");
28
  example2d(2,8,(uint8_t(*)[8])grosses_array);
29
  puts("");
30
  example2d(4,4,(uint8_t(*)[4])grosses_array);
31
  puts("");
32
  example2d(8,2,(uint8_t(*)[2])grosses_array);
33
  puts("");
34
  uint8_t test[][2] = {{1,2},{3,4},{5,6}};
35
  example2d(sizeof(test)/sizeof(*test),sizeof(*test)/sizeof(**test),test);
36
}
https://repl.it/repls/MeanLumberingWorker

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

hambi schrieb:
> so dass sizeof(kleines_array) == 8 ist.

Ist die Größe zur Compile-Time bekannt? Oder kann der Offset zum großen 
Array erst at Runtime bestimmt werden?

Wenn bekannt, dann kannst Du das über die obige Struct in der Union 
lösen.

Wenn nicht, musst Du die Größeninformation in einer separaten Variablen 
mitführen.

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

DPA schrieb:
> uint8_t (*const kleines_array)[8] =
> (uint8_t(*const)[8])&grosses_array[8];

... kannst du hierzu mal etwas erklärendes schreiben?


mt

von Oliver S. (oliverso)


Lesenswert?

Oliver S. schrieb:
> ist die
> Frage, was
> du genau bezweckst.

Und ?

Oliver

von DPA (Gast)


Lesenswert?

Apollo M. schrieb:
> DPA schrieb:
>> uint8_t (*const kleines_array)[8] =
>> (uint8_t(*const)[8])&grosses_array[8];
>
> ... kannst du hierzu mal etwas erklärendes schreiben?

uint8_t (*const kleines_array)[8] definiert einen konstanten Pointer auf 
ein uint8_t Array mit 8 elementen. Ein Array hat eine Adresse, wo die 
Daten stehen, aber diese muss nicht zwangsläufig irgendwo gespeichert 
sein, und kann nicht geändert werden. Compiler vermeiden eigentlich 
immer eine unnötige dereferenzierung, wenn man einen Pointer auf ein 
Array nutzt, um auf das Array zuzugreifen, indem sie im Pointer die 
Adresse des Arrays speichern, statt irgendwo die Adresse 
zwischenzuspeichern, und dann darauf zu zeigen. Mit anderen Worten, der 
Compiler behandelt einen Pointer auf ein Array vergleichbar zu einem 
Pointer auf dessen Daten. Desshalb ist bei einem Array wie grosses_array 
in der regel (void*)grosses_array==(void*)&grosses_array; Deshalb kann 
ich ein uint8_t[] oder uint8_t* nach uint8_t(*)[] Casten. Ist aber 
vermutlich hochgradig UB. Ich hol mir also einen Pointer auf das Array 
startend bei Element 8, und kaste es in ein Pointer auf ein Array mit 8 
Elementen, was implementationsbedingt eigentlich überall zufällig geht, 
weil es in beiden fällen pracktischerweise gleich dereferenziert wird.

In meinem Beispiel sind eigentlich alle Zeilen mit Casts UB, glaub ich. 
Der Rest ist aber 100% OK.

von Herbert P. (Gast)


Lesenswert?

Jaaa, aber...
1
...
2
uint8_t *kleines_array = &grosses_array[8];
3
...
4
    printf("(*kleines_array)[%d] = %d\n", i, kleines_array[i]);
5
...

tut irgendwie das selbe, ohne so bedrohlich zu wirken. :)

Nachdem kleines_array so oder so ein Pointer ist, liefert der Ausdruck 
sizeof(kleines_array) immer nur den Wert 4, in der Fassung von DPA 
genauso wie in meiner.

Gruß
Herby

von Oliver S. (oliverso)


Lesenswert?

DPA schrieb:
> uint8_t (*const kleines_array)[8] definiert einen konstanten Pointer auf
> ein uint8_t Array mit 8 elementen.

Das definiert ein Array von Pointern auf const int, und ist damit 
vermutlich nicht das, was gewünscht war.

Aber da der TO sich nicht dazu auslässt, was er überhaupt vorhat, weiß 
sowieso niemand, was gewünscht ist.

Oliver

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


Lesenswert?

DPA schrieb:
> In meinem Beispiel sind eigentlich alle Zeilen mit Casts UB, glaub ich.
> Der Rest ist aber 100% OK.

Wenn ich die ISO-Norm nicht missverstehe, ist dein Code völlig korrekt
und legal, denn:

Das Hin- und Her-Casten zwischen verschiedenen Pointer-Typen wird
nirgends verboten, Einschränkungen gibt es lediglich bei direkter
Zuweisung ohne Cast-Operator.

Der Ausdruck
1
(*kleines_array)[i]

mit dem um die Ecke herum auf ein Element von grosses_array zugriffen
wird, ist ebenfalls in Ordnung, weil dieser Ausdruck und das Element,
auf das er zugreift, denselben Typ haben (nämlich uint8_t), womit die
"strict aliasing rule" (ISO-Norm Abschnitt 6.5, Absatz 7) befolgt wird.


Herbert P. schrieb:
> Jaaa, aber...
> ...
> uint8_t *kleines_array = &grosses_array[8];
> ...
>     printf("(*kleines_array)[%d] = %d\n", i, kleines_array[i]);
> ...
>
> tut irgendwie das selbe, ohne so bedrohlich zu wirken. :)

Nicht ganz: Du kannst sizeof nicht verwenden, um die Größe des kleinen
Arrays zu ermitteln.

Der TE möchte aber,

hambi schrieb:
> dass sizeof(kleines_array) == 8 ist.

Das ist zwar bei bei DPAs Vorschlag auch nicht der Fall, aber zumindest
gilt dort

1
  sizeof *kleines_array == 8

Man könnte diesen (und andere) Schönheitsfehler noch mit

1
#define kleines_array (*kleines_array)

beheben, muss dabei aber beachten, dass Makros einen größeren Scope als
Variablennamen haben.

: Bearbeitet durch Moderator
von Apollo M. (Firma: @home) (majortom)


Lesenswert?

DPA schrieb:
> void example(uint8_t e[8]){
>   // AHTUNG: kein sizeof auf e anwenden!
>   for(int i=0; i<8; i++)
>     printf("e[%d] = %d\n",i,(int)e[i]);
> }

... ich denke, das ist "böse"!

weil unnötigerweise hier ein array im daten stack angelegt wird und 
nicht wie gewünscht als referenz.


mt

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Herbert P. schrieb:
> sizeof(kleines_array) immer nur den Wert 4, in der Fassung von DPA
> genauso wie in meiner.

... das wäre ja auch logisch, weil das die grösse des pointer's ist - 
und auch noch abhängig von der zielplattform.


mt

von Bernd K. (prof7bit)


Lesenswert?

Apollo M. schrieb:
> DPA schrieb:
>> void example(uint8_t e[8]){
>>   // AHTUNG: kein sizeof auf e anwenden!
>>   for(int i=0; i<8; i++)
>>     printf("e[%d] = %d\n",i,(int)e[i]);
>> }
>
> ... ich denke, das ist "böse"!
>
> weil unnötigerweise hier ein array im daten stack angelegt wird und
> nicht wie gewünscht als referenz.

Legt er hier überhaupt eins an oder ist das nur ne verschrobene Art nen 
Pointer hinzuschreiben?

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

DPA schrieb:
> und kaste es in ein Pointer auf ein Array mit 8
> Elementen,

... das ist für mich nur zum schein und auch kein voodo.
ob da 8, 128 oder 0 steht ist dem compiler egal, nur der daten typ des 
array's ist interessant.

daher ist die ganze anweisung für mich murks, weil sie nichts bewirkt, 
was ich nicht auch viel einfacher realisieren kann.

auch diese idee macht für mich nicht richtig sinn

void example2d(int n, int m, uint8_t e[n][m]){...

und auch das gleiche wie

void example2d(int n, int m) {
   uint8_t e[n][m]);
   ...
}


mt

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Bernd K. schrieb:
> Legt er hier überhaupt eins an oder ist das nur ne verschrobene Art nen
> Pointer hinzuschreiben?

nix pointer voodo!

... diese anweisung erzeugt ein speicher objekt und wird komplett 
initialisert mit einer kopie des array's.


mt

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Apollo M. schrieb:
> ... diese anweisung erzeugt ein speicher objekt und wird komplett
> initialisert mit einer kopie des array's.

Welcher C-Standard soll das sein?

von Daniel A. (daniel-a)


Lesenswert?

Apollo M. schrieb:
> Bernd K. schrieb:
>> Legt er hier überhaupt eins an oder ist das nur ne verschrobene Art nen
>> Pointer hinzuschreiben?
>
> nix pointer voodo!
>
> ... diese anweisung erzeugt ein speicher objekt und wird komplett
> initialisert mit einer kopie des array's.

Blödsinn. Wäre dass tatsächlich der Fall, würde das hier nicht gehen:
1
#include <stdio.h>
2
3
void test(int n, int m, int x[n][m]){
4
  int k = 1;
5
  for(int i=0; i<n; i++)
6
    for(int j=0; j<m; j++)
7
      x[i][j] = k++;
8
}
9
10
int main(){
11
  int x[3][4] = {0};
12
  test(3,4,x);
13
  for(int i=0; i<3; i++)
14
    for(int j=0; j<4; j++)
15
      printf("x[%d][%d] = %d\n", i, j, x[i][j]);
16
}

Apollo M. schrieb:
> auch diese idee macht für mich nicht richtig sinn
>
> void example2d(int n, int m, uint8_t e[n][m]){...

Ist doch einfacher uint8_t e[n][m] zu verwenden und e[i][j] anzugeben, 
als uint8_t* e; und dann jedesmal mit e[i+j*n] das Feld manuell 
berechnen zu müssen.

von Daniel A. (daniel-a)


Lesenswert?

Oliver S. schrieb:
> DPA schrieb:
>> uint8_t (*const kleines_array)[8] definiert einen konstanten Pointer auf
>> ein uint8_t Array mit 8 elementen.
>
> Das definiert ein Array von Pointern auf const int, und ist damit
> vermutlich nicht das, was gewünscht war.

Nö, ein Array von Pointern auf const int wäre "const int* 
kleines_array[8]".

von Vincent H. (vinci)


Lesenswert?

Dieser Thread schreit mikrocontroller.net von oben bis unten.

Der Threadersteller, der mit großer Wahrscheinlichkeit den Unterschied 
zwischen Stack und globalen Variablen nicht kennt, macht sich Sorgen um 
8 Byte "Speicherverbrauch".

Dann berät man in zwei dutzend Antworten darüber wie man das Array 
umcasten kann ohne ein einziges Mal zu erwähnen wie sinnlos das Ganze 
ist, wenn der entstehende Pointer auf jeder halbwegs modernen Platform 
bereits die Hälfte der neuen Array-Größe in Anspruch nehmen würde.

Und damit der Threadersteller auch ja nichts lernt voted man sinnvolle 
Antworten wie "memcpy" oder "C Arrays besitzen keine Längeninfo" noch 
schön runter...

Grandios :)

von Oliver S. (oliverso)


Lesenswert?

Daniel A. schrieb:
> Oliver S. schrieb:
>> DPA schrieb:
>>> uint8_t (*const kleines_array)[8] definiert einen konstanten Pointer auf
>>> ein uint8_t Array mit 8 elementen.
>>
>> Das definiert ein Array von Pointern auf const int, und ist damit
>> vermutlich nicht das, was gewünscht war.
>
> Nö, ein Array von Pointern auf const int wäre "const int*
> kleines_array[8]".

Ja, war mein Fehler.

Oliver

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Daniel A. schrieb:
> Ist doch einfacher uint8_t e[n][m] zu verwenden und e[i][j] anzugeben,
> als uint8_t* e; und dann jedesmal mit e[i+j*n] das Feld manuell
> berechnen zu müssen.

ja sieht besser aus, ich muss wohl nochmals in meine denkstube gehen - 
bin ganz wirr :/)


mt

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Daniel A. schrieb:
> Blödsinn. Wäre dass tatsächlich der Fall, würde das hier nicht gehen:

joh, ich habe wohl  unsinn verbockt!


mt

von leo (Gast)


Lesenswert?

Oliver S. schrieb:
> Daniel A. schrieb:
>> Oliver S. schrieb:
>>> DPA schrieb:
>>>> uint8_t (*const kleines_array)[8] definiert einen konstanten Pointer auf
>>>> ein uint8_t Array mit 8 elementen.
>>>
>>> Das definiert ein Array von Pointern auf const int, und ist damit
>>> vermutlich nicht das, was gewünscht war.
>>
>> Nö, ein Array von Pointern auf const int wäre "const int*
>> kleines_array[8]".

BTW weil solche Deklarationen oft falsch sind, cdecl hilft:
1
$ cdecl explain 'int (*const kleines_array)[8]'
2
declare kleines_array as const pointer to array 8 of int
3
4
$ cdecl explain const int* kleines_array[8]
5
declare kleines_array as array 8 of pointer to const int
6
7
$ cdecl declare kleines_array as array 8 of pointer to const int
8
const int *kleines_array[8]

HTH
leo

von Yalu X. (yalu) (Moderator)


Lesenswert?

Vincent H. schrieb:
> Dann berät man in zwei dutzend Antworten darüber wie man das Array
> umcasten kann ohne ein einziges Mal zu erwähnen wie sinnlos das Ganze
> ist, wenn der entstehende Pointer auf jeder halbwegs modernen Platform
> bereits die Hälfte der neuen Array-Größe in Anspruch nehmen würde.

Wenn du dieser Zeile

DPA schrieb:
> uint8_t (*const kleines_array)[8] = (uint8_t(*const)[8])&grosses_array[8];

ein static voranstellst, belegt die Pointer-Variable kleines_array
exakt 0 Bytes an Speicher, weil sie vom Compiler gar nicht erst angelegt
wird.

Ich hatte zwar selber noch nie den Fall, wo ich ein solches Konstrukt
wirklich sinnvoll einsetzen konnte, kann mir aber solche Fälle durchaus
vorstellen. Man kann nun entweder über Sinn und Unsinn der Fragestellung
philosophieren oder versuchen, die Frage – einfach so wie sie gestellt
ist – bestmöglich zu beantworten. Bernd und DPA haben sich für letzteres
entschieden. Ist das wirklich so schlimm?

Bernds Vorschlag mit der Union ist gut und richtig, DPAs Weg über den
Array-Pointer gefällt mir noch etwas besser, weil dort das Original-
Array grosses_array in genau der gleichen Weise verwendet werden kann
als gäbe es den Subarray-Alias kleines_array gar nicht.

Noch besser wäre es wenn auch kleines_array ohne den Umweg über die
Union (bei Bernd) oder die Pointer-Dereferenzierung (bei DPA) wie ein
ganz normales Array verwendet werden könnte, das ist aber in C IMHO gar
nicht möglich.

von Dirk B. (dirkb2)


Lesenswert?

Vincent H. schrieb:
> Und damit der Threadersteller auch ja nichts lernt voted man sinnvolle
> Antworten wie "memcpy" oder "C Arrays besitzen keine Längeninfo" noch
> schön runter...

Man kommt aber an die Längeninfo von Arrays mit sizeof ran.

Und da fing das Probblem beim TO ja auch an.

Er wollte das sizeof ja weiterjhin nehmen.

Hätte er von Anfang an eine Variable/#define/enum dafür genommen, wäre 
das Problem nicht vorhanden.

von Wilhelm M. (wimalopaan)


Lesenswert?

Dirk B. schrieb:
> Vincent H. schrieb:
>> Und damit der Threadersteller auch ja nichts lernt voted man sinnvolle
>> Antworten wie "memcpy" oder "C Arrays besitzen keine Längeninfo" noch
>> schön runter...
>
> Man kommt aber an die Längeninfo von Arrays mit sizeof ran.

Nur wenn der Arraybezeichner noch nicht zur einem Zeigertyp zerfallen 
ist.

von Wilhelm M. (wimalopaan)


Lesenswert?

Niklas G. schrieb:
> In C++ könnte man eine Referenz umcasten. Ist aber ziemlich hässlich.


Ich würde in C++ std::span<> dafür einsetzen, und natürlich std::array 
von Anfang an ...

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Ich würde in C++ std::span<> dafür einsetzen, und natürlich std::array
> von Anfang an ...

Vincent H. schrieb:
> Und damit der Threadersteller auch ja nichts lernt voted man sinnvolle
> Antworten wie "memcpy" oder "C Arrays besitzen keine Längeninfo" noch
> schön runter...

Er wollte es aber genau so haben! Und da es in diesem Forum ja verpönt 
ist den Sinn der Frage anzuzweifeln und alternative Lösungswege 
aufzuzeigen...

von Wilhelm M. (wimalopaan)


Lesenswert?

Niklas G. schrieb:
> Wilhelm M. schrieb:
>> Ich würde in C++ std::span<> dafür einsetzen, und natürlich std::array
>> von Anfang an ...
>
> Vincent H. schrieb:
>> Und damit der Threadersteller auch ja nichts lernt voted man sinnvolle
>> Antworten wie "memcpy" oder "C Arrays besitzen keine Längeninfo" noch
>> schön runter...
>
> Er wollte es aber genau so haben! Und da es in diesem Forum ja verpönt
> ist den Sinn der Frage anzuzweifeln und alternative Lösungswege
> aufzuzeigen...

Na, den Sinn einer Frage würde ich nicht anzweifeln, aber alternative 
Lösungswege sollte man immer aufzeigen ... jedenfalls werde ich daran 
festhalten ;-)

von Dirk B. (dirkb2)


Lesenswert?

Wilhelm M. schrieb:
> Nur wenn der Arraybezeichner noch nicht zur einem Zeigertyp zerfallen
> ist.

Dann ist es auch kein Array mehr.

auch wenn es wie eines aussehen mag.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Dirk B. schrieb:
> Wilhelm M. schrieb:
>> Nur wenn der Arraybezeichner noch nicht zur einem Zeigertyp zerfallen
>> ist.
>
> Dann ist es auch kein Array mehr.

Doch, das Objekt ändert sich nicht.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Dirk B. schrieb:
>> Wilhelm M. schrieb:
>>> Nur wenn der Arraybezeichner noch nicht zur einem Zeigertyp zerfallen
>>> ist.
>>
>> Dann ist es auch kein Array mehr.
>
> Doch, das Objekt ändert sich nicht.

Es ist ja auch nicht das Objekt, das zerfällt, sondern der Ausdruck, der
das Objekt referenziert. Dieser ist nach dem Zerfall im Beispiel von
grosses_array nicht mehr vom Typ uint8_t[16] (Array), sondern vom Typ
uint8_t* (Pointer).

von Bernd K. (prof7bit)


Lesenswert?

Vincent H. schrieb:
> macht sich Sorgen um
> 8 Byte "Speicherverbrauch".

Das hat er nicht geschrieben. Es gibt noch mehr Gründe warum man 
irgendwo mal lieber eine Referenz als eine Kopie haben will.

von Oliver S. (oliverso)


Lesenswert?

Aber nicht mitten in ein anderes Array hinein, mit der Anforderung, daß 
sizeof eine Größe liefert.

Oliver

: Bearbeitet durch User
von hambi (Gast)


Lesenswert?

Besten Dank für eure vielen und sachlichen Antworten. Ich werde mal die 
Lösungen von Bernd und DPA ausprobieren.

Es geht darum, dass ich über BLE von verschiedenen Perpherals 
verschiedenen Daten empfange. Diese sind dann in einem uint8_t Array 
gespeichert, wessen Grösse bei Compilezeit bekannt ist. Zur Verarbeitung 
dieser Daten muss ich sie in kleinere Array's füllen, was ich nur 
ungerne mit Copy machen würde. Da jedes Peripheral andere Daten sendet, 
wird jedes "Teilarray" nur einmal verwendet, d.h. deren Grösse variiert 
mit dem Peripheral. Wenn ich sizeof verwenden kann, dann brauche ich nur 
einmal anzugeben wie gross das Array ist, und kann danach auf sizeof 
zurückgreifen. Es wäre natürlich möglich diese Information in 
verschiedenen Define's zu speichern, was aber den Code aufbläst für eine 
Information, welche (wenn sizeof das korrekte gibt) nur zum deklarieren 
verwendet wird.

Besten Dank an euch alle, Hambi

von Niklas Gürtler (Gast)


Lesenswert?

hambi schrieb:
> Zur Verarbeitung dieser Daten muss ich sie in kleinere Array's füllen

Warum?

hambi schrieb:
> Es wäre natürlich möglich diese Information in verschiedenen Define's zu
> speichern, was aber den Code aufbläst für eine Information, welche (wenn
> sizeof das korrekte gibt) nur zum deklarieren verwendet wird

Ok du jetzt eine Zeile mit define oder eine Zeile mit hässlichem Cast 
hast...


Vielleicht hilft dir in dem Zusammenhang auch Serialisierung.

von Dirk B. (dirkb2)


Lesenswert?

hambi schrieb:
> was aber den Code aufbläst für eine
> Information,

Nein.

Magic Numbers sind doof.

hambi schrieb:
> uint8_t grosses_array[16];
> uint8_t kleines_array[8] = grosses_array+8;

Die 16 ist schon doof, gehört in ein #define. Schon kann man auf das 
sizeof verzichten.
Bei der 8 genauso.

von DPA (Gast)


Lesenswert?

hambi schrieb:
> Es geht darum, dass ich über BLE von verschiedenen Perpherals
> verschiedenen Daten empfange. Diese sind dann in einem uint8_t Array
> gespeichert, wessen Grösse bei Compilezeit bekannt ist. Zur Verarbeitung
> dieser Daten muss ich sie in kleinere Array's füllen, was ich nur
> ungerne mit Copy machen würde.

Ich würde ja einfach ein Struct mit Pointer + Grössenangabe nehmen:
1
typedef struct ble_data {
2
  uint8_t* data;
3
  size_t length;
4
} ble_data_t;

Oder bei verschiedenen BLE structs den typ mitspeichern, und dann ne 
lookup tabelle:
1
// .h
2
enum ble_dev_type {
3
  BLE_DEV_T_BLA,
4
  BLE_DEV_T_BLUB
5
};
6
7
struct_ble_type_info {
8
  size_t data_size;
9
};
10
11
struct ble_dev {
12
  enum ble_dev_type type;
13
};
14
15
struct ble_dev_bla {
16
  struct ble_dev ble;
17
  int whatever;
18
  ...
19
};
20
21
struct ble_dev_blub {
22
  struct ble_dev ble;
23
  int blablub;
24
  ...
25
};
26
27
28
// .c
29
const struct_ble_type_info ble_type_info = {
30
  [BLE_DEV_T_BLA] = {
31
    .data_size = 16;
32
  },
33
  [BLE_DEV_T_BLUB] = {
34
    .data_size = 24;
35
  },
36
}

Und dann ble_dev_bla.ble.type immer mit BLE_DEV_T_BLA initialisieren, 
damit man die grösse bei jedem ble_dev mit 
ble_type_info[ble_dev.ble.type].data_size abfragen kann. Mit dem Typ 
kann man dann auch mit nem switch wieder von nem generischen "struct 
ble_dev" zu einem ble_dev_bla oder ble_dev_blub kommen.

Falls man keine runtime typ infos will, aber ein struct pro datensatz, 
und keine upcasts braucht, gibt es seit c11 auch _Generic:
1
struct ble_bla { ... };
2
struct ble_blub { ... };
3
4
#define BLE_DATA_SIZE(X) _Generic((X), \
5
    ble_bla: 12ul, \
6
    ble_blub: 23ul \
7
  )

Dann kann man später so zeug machen: "struct ble_bla bla; 
printf("%lu\n",BLE_DATA_SIZE(bla));" Ausgabe 12.

Jenachdem, was genau gebraucht wird.

von DPA (Gast)


Lesenswert?

Edit: Das generic sollte eher so sein, sorry:
1
#define BLE_DATA_SIZE(X) _Generic((X), \
2
    struct ble_bla: 12ul, \
3
    struct ble_blub: 23ul \
4
  )

von Nachdenklicher (Gast)


Lesenswert?

Niklas G. schrieb:
> In C++ (...) Ist aber ziemlich hässlich.

Redundant. Letzteres folgt aus Ersterem. Schönheit und Eleganz des 
Quelltextes gehören nicht zu den Stärken von C++.

von Johnny B. (johnnyb)


Lesenswert?

hambi schrieb:
> Da jedes Peripheral andere Daten sendet,
> wird jedes "Teilarray" nur einmal verwendet, d.h. deren Grösse variiert
> mit dem Peripheral. Wenn ich sizeof verwenden kann, dann brauche ich nur
> einmal anzugeben wie gross das Array ist

Irgendwo musst Du die Grössen so oder so angeben, daher bleibe ich bei 
meiner ursprünglichen Meinung, dies mit einem sprechend benannten 
#define an einer Stelle im Code zu tun.
Guter Code ist auch solcher, welchen auch Leute verstehen, die keinen 
Doktor in C Programmierung gemacht haben.

von Niklas Gürtler (Gast)


Lesenswert?

Nachdenklicher schrieb:
> Redundant. Letzteres folgt aus Ersterem. Schönheit und Eleganz des
> Quelltextes gehören nicht zu den Stärken von C++.

Genau wie bei C. Man darf darauf hoffen dass Rust hier aufräumen wird...

von DPA (Gast)


Lesenswert?

Niklas Gürtler schrieb:
> Nachdenklicher schrieb:
>> Redundant. Letzteres folgt aus Ersterem. Schönheit und Eleganz des
>> Quelltextes gehören nicht zu den Stärken von C++.
>
> Genau wie bei C. Man darf darauf hoffen dass Rust hier aufräumen wird...

Ich finde, bei C ist das viel einfacher als bei C++. Wenn man es richtig 
beherrscht ist es fast so versatil wie C++, aber es gibt nur wenige, 
klar festgelegte Konstrukte, statt viele verschiedene, um dinge damit 
umzusetzen. Damit ist es schwieriger, durch Verwendung aller möglicher 
Features Verwirrung zu stiften. Es ist immer eine Kunst, schönen und 
Lesbaren Code zu schreiben, ohne Spagetticode, Romanfunktionen & 
Dateien, oder Feature-Überkomplexifizierung zu betreiben. Wenn man C 
Code schön strukturiert, geht das meiner Meinung nach meistens aber sehr 
gut. Ich hatte auch schon läute, die überacht waren, als sie einen 
einfach verständlichen JavaScript Code gesehen haben. Das wichtigste 
ist, dass alles an einem Ort ist, wo es sinn macht. Dass man die 
Struktur ansieht, und denkt, "das müsste dort sein", und es dann 
tatsächlich dort ist, und es im Idealfall dann nur ein paar simple 
Funktionen und Datenstrukturen sind, dessen Zweck man sofort versteht. 
Oft macht es auch sinn, die Kontrolle über Listeneinträge usw. 
umzukehren, also z.B. die Implementationen von dingen sich selbst 
registrieren lassen, statt eine Liste zu nehmen wo alle Implementationen 
drin sind. (siehe z.B. linker listen in uboot, oder meinen linker hacks 
artikel im wiki). Aber aufpassen, wenn man das übertreibt und mit event 
Systemen koppelt, kann man den überblick verlieren wie etwas von x nach 
y kommt. Und bei total anderen Funktionen am besten ein anderes Projekt 
nehmen, ne library generieren, und ein möglichst schlankes und stabiles 
Interface im voraus definieren. Man kann übrigens in git auch submodule 
auf andere branches im selben repo machen. Man braucht aber immer viel 
Übung, und es ist sinvoll ein Refactoring immer sofort zu machen, statt 
zu warten. Zusammengefasst: Der Code ist gut, wenn man das gesuchte ohne 
grosses Vorwissen sofort dort findet, wo man es erwartet, und man immer 
sehr schnell sieht, woher etwas aufgerufen werden kann, welche Abläufe 
es gibt, und was die machen, bzw. wo die Informationen überall 
durchgeschoben werden. Oh, und nein, Rust kann auch nicht verhindern, 
dass jemand schlechten Code schreibt, keine Sprache kann das. Ausser 
Brainfuck, dort gibt es keinen besseren oder schlechteren code.

von Niklas Gürtler (Gast)


Lesenswert?

"Rambling"

von Oliver S. (oliverso)


Lesenswert?

DPA schrieb:
> void example(uint8_t e[8]){
>   // AHTUNG: kein sizeof auf e anwenden!
> }

Das ist richtig, aber da es dem TO ja um kleines_array und dessen Größe 
ging, kann man deinen array-pointer tatscählich auch dort mit sizeof() 
verwenden:
1
#include <stdint.h>
2
#include <stdio.h>
3
4
uint8_t grosses_array[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
5
6
typedef uint8_t (*const kleines_array_p_t)[7];
7
static kleines_array_p_t kleines_array = (kleines_array_p_t)&grosses_array[8];
8
9
10
void example(kleines_array_p_t e)
11
{
12
  printf("sizeof(*e) = %I64u\n", sizeof(*e));
13
  for(int i=0; i<sizeof(*e); i++)
14
    printf("e[%d] = %d\n",i,(int)(*e)[i]);
15
}
16
17
int main()
18
{
19
  printf("sizeof(*kleines_array) = %I64u\n", sizeof(*kleines_array));
20
  puts("");
21
  for(int i=0; i<8; i++)
22
    printf("(*kleines_array)[%d] = %d\n",i,(int)(*kleines_array)[i]);
23
  puts("");
24
  example(kleines_array);
25
}

: Bearbeitet durch User
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.