Forum: Compiler & IDEs speicher allokierung


von oliver (Gast)


Lesenswert?

hallo,
wie kann ich einen Speicher für ein zweidimensionales Array allokieren.
Beispiel Buffer[10][25];
wenn ich so schreibe: char[25]* Buffer = malloc(14 * sizeof(char[25]));
bekomme ich einen Fehler.

Danke

von Klugscheisser (Gast)


Lesenswert?

@ Oliver
Nun wäre es ja mal interessant gewesen die Fehlermeldung zu lesen.
Aber man kann es sich schon denken:

Mit
>Beispiel Buffer[10][25];
IST der Speicher schon alloziiert.

Warum dann noch mal alloziieren?

von Klugscheisser (Gast)


Lesenswert?

Jetzt habe ich doch selber einen Schreibfehler drin: Es muss heissen 
"allozieren". Kein doppeltes "i".

von Klugscheisser (Gast)


Lesenswert?

Also noch einmal ein bischen ausführlicher:

Du hast mehrere Möglichkeiten:

1. Volle Definition
char Buffer[10][25];
Hier brauchst Du keine Allokation.

2. Definition eines Pointer-Arrays
char * Buffer [10];
Dann brauchst Du eine (oder mehrere) Allokation(en):
Buffer[0] = (char *) malloc (25);
Buffer[1] = (char *) malloc (25);
usw.
(Evtl. muss das Argument von malloc noch gecastet werden. Kommt auf den 
Compiler an.

3. Definition eines Zeiger auf ein Zeiger-Array
char ** Buffer;
Dann erstmal Allokierung der Zeiger.
Buffer = (char **) malloc (10 * sizeof(char *));
Dann die einzelnen Strings.
* Buffer = (char *) malloc (25);
* (Buffer + 1) = (char *) malloc (25);

Was Du da geschrieben hast ist einfach seltsam:
>char[25]* Buffer = malloc(14 * sizeof(char[25]));
Was soll z.B. char[25] * sein?
Das ist syntaktisch nicht korrekt.
Und was soll 14 * sizeof(char[25]) sein?
Wo kommt die 14 her? Und wenn dann sollte es 25 * sizeof(char) heissen.
Die ganze Zeile passt aber nicht in den Kontext von
>Buffer[10][25];
Hier fehlt aber auch der Datentyp.

Klar, soweit?

von oliver (Gast)


Lesenswert?

ja

von yalu (Gast)


Lesenswert?

Ein echtes zweidimensionales 10x25-Array, wie du es ursprünglich wohl
vor hattest, geht so:
1
  char (*buffer)[25];
2
3
  buffer = malloc(10 * sizeof (char [25]));

Du bist also nur knapp an der richtigen Schreibweise vorbeigeschrammt.
Lediglich deine Definition des Puffers als Zeiger auf ein char-Array
mit 25 Elementen war falsch.

Ob man lieber ein zweidimensionales Array (also ein Array von Arrays)
oder ein Array von Zeigern anlegt, hängt vom Anwendungsfall ab. Das
Zeigerarray braucht zusätzlichen Datenspeicher für die Zeiger und
zusätzlichen Programmspeicher für die Allokation, dafür können die
einzelnen Zeilearrays unterschiedlich groß gewählt werden.

von Klugscheisser (Gast)


Lesenswert?

@ yalu

Ich gebe Dir zwar recht, das bis auf die erste Lösung
1
char Buffer[10][25];
 meine anderen keine "echten" Arrays in dem Sinne sind, das alle 
Elemente hintereinander weg im Speicher liegen, aber Deine Deklaration
1
char (*buffer)[25];
 ist genau genommen auch nicht richtig, da sie einen Zeiger auf einen 
Vektor mit 25 char Elementen deklariert. Es müsste also
1
char (*buffer)[250];
 heissen.

von yalu (Gast)


Lesenswert?

> Ich gebe Dir zwar recht, das bis auf die erste Lösung
>
> char Buffer[10][25];
>
>  meine anderen keine "echten" Arrays in dem Sinne sind, ...

Wie gesagt, das Adjektiv "echt" sollte deine Vorschläge 2 und 3 nicht
abwerten. Je nach Anwendungsfall ist einmal die eine und ein anderes
Mal die andere Lösung vorteilhafter.

> ... aber Deine Deklaration
>
> char (*buffer)[25];
>
>  ist genau genommen auch nicht richtig, da sie einen Zeiger auf einen
> Vektor mit 25 char Elementen deklariert.

Genau das ist auch beabsichtigt. In C ist ein 10x25-char-Array ein
Array mit 10 Elementen, von denen jedes ein Array mit 25 chars ist.
Ein Array kann durch einen Zeiger auf das erste Element repräsentiert
werden. So kann bspw. ein Zeiger auf int als Repräsentant für ein
ein int-Array verwendet werden. Der genaue Typ des Zeigers ist
wichtig, weil dessen Größe zur Adressberechnung benutzt wird.

Im obigen Beispiel sind die Elemente des äußeren Arrays nicht vom Typ
int, sondern vom Typ char[25]. Folglich muss ein das Array
repräsentierende Zeiger ein Zeiger auf char[25] sein, also den Typ
char(*)[25] haben.

Ein dreidimensionales 7x10x25-char-Array hätte den Elementtyp
char[10][25]. Es müsste somit mit
1
char (*buffer)[10][25];
2
buffer = malloc(7 * sizeof (char [10][25]));

alloziert werden. Für höherdimensionale Arrays gilt entsprechendes.

von daniel (Gast)


Lesenswert?

klugscheisser lern von yalu^^

in der freien minute kannst du folgendes analysieren ;)
char (*buffer[10])[10];
int (*p(int x, int y))();

