Forum: PC-Programmierung Grösse eines Array of pointers


von Reiner W. (reiner_w)


Lesenswert?

Ich stehe hier mit meinen wackligen C-Kenntnissen total auf dem 
Schlauch.

Ich habe ein int array mit 2 Pointern, die auf jeweils ein int Array 
unterschiedlicher Größe zeigen.
1
#include <stdio.h>
2
3
int main() {
4
    int t1[4] = {0, 1, 2, 3};  //4 int array 0,1,2,3
5
    int t2[3] = {4, 5, 6};     //3 int array 4,5,6
6
    int *tab[2] = {t1, t2};    //2 pointer of integers array address of the first element of each array
7
 
8
    int i,j ; 
9
    
10
    int len_tab = 2; // how can i determine the length of tab[]
11
    for (i = 0; i < len_tab; i++) {
12
        printf("%d\t", *tab[i]);  //int that is pointed to by the i-th pointer in the tab array t1[0], t2[0]
13
    }
14
    
15
    printf("\n\n");
16
  
17
    for (i = 0; i < len_tab; i++) {
18
        int lenght_ti = sizeof(*tab[i]) / sizeof(int);  // do'nt work correct
19
        for (j = 0; j < lenght_ti; j++) {
20
            printf("%d\t", *(tab[i] + j));
21
        }
22
        printf("\n");
23
    }
24
    return 0;
25
}

Das Problem ist, dass sowohl das Pointer-Array tab als auch die Arrays 
ti unterschiedliche Größe haben können.
Ich muss also zur Laufzeit die Größe von tab als auch die inneren Arrays 
ti ermitteln können.
Für die inneren Arrays ti würde es ja z.B. mit
1
sizeof(t1) / sizeof(int)
gehen. Aber ich habe ja nur den Pointer auf diese Arrays.

Kann mir jemand sagen, ob und wie ich das machen kann?

Vielen dank schon mal.

Reiner

Nachtrag
Ok, für das äußere Array tab hab ich's gefunden
1
sizeof(tab) / sizeof(int *)

Bleiben noch die inneren arrays

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

Bei Pointer-Arrays nimmt man "0-terminierte-Listen": Die Liste schließt 
mit 0 ab. Ähnlich wie bei Strings.
1
    int *tab[] = {t1, t2, 0}; // Dein erstes Beispiel geändert 
2
//    int i,j ; 
3
//    int len_tab = 2; // how can i determine the length of tab[]
4
    int **pl;
5
    for(pl = tab; pl; pl++) {
6
        printf("%d\t", **pl); 
7
    }
*pl ist am Anfang t1, dann t2, dann hört die for-Schleife auf (weil pl 0 
ist)
**pl ist dann der Zahlenwert der in t1 (und danach t2) als erster steht.

Bei int-Arrays geht das leider nicht so einfach, da muss man sich, wenn 
0 vorkommen darf, irgendwas überlegen (-1, MIN_INT, ein zusätzliches 
Feld, der erste Wert, ein paralleles Array, ...)

von A. S. (Gast)


Lesenswert?

Reiner W. schrieb:
> Ok, für das äußere Array tab hab ich's gefunden
> sizeof(tab) / sizeof(int *)

das ist unschön, da man diese Zeile bei Änderung des Typs nachziehen 
muss.

Besser ist sizeof(tab)/sizeof(tab[0])

meist als countof oder _countof-makro vorhanden. Fällt aber natürlich 
bei reinen pointern auf die Nase.

von Reiner W. (reiner_w)


Lesenswert?

A. S. schrieb:
> Besser ist sizeof(tab)/sizeof(tab[0])

Vielen Dank. Ja, das ist besser. Gibt es noch eine Möglichkeit, die 
Größen der inneren arrays festzustellen, oder geht das nicht (wie bei 
der Funktionsübergabe von array-pointer)?

von Reiner W. (reiner_w)


Lesenswert?

A. S. schrieb:
> Bei Pointer-Arrays nimmt man "0-terminierte-Listen": Die Liste schließt
> mit 0 ab. Ähnlich wie bei Strings.

Danke für den Tip, auch wenn der nicht direkt mein Problem mit den 
inneren Arrays löst.

von A. S. (Gast)


Lesenswert?

Und hier noch mit -1 terminierten Listen
1
int main() {
2
    int t1[4] = {0, 1, 2, 3, -1};  
3
    int t2[3] = {4, 5, 6, -1};
4
    int *tab[2] = {t1, t2, 0};
5
    int **pl;   
6
    
7
    printf("\n\n");
8
  
9
    for(pl=tab; *pl; pl++) {
10
        for (int *p = *pl; *p!=-1; p++) {
11
            printf("%d\t", *p));
12
        }
13
        printf("\n");
14
    }
