So oder so verflacht Dein Array bei der Übergabe zu einem Pointer, d.h.
Du hast keine Längeninformation. Die muß als zweiter Parameter übergeben
werden. Bei dem sizeof zur Längenbestimmung muß die Definition des
Arrays im sichtbaren Scope sein, was hier der Fall ist, weil das Array
global ist.
Zuerst mache ich immer eine typedef
typedef struct
{
int Temperature;
double Position;
} Combityp;
Dann kann ich danach mit dem Combityp arbeiten. Das macht die Sache
übersichtlicher.
Combityp array[17];
int function1 (Combityp* sa) { ... };
Und die Doppel-Sterne sind da überflüssig.
Peter P. schrieb:> aber> beantwortet nicht die eigentliche Frage.
Die hat Nop doch schon beantwortet.
Schreib dir auf, was STRUCTNAME für ein typ ist, was arr für ein Typ
ist, und was du als Parameter für function1 übergeben willst. Wenn du es
nicht hinbekommst, mach das gleiche mit einem int stat dem struct. Dann
verstehst du es auch.
Der Tipp mit dem typedef ist aber trotzdem bedenkenswert.
Oliver
Peter P. schrieb:> ich möchte eine Referenz auf ein Struct Array in einer Funktion einer> anderen Funktion übergeben.
Dein Problem ist ein anderes:
&arr gibt es nicht!
arr (oder &arr{0]) ist das, was Du meinst. Ein Ptr auf eine liste (hier
8, aber egal) von Strukturen.
Deine Funktion erwartet den Ptr auf einen Ptr. Entweder also
1
intf1(structSTRUCTNAME*sa){...};// nur ein * statt **
oder extra einen Ptr anlegen, der auf arr zeigt und damit aufrufen
1
structSTRUCTNAME*p=arr;
2
3
function1(&p);
Wenn Du wirklich 8 Ptr haben wolltest (also 8 Ptr nacheinander in einer
Liste, dann hätte es
A. S. schrieb:> &arr gibt es nicht!
Doch, gibt es schon. Das ist ein Pointer auf ein Array.
> arr (oder &arr{0]) ist das, was Du meinst. Ein Ptr auf eine liste (hier> 8, aber egal) von Strukturen.
Das ist ein Pointer auf das erste Element des Arrays.
Yalu X. schrieb:> Doch, gibt es schon. Das ist ein Pointer auf ein Array.
in diesem Kontext
Peter P. schrieb:> struct STRUCTNAME arr[8];> int function2 () {> function1 (&arr);
sind arr und &arr für den Compiler (*) idntisch.
Bei anderen Definition von arr ist es ggf. anders.
(* Wenn er es erlaubt. Ob der Compiler das erlauben muss, weiß ich
nicht. Bisher taten sie es bei mir mit entsprechender Warnung)
A. S. schrieb:> in diesem Kontext> Peter P. schrieb:>>> struct STRUCTNAME arr[8];>> int function2 () {>> function1 (&arr);>> sind arr und &arr für den Compiler (*) idntisch.
Beide sind zwar numerisch gleich (gleiche Speicheradressen), haben aber
unterschiedliche Datentypen, die nicht implizit ineinander konvertierbar
sind. Deswegen führt (je nach Deklaration von function1) mindestens
eines von beiden zu einer Fehlermeldung.
Yalu X. schrieb:> Deswegen führt (je nach Deklaration von function1) mindestens> eines von beiden zu einer Fehlermeldung.
OK. Ich wette eine Kiste Bier dagegen.
Beide (arr und &arr) sind bei Deinem meistgenutzten Compiler und
gegebener Definition von arr gleichwertig. Wobei &arr natürlich eine
Warnung wirft, weil es das strenggenommen nicht gibt. Und natürlich
unterscheiden die sich, wenn z.B. ein Feld angegeben wird, dann ist
"arr[0]" natürlich etwas anderes als "&arr[0]".
Probier es aus, wenn es einen Unterschied gibt, schicke ich eine Kiste
rüber.
A. S. schrieb:> Yalu X. schrieb:>> Deswegen führt (je nach Deklaration von function1) mindestens>> eines von beiden zu einer Fehlermeldung.>> OK. Ich wette eine Kiste Bier dagegen.
Bei mir führt es in C zu einer Warnung und in C++ zu einem Fehler, weil
der Typ falsch ist, genauso wie z.B.
1
voidfunc(float*f);
2
3
intmain()
4
{
5
inti;
6
func(&i);
7
}
> Beide (arr und &arr) sind bei Deinem meistgenutzten Compiler und> gegebener Definition von arr gleichwertig.
Nein.
> Wobei &arr natürlich eine Warnung wirft, weil es das strenggenommen nicht> gibt.
Natürlich gibt es das. Es ist die Adresse des Arrays.
> Und natürlich unterscheiden die sich, wenn z.B. ein Feld angegeben wird,> dann ist "arr[0]" natürlich etwas anderes als "&arr[0]".
Damit sagst du ja schon selber, dass arr und &arr eben nicht
gleichwertig sind. Und das liegt genau daran, dass der Typ
unterschiedlich ist.
Rolf M. schrieb:> Nein.
Sorry, ich bezog mich nur auf C. Bei C++ magst Du Recht haben. Bei C
schicke ich Dir auch gerne eine Kiste wenn ich unrecht habe.
A. S. schrieb:> Rolf M. schrieb:>> Nein.>> Sorry, ich bezog mich nur auf C. Bei C++ magst Du Recht haben. Bei C> schicke ich Dir auch gerne eine Kiste wenn ich unrecht habe.
Der Unterschied zwischen C und C++ ist hier nur, dass es beim einen nur
eine Warnung, beim anderen eine Fehlermeldung gibt. Ansonsten ist das
Problem das selbe.
Rolf M. schrieb:> Ansonsten ist das Problem das selbe.
Probier es aus. Dein Compiler wird es trotzdem "richtig" machen, also
gleich. Wenn nicht, sag wo ich das 🍺 hinschicken soll.
Rolf M. schrieb:>> Wobei &arr natürlich eine Warnung wirft, weil es das strenggenommen nicht>> gibt.>> Natürlich gibt es das. Es ist die Adresse des Arrays.
Das wird von den meisten Compilern so umgesetzt, der Grund hat sich mir
aber noch nicht erschlossen.
1
myStruct_t arr_1[4];
2
myStruct_t arr_2[4];
3
myStruct_t* poi = arr_1;
Pointer:
Beim Pointer hast du zwei Adressen, einmal die Adresse auf die der
Pointer zeigt und einmal die Adresse des Pointers selbst (also die
Speicherstelle an der der Pointer liegt).
Array:
Bei einem Array hast du das nicht, denn "arr_1" ist lediglich ein
Identifier der für einen Speicherbereich steht. Du kannst zwar auf
diesen Speicherbereich lesend/schreibend zugreifen aber du kannst dem
Identifier keinen neuen Wert zuweisen:
1
arr_1 = arr_2; // Compilerfehler
Zumindest beim gcc führen "arr_1" und "&arr_1" jeweils zur gleichen
Ausgabe, also der Rückgabe der Adresse an der das Array liegt. Den Sinn
von "&arr_1" habe ich aber ehrlich gesagt nicht so ganz verstanden.
Meiner Meinung nach ist "arr_1" lediglich ein Identifier der auf einen
Speicherbereich verweist, keine separate Variable (wie beispielsweise
ein Pointer) die im Speicher liegt und auf deren Adresse man zugreifen
könnte.
A. S. schrieb:> Yalu X. schrieb:>> Deswegen führt (je nach Deklaration von function1) mindestens>> eines von beiden zu einer Fehlermeldung.>> OK. Ich wette eine Kiste Bier dagegen.
Ok, ich habe das vorher nicht ausprobiert. Beim GCC und Clang im C-Modus
kommt mit den Defaultoptionen tatsächlich nur eine Warnung der Art
"passing argument from incompatible pointer type"
Trotzdem ist die Übergabe eines Pointers mit inkompatiblem Typ ganz klar
ein Fehler (s. ISO 9899:2017, Abschnitte 6.5.2.2 und 6.5.16.1, jeweils
unter "Constraints"), den der Compiler melden muss (5.1.1.3). Ihm ist es
allerdings freigestellt, in welcher Form dies geschieht, weswegen auch
eine Warnung die Norm erfüllt.
Mit der Option -pedantic-errors (die für standardkonforme Programmierung
immer aktiv sein sollte) werden alle Fehler, für die der Standard eine
Meldung vorschreibt, als Error statt nur einer Warning ausgegeben, so
auch in diesem Fall.
Die defaultmäßige Ausgabe einer Warning statt eines Errors beim GCC und
Clang geschieht vermutlich aus Rücksichtnahme auf Legacy-Code. Im
C++-Modus entfällt dieser Grund, weswegen dort immer ein Error
ausgegeben wird.
A. S. schrieb:> Probier es aus. Dein Compiler wird es trotzdem "richtig" machen
Ja, das tut er deswegen, weil – wie ich oben schon schrieb – mit arr und
&arr der gleiche numerische Wert an die Funktion übergeben wird,
weswegen die Funktion den Unterschied gar nicht mitbekommt.
Der Unterschied zwischen arr und &arr wird aber in folgendem Beispiel
deutlich:
Holger schrieb:> Das wird von den meisten Compilern so umgesetzt, der Grund hat sich mir> aber noch nicht erschlossen.
Der Grund ist, dass es im Standard so vorgeschrieben ist.
> myStruct_t arr_1[4];> myStruct_t arr_2[4];> myStruct_t* poi = arr_1;>> Pointer:> Beim Pointer hast du zwei Adressen, einmal die Adresse auf die der> Pointer zeigt und einmal die Adresse des Pointers selbst (also die> Speicherstelle an der der Pointer liegt).>> Array:> Bei einem Array hast du das nicht,
Doch, hast du auch.
> denn "arr_1" ist lediglich ein Identifier der für einen Speicherbereich> steht.
Das gilt aber für jede Variable.
> Du kannst zwar auf diesen Speicherbereich lesend/schreibend> zugreifen aber du kannst dem Identifier keinen neuen Wert zuweisen:
Das liegt daran, dass ein Array ein so genannter "rvalue" ist. Das r
soll sagen, dass es nur auf der rechten Seite einer Zuweisung erlaubt
ist.
> arr_1 = arr_2; // Compilerfehler>> Zumindest beim gcc führen "arr_1" und "&arr_1" jeweils zur gleichen> Ausgabe, also der Rückgabe der Adresse an der das Array liegt.
Es ist aber logisch etwas anderes. Das eine ist die Adresse des ersten
Elements, das andere die Adresse des Arrays selbst. Während beide auf
die selbe Speicherstelle verweisen, ist ihr Typ unterschiedlich. Wenn du
sowas hast wie
1
structX
2
{
3
inta;
4
intb;
5
};
6
7
structXx;
dann verweisen &x und &x.a auch auf die selbe Speicherstelle, sind aber
verschiedene Dinge. Bei einem Array ist das auch so. Bei
1
intarr[10];
zeigen &arr und &arr[0] auch auf die selbe Speicherstelle, sind aber von
unterschiedlichem Typ. Nur gibt es dort einen Mechanismus, durch den arr
fast überall als verkürzte Schreibweise für &arr[0] verwendet werden
kann.
> Den Sinn von "&arr_1" habe ich aber ehrlich gesagt nicht so ganz> verstanden.
Das ist das selbe wie bei jedem anderen Typ. Man bekommt einen Zeiger
auf das entsprechende Objekt.
1
inti;
2
int*p1=&i;//Adresse des Integers i vom Typ Zeiger auf int
3
4
voidf(void);
5
void(*p2)(void)=&f;// Adresse des Funktion f vom Typ Zeiger
6
// auf Funktion ohne Parameter und Returnwert
7
8
inta[10];
9
int(*p3)[10]=&a;// Adresse des Arrays a vom Typ Zeiger auf Array
10
// aus 10 int. Man kann über diesen Zeiger durch
11
// Dereferenzierug auf das Array zugreifen
12
(*p3)[3]=5;
13
14
int*p4=&a[0];// Zeiger auf das erste Element des Arrays vom
15
// Typ Zeiger auf int
16
17
int*p5=a;// Äquivalent zu p4
> Meiner Meinung nach ist "arr_1" lediglich ein Identifier der auf einen> Speicherbereich verweist, keine separate Variable (wie beispielsweise> ein Pointer) die im Speicher liegt und auf deren Adresse man zugreifen> könnte.
Nein, es ist wie bei jedem anderen Typ auch einfach der Name der
Variablen. Es gibt lediglich in der Anwendung ein paar Besonderheiten.
1
#include<stdio.h>
2
typedefcharmy_array[10];
3
my_arrayarr;
4
5
intmain()
6
{
7
char*p1=arr;// An dieser Stelle äquivalent zu &arr[0]
8
my_array*p2=&arr;
9
printf("Größe von arr: %zd\n",sizeofarr);
10
// bei sizeof ist &arr[0] nicht äquivalent zu arr
11
printf("Größe von &arr[0]: %zd\n",sizeof&arr[0]);
12
printf("Größe von dem, worauf p1 zeigt: %zd\n",sizeof*p1);
13
printf("Größe von dem, worauf p2 zeigt: %zd\n",sizeof*p2);
Yalu X. schrieb:> Der Unterschied zwischen arr und &arr wird aber in folgendem Beispiel> deutlich:
Ja, vielen Dank. And Dich und Rolf. Ich bin froh, dass ich die Kiste
Bier nur auf den Effekt und nicht auf die zugrunde liegende, natürlich
abstruse Erklärung von mir ausgelobt habe ;-)
PittyJ schrieb:> Zuerst mache ich immer eine typedef
na klasse! und was versprichst du dir davon?
Gewöhnlichermaßen wird ein Struct so definiert:
1
structCombityp
2
{
3
intTemperature;
4
doublePosition;
5
};
Du hast ganz vergessen, daß 'typedef' nicht zum Definieren eines Typs
gedach ist, sondern lediglich zum Erzeugen von sowas wie einem Alias.
Dehalb sieht dein Beispiel abstrakt etwa so aus:
1
typedefNamenloserStructNeuerName;
Hierbei ist der namenlose Ausgangs-Struct-Typ im Programm nicht
benutzbar, weil er zwar definiert ist, aber keinen Namen hat. Von daher
kann man ihn nur über seinen Alias 'NeuerName' benutzen.
W.S.
W.S. schrieb:> PittyJ schrieb:>> Zuerst mache ich immer eine typedef>> na klasse! und was versprichst du dir davon?
Er verspricht sich davon vermutlich, bei der Nutzung des Typs das
Wörtchen "struct" nicht davor schreiben zu müssen.
> Hierbei ist der namenlose Ausgangs-Struct-Typ im Programm nicht> benutzbar, weil er zwar definiert ist, aber keinen Namen hat. Von daher> kann man ihn nur über seinen Alias 'NeuerName' benutzen.
Und warum ist das ein Problem?
Man könnte statt deinem Beispiel auch das schreiben:
1
typedefstructCombityp
2
{
3
intTemperature;
4
doublePosition;
5
}Combityp;
oder entzerrt
1
structCombityp
2
{
3
intTemperature;
4
doublePosition;
5
};
6
typedefstructCombitypCombityp;
Wäre das dann besser, auch wenn man es nachher nie unter seinem Namen
"struct Combityp", sondern nur unter dem Alias "Combityp" anspricht?
Ich nutze es zwar normalerweise auch ohne Typedef, weil ich es dann
klarer finde, als das "struct" hinter einem Typedef zu verstecken, aber
da sind die Geschmäcker unterschiedlich. Wenn man aber schon den Typedef
hat, wozu soll dann ein zusätzlicher nie genutzter Name für die struct
selbst gut sein?
Ich nutze übrigens manchmal sogar Struct-Typen, die weder einen Namen,
noch ein Alias haben, um eine einfache Möglichkeit zu haben, Daten in
einer größeren struct zu gruppieren:
Rolf M. schrieb:> Und warum ist das ein Problem?> Man könnte statt deinem Beispiel auch das schreiben:
Ja, das ist erlaubt, aber unschön.
Vor allem Anfänger sind immer verwirrt, wenn derselbe Bezeichner für
zwei verschieden Dinge benutzt wird.
Das führt dann dazu, dass sich einbrennt: "Ich muss den gleichen Namen
hier und hier hinschreiben" und die Unterschiede überhaupt nicht mehr
klar werden.
Die Nutzung um nur ein "struct" zu sparen verhindert m.E. gerade die
stärken von typedef: Elementare Felder zentral ändern zu können (z.B.
ein tCount als uint8_t, int oder unsigned long oder ..) oder zentrale
Typen (mit vielen Klammern und Pointern) zu vereinfachen.
Stattdessen Voodoo damit 2 Strukturen wechselseitig auf sich zeigen
können. Irgendwas seit Urzeiten kopiertes, vom Gründervater der
Abteilung ausgeklügeltes mit 6 typedefs, statt einfach