Forum: Mikrocontroller und Digitale Elektronik [C] Arrays zusammenführen, oder: Array als Funktionsrückgabe


von Draco (Gast)


Lesenswert?

Problem: Ich habe mehrere Arrays, welche ich während der Laufzeit zu 
einem Array zusammenfassen muss. Ich habe mir eine Funktion geschrieben 
und wollte damit eigentlich einfach ein Array zurückgeben, dies 
funktioniert aber so natürlich nicht.
1
uint8_t[] AttachArray(uint8_t ArrayOne[],uint8_t ArrayTwo[])
2
{
3
  uint8_t ArrayOneLength = sizeof(ArrayOne);
4
  uint8_t ArrayTwoLength = sizeof(ArrayTwo);
5
  uint8_t ArrayAllLength = ArrayOneLength + ArrayTwoLength;
6
7
        uint8_t newArray[];
8
9
  uint8_t NewArray[ArrayAllLength];
10
11
  for(uint8_t i = 0;i<ArrayOneLength;i++)
12
  {
13
     NewArray[i] = ArrayOne[i];
14
  }
15
16
  for(uint8_t i = ArrayOneLength; i < ArrayAllLength;i++)
17
  {
18
     NewArray[i] = ArrayTwo[i-ArrayOneLength];
19
  }
20
21
        return NewArray;
22
}

Gibt es eine einfache Möglichkeit dies zu bedienen? Man Könnte ja noch 
mit Pointern arbeiten, aber das wollte ich eigentlich umgehen. 
Desweiteren: gibt es eine Möglichkeit ein Array nicht mit einer 
statischen Größe anzulegen sondern mit einer dynamischen?

von DFWKGF (Gast)


Lesenswert?

Sizeof macht nicht das was du denkst. Du solltest die größe der arrays 
mit übergeben.

von Draco (Gast)


Lesenswert?

DFWKGF schrieb:
> Sizeof macht nicht das was du denkst. Du solltest die größe der arrays
> mit übergeben.

Ja ich weiß, das ist mir bewusst.

Bei uint8 aber schon, sizof gibt ja die Größe des Bereichs in Byte an... 
das Array ist ja soundso viel Bytes groß, bei einem uint8_t Array[10] 
ist die Größe auch 10. Bei einem uint16_t Array[10] ist die Größe dann 
20 Bytes usw...

von DFWKGF (Gast)


Lesenswert?

Und jetzt wo ich mir mal ein paar Zeilen mehr angesehen hab...

-newarray[] ist nach dem return tot
-arrays sind eigentlich auch pointer. Warum haben alle Anfänger Angst 
vor Pointern?

von Draco (Gast)


Lesenswert?

DFWKGF schrieb:
> Und jetzt wo ich mir mal ein paar Zeilen mehr angesehen hab...
>
> -newarray[] ist nach dem return tot
> -arrays sind eigentlich auch pointer. Warum haben alle Anfänger Angst
> vor Pointern?

- NewArray[] darf ja nach dem return tot sein. Es sei denn ich setze ein 
Pointer.

- Die piecksen so :-D Das Problem ist, ich muss mich ja dann um die 
ganze Speicherverwaltung selber kümmern, weil - wie du schon sagtest, 
das neue Array dann ja tot ist, also muss ich es ja irgendwo speichern 
können. Sobald ja der Pointer auf der Funkttion liegt, die Funktion 
verlassen wird, wird ja der Speicher wieder freigegeben. Der Pointer 
liegt zwar auf der Speicherstelle aber Werte darin können wieder 
überschrieben werden.

von Peter II (Gast)


Lesenswert?

Draco schrieb:
> DFWKGF schrieb:
>> Sizeof macht nicht das was du denkst. Du solltest die größe der arrays
>> mit übergeben.
>
> Ja ich weiß, das ist mir bewusst.
>
> Bei uint8 aber schon, sizof gibt ja die Größe des Bereichs in Byte an...

nein macht es nicht. Scheinbar ist es dir nicht bewusst.

von Rolf M. (rmagnus)


Lesenswert?

Draco schrieb:
> Gibt es eine einfache Möglichkeit dies zu bedienen? Man Könnte ja noch
> mit Pointern arbeiten, aber das wollte ich eigentlich umgehen.

Tust du aber bereits, offenbar ohne das zu wissen.

> Desweiteren: gibt es eine Möglichkeit ein Array nicht mit einer
> statischen Größe anzulegen sondern mit einer dynamischen?

