Parameter Typ richtig angeben?
-
Bei der folgenden Funktion scalar_prod möchte ich, dass der Aufrufer einen Zeiger auf ein drei-elementiges float-Array übergeben MUSS:
#include <float.h> #include <stdio.h> float scalar_prod(float (*v1)[3], float (*v2)[3]){ return (*v1)[0] * (*v2)[0] + (*v1)[1] * (*v2)[1] + (*v1)[2] * (*v2)[2]; } int main(){ float vec1[4], vec2[4]; puts("Skalarprodukt berechnen. "); puts("1. Vektor eingeben: "); scanf("%g%g%g", vec1, vec1+1, vec1+2); puts("2. Vektor eingeben: "); scanf("%g%g%g", vec2, vec2+1, vec2+2); printf("Das Skalarprodukt lautet: %g", scalar_prod(&vec1, &vec2)); /* Der Compiler meldet aber hier keinen Fehler, obwohl jeweils ein 4-elementiges Array übergeben wird! */ while(getchar() != '\n') ; getchar(); return 0; }
Leider meldet der Compiler aber keinen Fehler, wenn anstatt eines 3-elementigen Arrays ein 4-elementiges Array übergeben wird. Sogar wenn ich für einen der Parameter die Adresse eines beliebigen float-Zeigers angebe (der womöglich nur auf eine vereinzelte float-Variable und gar kein Array zeigt) kommt kein Compiler-Fehler. Sondern jeweils nur eine Warnung, dass die Anzahl der Array-Elemente bzw. Dereferenzierungen unterschiedlich ist.
Wie muss ich die Parameter der scalar_prod() Funktion definieren, damit der Aufrufer sie in jedem Fall nur mit einem 'Zeiger auf 3-elementiges float-Array'
und nichts anderes aufrufen kann (bzw. dazu gezwungen ist, explizit einen (cast) zu machen, wenn er etwas anderes übergeben möchte)?
?
-
vielleicht so?
float scalar_prod (float *v1, float *v2) { return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; } ... scalar_prod (vec1, vec2)); // <-- und so aufrufen ...
-
Ich möchte in der scalar_prod() Funktion auf Nummer sicher gehen, dass ich beim Indizieren der Parameter nicht unerlaubter Weise auf einen Bereich außerhalb des float[3] Arrays zugreife (auch wenn es nur lesend sein sollte).
Von diesem Funktionskopffloat scalar_prod(float (*vec1)[3], float (*vec2)[3])){
hätte ich mir erhofft, dass der Aufrufer als Parameter nur die Adresse von 3-elementigen float Arrays angeben darf, womit aus der Sicht meiner Funktion sicher gestellt wäre, dass ich innerhalb der Funktion z.B. mit (*vec1)[2] nicht auf einen undefinierten Speicherbereich zugreife, weil ja der Aufrufer dazu gezwungen wäre, auch wirklich nur einen Zeiger auf ein 3-elementiges float-Array zu übergeben, und nichts anderes -> immerhin beanstandet es mein Compiler mit einer Warnung, wenn der Aufrufer einen anderen - aber scheinbar doch kompatiblen - Typ als ((float[3])*) als Aktualparameter der Funktion übergibt.
Mich hätte interessiert, ob es vom ANSI-C Standard her so beabsichtigt ist, dass ein Compiler (float**), ((float[3])) ... einerlei behandelt ?
Also von meinem Verständnis her, ist ein 'Zeiger auf ein Array mit 3 floats' ein anderer Datentyp als ein 'Zeiger auf einen float-Zeiger'. Mir ist es nicht erklärlich, wieso das vom Compiler als ein und dasselbe bzw. nicht streng genug unterschieden wird, um bei der Übersetzung des Quelltextes einen Fehler zu generieren, wenn ein Aufrufer einen Aktualparameter mit einem falschem Datentyp beim Funktionsaufruf angibt. Wie gesagt, es wäre mir schon genug, wenn zumindest der Aufrufer dazu gezwungen wäre, explizit zu casten. Aber scheinbar tut das der Compiler auch implizit. -> Ist das so im C-Standard vorgesehen, dass (float**) und ((float[3])) implizit austauschbare Datentypen sind?
Was kann ich tun, damit ich innerhalb der scalar_prod() Funktion nicht auf einen ungültigen Speicherbereich zugreife, nur weil der Aufrufer z.B. ein zu kleines Array übergeben hat?
-
C_Laie1 schrieb:
Ich möchte in der scalar_prod() Funktion auf Nummer sicher gehen, dass ich beim Indizieren der Parameter nicht unerlaubter Weise auf einen Bereich außerhalb des float[3] Arrays zugreife (auch wenn es nur lesend sein sollte).
mach dir doch ne struct, ungefähr so:
typedef struct vector3D { float x, y, z; } vector3D_t;
-
+fricky schrieb:
mach dir doch ne struct, ungefähr so:
Wenn er die Parameter dann aber effizient als Zeiger übergeben will, steht er wieder vor dem gleichen Problem
-
nochKeinen schrieb:
+fricky schrieb:
mach dir doch ne struct, ungefähr so:
Wenn er die Parameter dann aber effizient als Zeiger übergeben will, steht er wieder vor dem gleichen Problem
er kann ja nen zeiger auf die structs übergeben. aber bei der kleinen struct ist es speedmässig gesehen wahrscheinlich egal, ob sie als kopie oder als zeiger weitergereicht wird.
-
C_Laie1 schrieb:
Ich möchte in der scalar_prod() Funktion auf Nummer sicher gehen, dass ich beim Indizieren der Parameter nicht unerlaubter Weise auf einen Bereich außerhalb ...
Machst du wie schon erwähnt, als Struktur. Oder übergibst du die Anzahl, guckst du:
#include <stdio.h> #define SIZE 3 void some_func (float* a, int n) { if ( n != SIZE ) { puts("Nanana!"); return; } while(--n >= 0) printf("%f ", a[n]); } int main() { float array[3] = {1.0,2.0,3.0}; some_func( array, 4 ); return 0; }
-
+fricky schrieb:
er kann ja nen zeiger auf die structs übergeben. aber bei der kleinen struct ist es speedmässig gesehen wahrscheinlich egal
Klar, bei so kleinen Strukturen ist das egal.
Aber das Hilfskonstrukut in diesem Fall ändert nichts daran, dass Compiler alles pointerartige als Parameter schlucken, wenn der Parameter der Funktion erst mal als Pointer deklariert wurde.
Pointer auf Objekte können nämlich zu Pointer auf anderen Objekten konvertiert werden - auch wenn das häufig totaler Unsinn ist.
-
nochKeinen schrieb:
...ändert nichts daran, dass Compiler alles pointerartige als Parameter schlucken, wenn der Parameter der Funktion erst mal als Pointer deklariert wurde.
dazu musste aber schon mit void* ankommen.
nochKeinen schrieb:
Pointer auf Objekte können nämlich zu Pointer auf anderen Objekten konvertiert werden - auch wenn das häufig totaler Unsinn ist.
klar, aber nur mit absicht. so'n bisschen typüberprüfung macht C ja auch.