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
intmain(){
4
intt1[4]={0,1,2,3};//4 int array 0,1,2,3
5
intt2[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
inti,j;
9
10
intlen_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
intlenght_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
return0;
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
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, ...)
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.
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)?
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.
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
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.
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.
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".
@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.
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
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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?
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.
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
intx=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.
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.