Ja.

> uint8_t[] AttachArray(uint8_t ArrayOne[],uint8_t ArrayTwo[])

Diese Zeile hätte schon einen Fehler bringen sollen, denn Arrays können 
nicht als Rückgabetyp von Funktionen verwendet werden. Auch als 
Parameter können sie nicht übergeben werden, aber da gibt es eine 
Besonderheit: Es wird automatisch stattdessen ein Pointer auf den Anfang 
des Arrays übergeben...

> {
>   uint8_t ArrayOneLength = sizeof(ArrayOne);
>   uint8_t ArrayTwoLength = sizeof(ArrayTwo);

... womit hier natürlich schon Blödsinn rauskommt, denn ArrayOneLength 
und ArrayTwoLength wird die Größe eines Zeigers enthalten.

>   uint8_t ArrayAllLength = ArrayOneLength + ArrayTwoLength;
>
>         uint8_t newArray[];

Auch das wird nicht gehen. Mann kann keine Array-Variable ohne Größe 
erzeugen.

>   uint8_t NewArray[ArrayAllLength];

Das wiederum geht, da lokale Arrays auch eine erst zur Laufzeit 
berechnete Länge haben. Allerdings hört dieses Array mit Rücksprung aus 
der Funktion auf zu existieren, darf also danach nicht mehr benutzt 
werden.

von Peter II (Gast)


Lesenswert?

Rolf M. schrieb:
>>   uint8_t NewArray[ArrayAllLength];
>
> Das wiederum geht, da lokale Arrays auch eine erst zur Laufzeit
> berechnete Länge haben.

ja und nein. Mittlerweile geht es, bei alten Compiler geht es nicht. 
Keine Ahnung mit welcher C-Version es zulässig geworden ist.

von Rolf M. (rmagnus)


Lesenswert?

Peter II schrieb:
> ja und nein. Mittlerweile geht es, bei alten Compiler geht es nicht.
> Keine Ahnung mit welcher C-Version es zulässig geworden ist.

Mit der, die im Jahre 1999 genormt wurde. Es stimmt allerdings, dass es 
immer noch einige ewig gestrige Compiler-Hersteller gibt, die das selbst 
stolze 17 Jahre später noch nicht hingekriegt haben.

von Sebastian S. (amateur)


Lesenswert?

>Desweiteren: gibt es eine Möglichkeit ein Array nicht mit einer
>statischen Größe anzulegen sondern mit einer dynamischen?

Du kannst jederzeit ein bekanntes (typ) Array anlegen.
Sonst kannst Du nur einen, linear zusammenhängenden Bereich reservieren. 
Die Nutzung desselben (Zeilen/Spalten) musst Du aber selber 
dokumentieren.
Die Doppeldeutigkeit lässt sich einfach Aufzeigen:
Array 5X4  (20 Bytes)
Array 4X5  (20 Bytes)
Array 2X10 (20 Bytes)

Für weitere Arbeiten mit solch einem Block braucht’s die Info, wo z.B. 
die Zweite Zeile und die Zweite Spalte liegen.
Zählung 1, 2, 3...
1. Array Byte 7
2. Array Byte 6
3. Array Byte 4

Traditionell lässt C nur die Rückgabe eines Wertes zu. Das kann ein Byte 
sein, oder ein 64 Bit Zeiger.

von Tom (Gast)


Lesenswert?

Da ein Array meistens das gleiche ist, wie der Zeiger auf sein erstes 
Element, kann man das ausnutzen, indem man nur das gesamte Array anlegt 
und als Einzel-Arrays Pointer an die passende Stelle im Gesamt-Array 
benutzt.
Dann stehen die Arrays automatisch hintereinander und AttachArray ist 
überflüssig. Die einzelnen Funktionen, die mit den kleinen Arrays 
arbeiten, merken das gar nicht: http://ideone.com/yv05vc

von Peter II (Gast)


Lesenswert?

Sebastian S. schrieb:
> Traditionell lässt C nur die Rückgabe eines Wertes zu. Das kann ein Byte
> sein, oder ein 64 Bit Zeiger.

naja, es lässt auch eine stuct zu. Die wiederum mehre Werte haben kann.

von Rolf M. (rmagnus)


Lesenswert?

Peter II schrieb:
> Sebastian S. schrieb:
>> Traditionell lässt C nur die Rückgabe eines Wertes zu. Das kann ein Byte
>> sein, oder ein 64 Bit Zeiger.
>
> naja, es lässt auch eine stuct zu. Die wiederum mehre Werte haben kann.

Und die lustigerweise natürlich auch Arrays enthalten kann.

von N. G. (newgeneration) Benutzerseite


Lesenswert?

Hallo,

ein paar Sachen, die mir aufgefallen sind:
1
uint8_t[] AttachArray(uint8_t ArrayOne[],uint8_t ArrayTwo[]) {
Tja, schade, das funktioniert nicht so wie du denkst.
Man kann in C keine Arrays als Parameter übergeben oder zurückgeben.
Im Prinzip ist das nur ne geschönte SChreibweise von
1
uint8_t* AttachArray(uint8_t ArrayOne*,uint8_t ArrayTwo*)
Und du siehst: es gibt keine Längeninformation, lediglich einen Pointer 
auf das erste Element.
Das führt gleich zum zweiten Fehler:
1
uint8_t ArrayOneLength = sizeof(ArrayOne);
2
uint8_t ArrayTwoLength = sizeof(ArrayTwo);
Das hier kann aufgrund der Pointer, bzw. der fehlenden Lägneninformation 
nicht klappen. Da kommt lediglich die Größe eines uint8_t*-Typs raus, 
wahrscheinlich 2 (bei 16bit) oder 4, bzw. 8 (32bit/64bit).
Deswegen muss man die Arraylängen auch mitübergeben.

Somit enthält die nächste Zeile auch nicht die länge der kombinierten 
Arrays, sondern einfach 2 x die Pointergröße
1
//uint8_t ArrayAllLength = sizeof(uint8_t*) * 2;
2
uint8_t ArrayAllLength = ArrayOneLength + ArrayTwoLength;

Und ein weiterer Fehler, der ganz böse enden kann (oder einfach nur 
nicht funktioniert, das wäre der beste Fall):
1
uint8_t NewArray[ArrayAllLength];
Du legst hier ein Array mit dynamischer Größe an, das geht auch (imho 
seit C99), soweit kein Problem (Das die Größe oben falsch berechnet 
wurde ignorieren wir mal).

Dann wird kopiert.
1
for(uint8_t i = 0; i < ArrayOneLength; i++)
2
  NewArray[i] = ArrayOne[i];
Passt
1
for(uint8_t i = ArrayOneLength; i < ArrayAllLength; i++)
2
  NewArray[i] = ArrayTwo[i-ArrayOneLength];
Ich hätte das eher so geschrieben: (Deins müsste aber auch gehen, habs 
nur überflogen)
1
for(uint8_t i = 0; i < ArrayTwoLength; i++)
2
  NewArray[i + ArrayOneLength] = ArrayTwo[i];

So jetzt kommt der schlimme Fehler:
Davor hohle ich noch kurz aus:
1
uint8_t foo(void) {
2
  //ein bisschen Code
3
  {
4
    uint8_t derAbsoluteWertDerZahlZweiUndVierzig = 42;
5
  }
6
  return derAbsoluteWertDerZahlZweiUndVierzig;
7
}
Was glaubst du passiert?
Ich hoffe man kann den Block {...} erkennen. Wenn innerhalb eines 
Blockes (gleiches gilt für eine Funktion) eine Variable angelegt wird 
(und Speicherplatz angefordert wird), dann lebt diese automatische 
Variable nur solange, wie es den Block gibt. Danach wird sie "zerstört" 
und der Wert (in diesem Speicherplatz) wird ungültig.

Zurück zu deiner Funktion:

Was machst du?
Genau, die erzeugst eine lokale (=automatische) Variable, die beim 
Verlassen der Funktion wieder zerstört wird. Schlecht!
1
  return NewArray;
2
}
wird als einen Pointer zurückgeben, der auf einen undefinierten Bereich 
zeigt.

Besser wäre es so (nur runtergetippt, enthält sicher Fehler!):
1
//gibt einen Pointer zum ersten Element des neuen Arrays zurück
2
void* AttachArray(void *newArray,                //der User muss den neuen Speicherplatz beim Aufrufen bereitstellen
3
        void *oldArray1, size_t oldArray1_len,   //das erste Array und seine Länge
4
        void *oldArray2, size_t oldArray2_len) { //das zweite Array und seine Länge
5
    size_t i;
6
    for(i = 0; i < oldArray1_len; i++)
7
        newArray[i] = oldArray1[i];
8
    
9
    for(i = 0; i < oldArray2_len; i++)
10
        newArray[i + oldArray1_len] = oldArray2[i];
11
    return newArray;
12
}
13
14
//aufgerufen wirds dann so:
15
int main(void) {
16
    uint8_t array1[12];
17
    uint8_t array2[10];
18
    
19
    uint8_t arrayLen1 = sizeof(array1)/sizeof(array1[0]);
20
    uint8_t arrayLen2 = sizeof(array2)/sizeof(array2[0]);
21
    
22
    uint8_t array_1und2[arrayLen1 + arrayLen2];
23
    
24
    AttachArray(array_1und2, 
25
            array1, 12,
26
            array2, 10);
27
    
28
    //mach was damit...
29
    return 0;
30
}

Achtung: nur "kurz" runtergetippt!
Hat aber natürlich viel zu lange gedauert ;)