15
    return 0;
16
}
ist natürlich nicht schön, dass -1 hier hardcodiert ist.

(im ersten Post muss es (wie hier) *pl in der Mitte der for-Schleife 
sein)

von Reiner W. (reiner_w)


Lesenswert?

A. S. schrieb:
> Und hier noch mit -1 terminierten Listen

Ja, falls ich keine Möglichkeit finde, die Größe der inneren Arrays 
festzustellen, kann ich damit leben.

Danke für deine Mühe

von Rolf M. (rmagnus)


Lesenswert?

Reiner W. schrieb:
> A. S. schrieb:
>> Besser ist sizeof(tab)/sizeof(tab[0])
>
> Vielen Dank. Ja, das ist besser. Gibt es noch eine Möglichkeit, die
> Größen der inneren arrays festzustellen, oder geht das nicht (wie bei
> der Funktionsübergabe von array-pointer)?

Es geht nicht. Letztendlich hast du nur einen Zeiger auf einen int und 
nicht mehr. Ob da noch weitere ints folgen und wie viele, weiß der 
Zeiger nicht. Die beiden Möglichkeiten, mit denen man normalerweise 
arbeitet, sind die schon erwähnte Nutzung eines sonst nicht verwendeten 
Werts als Terminierung oder die Größe explizit mitzugeben.

von Reiner W. (reiner_w)


Lesenswert?

Rolf M. schrieb:
> Es geht nicht. Letztendlich hast du nur einen Zeiger auf einen int und
> nicht mehr.

Ja, das habe ich mir gedacht. Ist ja bei der Übergabe an Funktionen 
genauso.
Mit dem Tip von A.S. kann ich gut leben.

Noch ne Frage, Was macht mehr Sinn, ein 0-terminiertes Pointer-array
zu verwenden, oder die Größe bestimmen?
Ein absichtlicher 0-Pointer sollte wohl ausgeschlossen sein.

von A. S. (Gast)


Lesenswert?

Reiner W. schrieb:
> Noch ne Frage, Was macht mehr Sinn, ein 0-terminiertes Pointer-array
> zu verwenden, oder die Größe bestimmen?
> Ein absichtlicher 0-Pointer sollte wohl ausgeschlossen sein.

0 ist die Konvention für einen ungültigen Pointer. Er sollte daher im 
normalen Programm nicht vorkommen, auch wenn das im embedded-Bereich 
sein kann.

Wenn Du (bzw. der Compiler) die Größe nicht kennst, sind 
0-Terminierungen das einfachste ("kein" Vergleich mit irgendwas 
notwendig, er ist oder ist nicht)

Wenn Du die Größe kennst, kann 0-Terminierung manchmal trotzdem sinnvoll 
sein, da der Code wie im Beispiel deutlich kleiner wird.

Andererseits sind 0-Terminierungen sehr gefährlich: wenn jemand die 0 
vergisst, ist ein Absturz "vorprogrammiert".

von Reiner W. (reiner_w)


Lesenswert?

@A.S. Danke, für die Hinweise.
Tatsächlich können die Array-Werte im realen Programm auch eingegeben 
werden. Da kann muss ich die Terminierung ohnehin per Programm 
sicherstellen. Sollte also sicher sein.

von cppbert3 (Gast)


Lesenswert?

Mach einen struct array_slice mit int* und size und nutze den als Typ 
für dein tab Array

auch wenn du ein array als Parameter an eine Funktion uebergibst 
brauchst du meistens so einen Typ (egal ob als 2 Paramerter mit int* und 
size, oder eben diesen Slice Typ den du dann durchgehend verwenden 
kannst)

steigert nur die visuelle Komplexität ist aber sauber

von Dirk B. (dirkb2)


Lesenswert?

Welchen Wert du als Terminator benutzt, hängt ja auch von der Anwendung 
ab.
Dies kann auch ein Maximalwert sein zB. INT_MIN bei int.
Beim 2er Komplement gibt es dazu eh kein positives Gegenstück.

Oder bei unsigned UINT_MAX. Denn der ist meist weit, weit weg.

von Reiner W. (reiner_w)


Lesenswert?

cppbert3 schrieb:
> Mach einen struct array_slice mit int* und size und nutze den als Typ
> für dein tab Array

Ja, auch ein sehr guter Ansatz. Danke dafür.

Dirk B. schrieb:
> Welchen Wert du als Terminator benutzt, hängt ja auch von der Anwendung
> ab.

Ja, tatsächlich verwende ich in der realen App an dieser Stelle nur CHAR 
(PS2 Emulator auf STM mit ein paar Sonderfunktionen). Da kann ich alles 
0-terminiert machen.