von Simon K. (simon) Benutzerseite


Lesenswert?

daniel wrote:
> klugscheisser lern von yalu^^
>
> in der freien minute kannst du folgendes analysieren ;)
> char (*buffer[10])[10];
> int (*p(int x, int y))();

Du könntest ruhig ein paar mehr Kommas und Punkte setzen. Also ich 
versteh deinen Post auch nach dem zweiten mal durchlesen nicht.

von Klugscheisser (Gast)


Lesenswert?

@ yalu

Leider bin ich nicht Deiner Auffassung.

>Im obigen Beispiel sind die Elemente des äußeren Arrays nicht vom Typ
>int, sondern vom Typ char[25].

Meiner Meinung nach sind die Elemente vom Typ char und nicht vom Typ 
char[25].
Strenggenommen gibt es den Typ char[25] garnicht.

Deine Ansicht impliziert auch (wenn ich Dich richtig verstehe), dass, 
falls wir Deine Deklaration
1
char (*buffer)[25];
voraussetzen, eine Ausdruck wie
1
buffer + 1
einen Speicher 25 Elemente nach dem allerersten adressieren würde. Also 
das "+1" immer eine ganze Zeile von 25 Elementen überspringen würde.
Das sehe ich nicht so. Er würde das zweite (also erste, wenn man von 0 
an zählt) Element adressieren.

von Klugscheisser (Gast)


Lesenswert?

@ yalu

Zunächst einmal möchte ich mich entschuldigen, dass ich in meinem 
letzten Post keine Begründung für meine gegen Deine gestellte Aussage 
geliefert habe. Ich wollte nicht unhöflich sein, aber ich hatte es eilig 
noch was einzukaufen, und, was noch schwerwiegender ist, der Gedanke, 
den Du da vorbringst widerspricht einigen Annahmen, die ich meinte aus 
meiner C-Erfahrung und dem lesen entsprechender Bücher ableiten zu 
können.

Beim fahren habe ich über das Ganze nochmal nachgedacht und finde Deine 
Behauptung jetzt zumindest plausibel. Es wäre nützlich wenn durch die 
Deklaration
1
char (*buffer)[25];
nachfolgende Adressberechnungn ausgehend von buffer berücksichtigen 
würden, das der Zeiger auf ein Array mit 25 Elementen zeigt.