von kernelpanic (Gast)


Lesenswert?

Wie wäre es mit folgender Lösung? Ohne Pointer (bzw. ohne im Code 
sichtbare Pointer) und mit vorallokiierten Speicherbereichen (buf), so 
dass du dich zur Laufzeit nicht um dynamische Speicherverwaltung 
(malloc, free) kümmern musst. Ob das die perfekte Lösung ist, mag ich 
nicht zu beurteilen.
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <stdint.h>
4
5
void AttachArray(uint8_t ArrayOne[], int sizeOne, uint8_t ArrayTwo[], int sizeTwo, uint8_t result[]) {
6
  if (result == NULL)
7
    return;
8
9
  memcpy(result, ArrayOne, sizeOne);
10
  memcpy(result + sizeOne, ArrayTwo, sizeTwo);
11
}
12
13
int main(void) {
14
  uint8_t arr1[5] = { 0x01, 0x02, 0x03, 0x04, 0x05 };
15
  uint8_t arr2[5] = { 0x06, 0x07, 0x08, 0x09, 0x0A };
16
  uint8_t buf[10];
17
18
  AttachArray(arr1, 5, arr2, 5, buf);
19
20
  return 0;
21
}

Ein Array mit einer dynamische Größe wäre dann ein Pointer auf einen 
zuvor reservierten Speicherbereich (malloc), z. B.
1
uint8_t* dynamic = (uint8_t*) malloc(10 * sizeof(uint8_t));
Damit hättest du ein uint8_t Array mit 10 Einträgen, das du alternativ 
auch mit dynamic[0] bis dynamic[9] ansprechen kannst. Natürlich musst du 
den zur Laufzeit reservierten Speicher bei Nichtnutzung wieder 
freigeben, sonst müllst du dir deinen Heap zu.
1
free(dynamic);