Herzlichen Dank nochmal an Alle für die schnelle und äußerst kompetente 
Hilfe - soviel OT ist hoffentlich erlaubt.

von fop (Gast)


Lesenswert?

Ich denke, ich würde aus "tab" ein Array von Structs machen, wobei jedes 
Struct einen Pointer und einen unsigned Integer enthält. Der Pointer 
zeigt auf das erste Element des anderen Arrays und der Integer gibt die 
Länge des anderen Arrays in Elementen an.
Dann noch fix ein Typedef für den Typ des Structs gehäkelt, damit man 
weniger Tipparbeit hat, wenn man es an eine Funktion übergibt.
Und zur Krönung noch ein Define, dem man einen Arraynamen mitgibt, dass 
einen Initialisierer für ein Struct ausgibt.

von Reiner W. (reiner_w)


Lesenswert?

fop schrieb:
> Ich denke, ich würde aus "tab" ein Array von Structs machen

Habs jetzt schon 0-terminiert. Aber für kommende Fälle schon mal fest im 
Kopf.
Wenn ich noch eine Frage (im Zusammenhang) nachschieben darf:

Sind diese Schreibweise voll äquivalent? oder gibt es irgend welche 
Tücken bei Verwendung von 2.?

1.
1
sizeof(tab)/sizeof(tab[0])

2.
1
sizeof(tab)/sizeof(*tab)

Ich hab mir so ein define gebastelt.
1
#define  LEN_STR_ARR(A) ((sizeof(A)/sizeof(*A))-1)

Läuft zwar, aber hoffentlich fällt mir das nicht irgendwo auf die Füße. 
Wie gesagt, bin leider selten mit C unterwegs.

von Rolf M. (rmagnus)


Lesenswert?

Reiner W. schrieb:
> Sind diese Schreibweise voll äquivalent? oder gibt es irgend welche
> Tücken bei Verwendung von 2.?
>
> 1.sizeof(tab)/sizeof(tab[0])
> 2.sizeof(tab)/sizeof(*tab)
> Ich hab mir so ein define gebastelt.

Die Schreibweisen sind 100% äquivalent, was sich daraus ergibt, dass 
a[b] eine vereinfachte Schreibweise für *(a + b) ist. Man könnte daher 
sogar 0[tab] schreiben; auch das wäre gleichwertig. Die Klammern kann 
man übrigens noch weglassen.

> #define  LEN_STR_ARR(A) ((sizeof(A)/sizeof(*A))-1)
> Läuft zwar, aber hoffentlich fällt mir das nicht irgendwo auf die Füße.
> Wie gesagt, bin leider selten mit C unterwegs.

Man muss halt bedenken, dass das nur direkt mit Arrays funktioniert, 
nicht mit Zeigern. Wenn man es mit einem Zeiger nutzt, kommt nicht der 
gewünschte Wert heraus, und es gibt keine Fehlermeldung oder Warnung.

von Reiner W. (reiner_w)


Lesenswert?

Rolf M. schrieb:
> a[b] eine vereinfachte Schreibweise für *(a + b)

Danke, so hatte ich noch gar nicht betrachtet.

Rolf M. schrieb:
> dass das nur direkt mit Arrays funktioniert

Ich hoffe, dass der Name da wenigsten eindeutig ist ;-)

Rolf M. schrieb:
> Die Klammern kann
> man übrigens noch weglassen.

Soll sich der Compiler drum kümmern ...

Schönen Sonntag noch

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

Reiner W. schrieb:
> Ich hab mir so ein define gebastelt.
> #define  LEN_STR_ARR(A) ((sizeof(A)/sizeof(*A))-1)

mach sowas nicht. Das fällt Dir als "Zaunlattenproblem" definitiv 
irgendwann auf die Füße.

Entweder 0-Terminiert, dann braucht es kein LEN_STR_ARR, oder ohne 
Extra-Element, dann tut es das _countof oder countof ohne "-1".

Reiner W. schrieb:
> Rolf M. schrieb:
>> dass das nur direkt mit Arrays funktioniert
>
> Ich hoffe, dass der Name da wenigsten eindeutig ist ;-)

Ja, nur kann das ganze auch z.B. 0 werden (wenn A als Ptr auf eine 
Struktur zeigt, die größer ist als ein Ptr).

Mit der A[0]-Form hast Du manchmal die Chance, dass Dich Dein Compiler 
warnt, wenn Du einen Ptr erwischst.

von Dirk B. (dirkb2)


Lesenswert?

A. S. schrieb:
> Mit der A[0]-Form hast Du manchmal die Chance, dass Dich Dein Compiler
> warnt, wenn Du einen Ptr erwischst.

Mit welcher Warnoption soll das denn funktionieren?