Ich habe dem widersprochen, weil
1. Eine Äquivalenz von Array-Namen und Zeigern besteht. D.h. mit
1
int a[8];
2
int * p1;
3
p1 = &a[0];
4
p2 = a;
werden p1 und p2 die gleichen Werte erhalten.
Daraus habe ich abgeleitet, das es keine Möglichkeit gibt, einen Zeiger 
auf ein Array so zu bilden, dass die Längeninformation erhalten bleibt. 
Ein Schluss der zugegebenermaßen, wenn er allein aus dieser Aussage 
abgeleitet wird, strenggenommen nicht korrekt ist. Aber ich meinte sonst 
keinen Hinweis gefunden zu haben der meiner Annahme widerspricht.

2. Die Verwendung in Klammern in dem Ausdruck
1
char (*buffer)[25];
würde deswegen erfolgen um den Vorrang der eckigen Klammen vor dem '*' 
aufzuheben, damit zwischen einem Array von Pointern und einem Zeiger auf 
einem Array unterschieden werden kann. In diesem Sinne wären dann
1
char (*buffer)[25];
und
1
char buffer[25];
äquivalent, wenn buffer als Adresse des Vektors zur Adressierung von 
Elementen des Array in den Grenzen 0 bis 24 dient und darüber hinaus zu 
nichts weiter.

Es gibt dazu im K&R, 2. Auflage "ANSI C", 1990, S.108 eine Aussage, die 
ich bisher irgendwie nicht richtig bewertet habe, wenn Deine Behauptung 
zutrifft. Der Schluss des Abschnitts über Mehrdimensionale Vektoren 
fordert, das die letzten Dimensionen eines Mehrdimensionalen Vektors 
immer genannt werden muss. Nur die erste darf weggelassen werden.
Nun denke ich, das diese Forderung ja nur Sinn macht, wenn man 
Adressarithmetik mit dem Zeiger machen kann; dazu muss ja die Grösse der 
"inneren" Vektoren bekannt sein.
Ich habe irgendwie immer nur den Teil mit dem Vorrang der eckigen 
Klammern ggü. dem '*' gelesen und war damit zufrieden.

Also, kurz und gut: Ich denke jetzt das Du recht hast, was die 
Verwendung Deines Ausdrucks betrifft.

Allerdings hätte ich doch noch eine Kritik an dem Ausdruck
1
char (*buffer)[25];
Und zwar ist vor allem Lint nicht mehr möglich zu entscheiden ob eine 
Arrayzugriff auf initilisierte Elemente erfolgt. Siehst Du das anders?

von yalu (Gast)


Lesenswert?

> Zunächst einmal möchte ich mich entschuldigen, dass ich in meinem
> letzten Post keine Begründung für meine gegen Deine gestellte
> Aussage geliefert habe.

Kein Problem :)

> Ich habe dem widersprochen, weil
> 1. Eine Äquivalenz von Array-Namen und Zeigern besteht. D.h. mit
>
> int a[8];
> int * p1;
> p1 = &a[0];
> p2 = a;
>
> werden p1 und p2 die gleichen Werte erhalten.
> Daraus habe ich abgeleitet, das es keine Möglichkeit gibt, einen Zeiger
> auf ein Array so zu bilden, dass die Längeninformation erhalten bleibt.

Das stimmt. Der Zeigertyp enthält zwar Informationen über den Typ und
damit Größe der Elemente des Arrays, auf dessen Anfang er zeigt (in
diesem Fall int). Er entält aber keine Informationen über die Anzahl
der Array-Elemente (in diesm Fall 8).

Das ist aber bei dem Zeiger
1
  char (*buffer)[25];
der auf den Anfang eines Arrays mit 10 Untererrays mit jeweils 25
chars zeigt, nicht anders: Der Elementtyp steckt mit drin, jedes
Element ist nämlich ein char-Array mit 25 Elementen. Die Größe des
gesamten Arrays (10 Elemente) geht aus dem Typ von buffer aber nicht
hervor. Muss es auch nicht, da die Gesamtgröße zur Adressierung der
einzelnen Elemente nicht benötigt wird und eine Überprüfung der
Array-Grenzen in C normalerweise nicht stattfindet.