von Markus F. (mfro)


Lesenswert?

Draco schrieb:

> Gibt es eine einfache Möglichkeit dies zu bedienen? Man Könnte ja noch
> mit Pointern arbeiten, aber das wollte ich eigentlich umgehen.
> Desweiteren: gibt es eine Möglichkeit ein Array nicht mit einer
> statischen Größe anzulegen sondern mit einer dynamischen?

Nur als schlechtes/akademisches Beispiel, liebe Kinder, bitte nicht 
Nachmachen: C hat (über einen Trick) durchaus die Möglichkeit, Arrays 
als Rückgabewert von Funktionen zu nutzen.

Man muss das Array lediglich in eine Struktur verpacken. Strukturen als 
Rückgabewert sind erlaubt (aber nur in Ausnahmefällen empfehlenswert):
1
struct wrapper1
2
{
3
    uint8_t array[20];
4
};
5
6
struct wrapper2
7
{
8
    uint8_t array[10];
9
};
10
11
struct wrapper1 nicht_nachmachen(struct wrapper2 w1, struct wrapper2 w2)
12
{
13
    int i;
14
    struct wrapper ret;
15
16
    for (i = 0; i < 10; i++)
17
    {
18
        ret.array[i] = w1.array[i];
19
        ret.array[i + 10] = w2.array[i];
20
    }
21
    return ret;
22
}

von Rolf M. (rmagnus)


Lesenswert?

Markus F. schrieb:
> Nur als schlechtes/akademisches Beispiel, liebe Kinder, bitte nicht
> Nachmachen

Was ist denn der tiefere Sinn darin, im Detail und mit Beispiel zu 
erklären, wie man es machen könnte, aber nicht soll?

von DFWKGF (Gast)