Wie schon erwähnt, sind a[b] und *(a+b) (und demnach auch b[a]) 
identisch.

von Reiner W. (reiner_w)


Lesenswert?

A. S. schrieb:
> Das fällt Dir als "Zaunlattenproblem" definitiv
> irgendwann auf die Füße.

Danke, genau sowas wollte ich wissen. Und in der Tat, da ich eh schon 
0-terminiert habe, kann ich darauf verzichten.

von fop (Gast)


Lesenswert?

Dirk B. schrieb:
> Wie schon erwähnt, sind a[b] und *(a+b) (und demnach auch b[a])
> identisch.

Sicher ? Meines Wissens können Pointer und Arrays in C auch einen Typ 
haben, auf den sie zeigen. Es wird dann nicht um entsprechede Adressen 
im Speicher hochgezählt, sondern um den Speicherplatz, den der Typ 
benötigt.

Nein, es kommt in der Tat keine Warnung vom GCC, sondern ein Fehler : 
Array subscript is not an integer.

von Rolf M. (rmagnus)


Lesenswert?

fop schrieb:
> Dirk B. schrieb:
>> Wie schon erwähnt, sind a[b] und *(a+b) (und demnach auch b[a])
>> identisch.
>
> Sicher ?

Ja. Steht auch so im C-Standard:

"The definition of the subscript operator [] is that E1[E2] is identical 
to (*((E1)+(E2)))."

Es gibt kaum etwas, das dort so klar und unmissverständlich formuliert 
ist, wie das ;-)

> Meines Wissens können Pointer und Arrays in C auch einen Typ haben, auf
> den sie zeigen.

Den haben sie immer. Bei Pointern kann das allerdings auch void sein, 
welches keine Größe hat. In diesem Fall kann man daher auch keine 
Pointer-Arithmetik damit machen und kein * oder [] darauf anwenden.

> Es wird dann nicht um entsprechede Adressen im Speicher hochgezählt,
> sondern um den Speicherplatz, den der Typ benötigt.

Ja, und? Das wirkt sich gleichermaßen auf beide Varianten aus.

> Nein, es kommt in der Tat keine Warnung vom GCC, sondern ein Fehler :
> Array subscript is not an integer.

Bei was?

: Bearbeitet durch User
von fop (Gast)


Lesenswert?

Mein Versuchsobjekt :
1
#include <stdint.h>
2
volatile uint8_t a[200];
3
volatile uint32_t b[200];
4
5
void nanu(void)
6
{
7
    a[0] = 11U;
8
    b[0] = 17U;
9
    a[b] = 42U;
10
}
11
12
void nana(void)
13
{
14
    a[0] = 11U;
15
    b[0] = 17U;
16
    b[a] = 42U;
17
}

Eigentlich wollte ich ja nur meine Vermutung überprüfen, dass die 
Ausdrücke nur gleich sind, wenn die Arrays bzw. Pointer auf den gleichen 
Typ gehen.

Und zum Thema "Pointer müssen einen Typ haben, auf den sie zeigen" : es 
gäbe da ja noch die Pointer auf void, was ja kein Typ im streng 
katholischen Sinne ist.

von Rolf M. (rmagnus)


Lesenswert?

fop schrieb:
> a[b] = 42U;

Das geht natürlich nicht, aber da würde ein
1
*(a + b) = 42U;
genauso fehlschlagen, da man zwei Pointer nicht addieren kann. Man kann 
zu einem Pointer nur Integer addieren, also muss einer er beiden 
Operanden auch ein solcher sein.

Wenn du aber schreibst:
1
int x = 5;
2
x[a] = 42U;
dann wird es gehen und den selben Effekt haben wie
1
a[x] = 42U;
und wie
1
*(a+x) = 42U;

fop schrieb:
> Und zum Thema "Pointer müssen einen Typ haben, auf den sie zeigen" : es
> gäbe da ja noch die Pointer auf void, was ja kein Typ im streng
> katholischen Sinne ist.

Das hatte ich noch dazugeschrieben. void ist schon ein Typ, aber ein so 
genannter unvollständiger Typ, so wie es z.B. auch eine deklarierte, 
aber nicht definierte struct wäre.

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

Bei a[b] und *(a+b) ist a ein Pointer-Typ und b ein Integraler-Typ (oder 
umgekehrt)

Zwei Pointer kann man nicht addieren (was soll da raus kommen).

fop schrieb:
> Und zum Thema "Pointer müssen einen Typ haben, auf den sie zeigen" : es
> gäbe da ja noch die Pointer auf void, was ja kein Typ im streng
> katholischen Sinne ist.

Da funktioniert aber keine Pointer-Arithmetik, da diese die Größe des 
Typs berücksichtigt.

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.