> 2. Die Verwendung in Klammern in dem Ausdruck
>
> char (*buffer)[25];
>
> würde deswegen erfolgen um den Vorrang der eckigen Klammen vor dem '*'
> aufzuheben, damit zwischen einem Array von Pointern und einem Zeiger auf
> einem Array unterschieden werden kann.

Genau deswegen werden die Klammern gesetzt. Ohne die Klammern wäre
buffer ein Array mit 25 char-Zeigern.

> In diesem Sinne wären dann
>
> char (*buffer)[25];
>
> und
>
> char buffer[25];
>
> äquivalent, wenn buffer als Adresse des Vektors zur Adressierung von
> Elementen des Array in den Grenzen 0 bis 24 dient und darüber hinaus zu
> nichts weiter.

Nein, wieso sollte man den Stern weglassen dürfen? Das sind zwei
völlig unterschiedliche Dinge. Beispiel: Wo im Speicher liegt
buffer[3]? Im zweiten Fall liegt es 3 Bytes hinter der Speicherzelle,
auf die buffer zeigt. Im ersten Fall liegt es aber 3*25=75 Bytes
dahinter.

> Allerdings hätte ich doch noch eine Kritik an dem Ausdruck
>
> char (*buffer)[25];
>
> Und zwar ist vor allem Lint nicht mehr möglich zu entscheiden ob eine
> Arrayzugriff auf initilisierte Elemente erfolgt. Siehst Du das anders?

Die statische Code-Analyse stößt bei Zeigerdereferenzierungen oft an
ihre Grenzen, nicht nur in diesem Fall. Sie kann aber auch in
folgendem (zeigerlosen) Beispiel nicht entscheiden, ob in der letzten
Zeile auf initialisierte Daten zugegriffen wird oder nicht:
1
  int array[10];
2
  array[komplizierte_funktion()] = 1;
3
  printf("%d\n", array[5]);
Das Programm ist nur dann in Ordnung, wenn komplizierte_funktion() den
Wert 5 liefert. Ist dies nicht der Fall, gibt printf Müll aus. Um dies
zu prüfen, müsste Lint komplizierte_funktion() probeweise ausführen,
was es aber nicht tut.

Zum Schluss noch ein kleines Testprogramm, das auf praktischem Wege
zeigt, dass
1
  char (*buffer)[25];
schon der richtige Zeigertyp ist, um auf ein 10x25-Array zuzugreifen:
1
#include <stdio.h>
2
3
int main(void) {
4
  char array[10][25];
5
6
  char (*zeiger)[25] = array;            // richtig
7
  // char *zeiger = array;               // falsch
8
  // char (*zeiger)[250] = array;        // falsch
9
  // char (zeiger)[25] = array;          // falsch
10
  // char (*zeiger)[10][25] = array;     // falsch
11
12
  char n;
13
  int i, j;
14
15
  // Intialisierung der Arrayelemente mit eindeutigen Werten
16
  n = 0;
17
  for(i=0; i<10; i++) {
18
    for(j=0; j<25; j++)
19
      array[i][j] = (char)n++; // n läuft über, aber sei's drum ;-)
20
  }
21
22
  // Überprüfung, ob in zeiger[i][j] und in array[i][j] die gleichen
23
  // Werte stehen
24
  for(i=0; i<10; i++) {
25
    for(j=0; j<25; j++) {
26
      if(zeiger[i][j] != array[i][j])
27
        printf("Fehler bei i=%d und j=%d\n", i, j);
28
    }
29
  }
30
  return 0;
31
}

Zu Beginn wird ein 10x25-Array angelegt. Dann wird ein Zeiger
angelegt, der auf den Anfang des Arrays zeigt. Nur wenn der Zeiger vom
richtigen Typ ist, greift zeiger[i][j] auf das gleiche Element wie
array[i][j] zu. Stimmt der Typ nicht, werden Fehlermeldungen
ausgegeben, sofern das Programm überhaupt compiliert wird.

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.