Lesenswert?

Und was soll schlimm daran sein?

von Draco (Gast)


Lesenswert?

Oh danke erstmal allen für eure sehr hilfreichen Antworten! Ich werde 
mir das morgen alles mal zu Gemüte führen. Aber ich hab nun doch schon 
einiges über Arrays gelernt - wo ich vorher einen anderen Blick drauf 
hatte.

Ich probiere das morgen alles mal aus.

von Markus F. (mfro)


Lesenswert?

Rolf M. schrieb:
> Markus F. schrieb:
>> Nur als schlechtes/akademisches Beispiel, liebe Kinder, bitte nicht
>> Nachmachen
>
> Was ist denn der tiefere Sinn darin, im Detail und mit Beispiel zu
> erklären, wie man es machen könnte, aber nicht soll?

Weil das Beispiel für das konkrete Problem des TO zwar nicht passt, ich 
aber trotzdem den Eindruck habe, daß dieses C-Feature (zu) oft vergessen 
wird.

Viele (nicht nur angehende) Programmierer scheinen dem Eindruck zu 
erliegen, daß C nur einfache Datentypen als Rückgabewert von Funktionen 
erlaube. Ich habe schon mehrere Beispiele erlebt (die rauszusuchen bin 
ich gerade zu faul) wo auch (anscheinend erfahrenere) Coder sofort 
"Böse, Böse: return einer lokalen Variable" geschrien haben, wenn eine 
Struktur returniert wurde.

Dabei ist das explizit erlaubt (die meisten Compiler haben sogar 
effektive Mechanismen dafür, um den Stack nicht über Gebühr zu belasten) 
und m.E. immer dann (insbesondere bei µC) besser, wenn die Alternative 
dynamische Speicherverwaltung hieße. Man muß nicht immer mit Kanonen auf 
Spatzen schießen.

von Dirk B. (dirkb2)


Lesenswert?

Früher, damals im letzten Jahrtausend, als C noch jung und von K&R 
bechrieben wurde, konnte man (auf manchen Systemen) keine structs 
zurück- bzw. übergeben.

Aber seit C89 ist das geregelt.

von Herbert P. (Gast)


Lesenswert?

Bezüglich sizeof: So, wie es der TO macht, geht es natürlich nicht, aber 
prinzipiell ist sizeof sehr wohl in der Lage, die Größe eines Arrays zu 
ermitteln (sogar die Anzahl der Elemente eines Arrays):

http://en.cppreference.com/w/c/language/sizeof

Gruß
Herby

von Peter II (Gast)


Lesenswert?

Herbert P. schrieb:
> Bezüglich sizeof: So, wie es der TO macht, geht es natürlich nicht, aber
> prinzipiell ist sizeof sehr wohl in der Lage, die Größe eines Arrays zu
> ermitteln (sogar die Anzahl der Elemente eines Arrays):
>
> http://en.cppreference.com/w/c/language/sizeof

aber nicht mehr in einer Funktion.

von Herbert P. (Gast)


Lesenswert?

Hat aber nichts mit einer Funktion zu tun, sondern mit der Tatsache, 
dass hier die Größe des Arrays dem Compiler nicht bekannnt ist. Sizeof 
wird nicht zur Laufzeit ausgewertet, sondern bei der Übersetzung. 
"sizeof(int[10])" funktioniert auch in einer Funktion.

Gruß
Herby

von Herbert P. (Gast)


Lesenswert?

Herbert P. schrieb:
> Hat aber nichts mit einer Funktion zu tun, sondern mit der
> Tatsache,
> dass hier die Größe des Arrays dem Compiler nicht bekannnt ist. Sizeof
> wird nicht zur Laufzeit ausgewertet, sondern bei der Übersetzung.
> "sizeof(int[10])" funktioniert auch in einer Funktion.
>
> Gruß
> Herby

Nachtrag: in seltenen Ausnahmen auch zur Laufzeit (sizeof eines Arrays 
mit variabler Größe)

Herby

von Draco (Gast)


Lesenswert?

Wo wir dann gerade dabei sind, wie ermittel ich denn ein Array zur 
Laufzeit? Oder ist es sinnvoller eine Variable mitlaufen zu lassen in 
welcher ich die größe des Arrays von vornherein Speicher?!

von Herbert P. (Gast)


Lesenswert?

Draco schrieb:
> Wo wir dann gerade dabei sind, wie ermittel ich denn ein Array zur
> Laufzeit? Oder ist es sinnvoller eine Variable mitlaufen zu lassen in
> welcher ich die größe des Arrays von vornherein Speicher?!

http://en.cppreference.com/w/c/language/array

Gruß
Herby

von Dirk B. (dirkb2)


Lesenswert?

Draco schrieb:
> Wo wir dann gerade dabei sind, wie ermittel ich denn ein Array zur
> Laufzeit? Oder ist es sinnvoller eine Variable mitlaufen zu lassen in
> welcher ich die größe des Arrays von vornherein Speicher?!

Unbedingt.

: Bearbeitet durch User
von Draco (Gast)


Lesenswert?

Wo wir dann gerade dabei sind, wie ermittel ich denn ein Array zur 
Laufzeit? Oder ist es sinnvoller eine Variable mitlaufen zu lassen in 
welcher ich die größe des Arrays von vornherein Speicher?!

Herbert P. schrieb:
> http://en.cppreference.com/w/c/language/array
>
> Gruß
> Herby

Da wird auch ausschließlich mit sizeof gearbeitet. Also so verkehrt kann 
das ganricht sein, solange es nicht funktionsübergreifend ist.

Dirk B. schrieb:
> Draco schrieb:
>> Wo wir dann gerade dabei sind, wie ermittel ich denn ein Array zur
>> Laufzeit? Oder ist es sinnvoller eine Variable mitlaufen zu lassen in
>> welcher ich die größe des Arrays von vornherein Speicher?!
>
> Unbedingt.

Ich weiß nun nicht ob das Sarkasmus oder Ernst ist :-D Ich gehe mal von 
ernst aus. Es ist ja nun auch nicht unbedingt üblich die Länge während 
einer Laufzeit zu ermitteln, da man ja eigentlich die Längen immer im 
Kopf hat. Deswegen kann man ja den Umweg für Ausnahmefälle die Länge 
eines Arrays in einer Variable speichern, oder ist das schlechter Stil?!

von Dirk B. (dirkb2)


Lesenswert?

Das war ernst gemeint.

Spätestens in einer Funktion hast du keine implizite Längeninformation 
über das Array.

Und eigentlich sollte es dem Programm egal sein, ob du ein Array hast 
oder dynamischen Speicher.

strcpy, strcat, strncpy und strncat sind Beispiele dafür, wie man es 
nicht macht.

von Daniel A. (daniel-a)


Lesenswert?

Ich löse sowas manchmal mit Callback funktionen:
1
#include <stddef.h>
2
#include <string.h>
3
4
void concat( size_t n1, void* a1, size_t n2, void* a2, void(*callback)(size_t,void*,void*), void* ptr ){
5
  char result[n1+n2];
6
  memcpy(result, a1, n1);
7
  memcpy(result + n1, a2, n2);
8
  callback(n1+n2,result,ptr);
9
}

von Rolf M. (rmagnus)


Lesenswert?

Markus F. schrieb:
> Dabei ist das explizit erlaubt (die meisten Compiler haben sogar
> effektive Mechanismen dafür, um den Stack nicht über Gebühr zu belasten)
> und m.E. immer dann (insbesondere bei µC) besser, wenn die Alternative
> dynamische Speicherverwaltung hieße. Man muß nicht immer mit Kanonen auf
> Spatzen schießen.

Die gewählte Alternative ist in C aber meist eher, den Aufrufer sich um 
den Speicher kümmern zu lassen und einen Zeiger darauf reinzugeben.

Herbert P. schrieb:
> Hat aber nichts mit einer Funktion zu tun, sondern mit der Tatsache,
> dass hier die Größe des Arrays dem Compiler nicht bekannnt ist.

Es hat nichts mit bekannten oder unbekannten Arraay-Größen zu tun, 
sondern damit, dass das, was hier an sizeof übergeben wird, schlicht und 
ergreifend kein Array ist, sondern ein Zeiger. Es gibt zwar hier wieder 
einige, die behaupten, ein Array sei ein Zeiger, aber das ist falsch. 
Und sizeof ist eine der Stellen, an denen man das merkt.

Draco schrieb:
> Wo wir dann gerade dabei sind, wie ermittel ich denn ein Array zur
> Laufzeit?

Gar nicht.

> Oder ist es sinnvoller eine Variable mitlaufen zu lassen in welcher ich
> die größe des Arrays von vornherein Speicher?!

Du musst dich selbst drum kümmern, die Größe zu kennen, z.B. indem du 
sie irgendwo mitführst, oder wie z.B. bei C-Strings durch Nutzung eines 
bestimmten Wertes als Ende-Markierung.
Wenn du z.B. einen char* hast, ist das ein Zeiger auf einen char, 
nicht mehr und nicht weniger. Ob da im Speicher jetzt genau nur dieser 
einzelne char steht oder dieser nur der Anfang eines ganzen Arrays ist, 
läßt sich am Zeiger nicht erkennen.

: Bearbeitet durch User
von Peter M. (r2d3)


Lesenswert?

Draco,

ich kann kaum C, programmiere meist immer noch in VBA.

die optimale Lösung für Dein Problem hängt davon ab, wie Du Deine Arrays
überhaupt nutzt!

Ist es so, dass Deine Arrays immer nur nach hinten anwachsen und dann
zwei Arrays aneinander gehängt werden müssen?

Dann kannst Du Dein Problem z.B. mit linearen Listen erschlagen:

http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/...

Du reservierst Dir den Speicher für jedes Element dynamisch und hängst
die neuen Elemente der Reihe nach hinten an.

Wenn Du zwei Listen verketten willst, durchläufst Du die vordere bis zum
letzten Element und setzt den Zeiger auf das nächste Element auf das
erste Element der hinteren Liste.

Das birgt einen Nachteil:
Bei jedem Dranhängen musst Du die Liste linear bis zum Ende durchlaufen,
so auch, wenn Du zwei Listen verketten willst.

Das kannst Du mit einem Trick umgehen:
Du nutzt die Draco-Liste!

Deine Liste besteht vorne aus dem besonderen Draco-Struktur, der normale
Structuren folgen.
Die Draco-Struktur hat zwei Zweiger:
Der eine Zeiger zeigt auf das letzte Element der Liste, und der zweite
Zeiger auf das nächste Element der Liste.

Das Anhängen von neuen Elementen geht jetzt ganz schnell:
Mit der Draco-Struktur vorne findest Du in einem Schritt das letzte
Element Deiner Liste und hängst einfach etwas an. Natürlich musst Du
auch den zweiten Zeiger vorne pflegen, damit der wieder auf das letzte
Element der Liste zeigt.

Wenn Du die zweite Draco-Liste dranhängst, musst Du beim Verketten nur
darauf achten, dass das letzte Element der ersten Liste nicht auf die
Drako-Struktur zu Beginn der zweiten Liste zeigt, sondern auf das erste
Element der zweiten Liste.
Den Speicher für die Drako-Struktur der zweiten Liste solltest Du dann
vermutlich freigeben, wenn Du sie nicht mehr brauchst.

Wenn Du allerdings in Deinen Listen öfters Elemente löschst und neue
irgendwo einfügst, dann bieten sich Baumstrukturen zur Verwaltung an.
Die sind allerdings ein bischen kniffeliger.

Hier ein Musterbeispiel für wirklich ineffizienten Code, wo jemand
Arrays verwendet, die aber ungeeignet sind.
Das vorhandene Array wird zyklisch in ein größeres Array umkopiert:

http://www.vb-fun.de/cgi-bin/loadframe.pl?ID=vb/ti...

Das Umkopieren bremst die rekursive Dateisuche enorm aus.
Loggt man das Anwachsen des Dateizählers "m_lngFileCount" mit, kann man
zugucken, wie das Programm immer langsamer wird.

Das kann man in VBA ganz geschickt durch Wahl der passenden
Datenstruktur (kein Array!) vermeiden.

Es ist keine gute Idee, Strategien mit Konstrukten, die in Sprachen wie
VB oder VBA  irgendwie funktionieren, auf C zu übertragen.

Große dynamische Strukturen verwaltet man am besten mit Zeigern.
Man denkt sich eine Struktur aus, die die Daten tragen soll.
Dynamisch reserviert man sich Speicher für so ein Element der Struktur,
packt seine Daten herein und sortiert das Element in seine Struktur ein
- via Zeiger. Beim Löschen repariert man seine Struktur und gibt den
freigewordenen Platz frei.

Irgendwann landet man dann auch bei Baumstrukturen.

Vom Array hin zu einer zeigerbasierten Struktur ist ein wichtiger
Erkenntnissprung, an dem kommt man irgendwann nicht mehr vorbei.

Möge der Zeiger bald mit Dir sein. :)
Viel Erfolg!

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.