2009-05-11 2 views
1

Der folgende Code warnt vor inkompatiblen Typen. Was ist der richtige Weg, um diesen Code zu lösen?Dynamisches Array von Strukturen und Funktionsaufruf f (const struct_type * const data [])

dank

typedef struct a_struct struct_type; 

void f(const struct_type *const data[], unsigned n); 

void test(unsigned n) 
{ 
    struct_type **v; 

    assert(n); 
    v = malloc(n * sizeof(struct_type *)); 
    /* fill v with new struct_type (with a malloc per entry) */ 
    f(v, n); <- WARN on v 
} 
+2

In welcher Zeile wird die Warnung angezeigt? – LeopardSkinPillBoxHat

Antwort

4

Der Grund für die Beschwerde des Compilers ist die erste const in f Erklärung.

Versuchen Sie es mit

void f(struct_type *const data[], unsigned n); 
/*...*/ 
f(v, n); 

und Sie werden nicht die gleiche Warnung. Alternativ können Sie v werfen, wenn Sie f

void f(const struct_type *const data[], unsigned n); 
/*...*/ 
f((const struct_type * const *) v, n); 

nennen Dies ist ein wenig eingängig, aber in C, man kann nicht eine pointer-to-pointer-to-nonconst für eine pointer-to-pointer-to-const passieren. Sie haben eine spezielle Ausnahme gemacht, damit Sie eine pointer-to-nonconst für eine pointer-to-const übergeben können.

Hier ist eine FAQ Frage "Why can't I pass a char ** to a function which expects a const char **?":

Sie können ein verwenden pointer-to-T (für jede Art T), wo ein pointer-to-const-T erwartet wird. Die Regel (eine explizite Ausnahme), die geringfügige Fehlanpassungen in qualifizierten Zeigertypen zulässt, wird jedoch nicht rekursiv angewendet, sondern nur auf der obersten Ebene. (const char ** ist pointer-to-pointer-to-const-char, und die Ausnahme daher nicht gilt.)

Der Grund, dass Sie einen char ** Wert auf einen const char ** Zeiger nicht zuordnen kann etwas unklar ist. Da der const Qualifier überhaupt existiert, möchte der Compiler Ihnen helfen, Ihre Versprechen einzuhalten, die Werte const nicht zu ändern. Deshalb können Sie eine char * einer const char * zuweisen, aber nicht anders herum: es ist eindeutig sicher const -ness zu einem einfachen Zeiger, aber es wäre gefährlich, es wegzunehmen. Allerdings nehme an, Sie die folgende kompliziertere Reihe von Aufgaben durchgeführt:

const char c = 'x';  /* 1 */ 
char *p1;   /* 2 */ 
const char **p2 = &p1;  /* 3 */ 
*p2 = &c;   /* 4 */ 
*p1 = 'X';   /* 5 */ 

In Zeile 3, weisen wir eine char ** zu einem const char **. (Der Compiler sollte sich beschweren.) In Zeile 4 weisen wir const char * einem const char * zu; das ist eindeutig legal. In Zeile 5 modifizieren wir, was ein char * zeigt - das soll legal sein. Jedoch zeigt p1 auf c, was const ist. Dies geschah in Zeile 4, weil * p2 wirklich p1 war. Dies wurde in Zeile 3 festgelegt, die eine Zuweisung einer Form ist, die nicht erlaubt ist, und gerade deshalb ist Zeile 3 nicht erlaubt.

Das Zuweisen einer char ** zu einer const char ** (wie in Zeile 3 und in der ursprünglichen Frage) ist nicht sofort gefährlich. Aber es schafft eine Situation, in der das Versprechen von p2 - dass der letztlich auf den Wert hinweisende Wert nicht geändert wird - nicht eingehalten werden kann.

(C++ hat kompliziertere Regeln für const, Qualifizierte Zeiger zuweisen, die können Sie mehrere Arten von Aufgaben ohne Warnungen zu verursachen, aber immer noch Schutz vor unbeabsichtigten Versuche machen const Werte zu ändern. C++ erlauben würde, noch nicht char ** einen const char ** zuweisen , aber es würde Sie weg mit Zuweisung eines char ** zu einem const char * const * lassen.)

In C, wenn man sich andere Hinweise, die Qualifier Mismatches haben als die erste Dereferenzierungsebene zuweisen oder übergeben müssen, müssen Sie explizite Casts verwenden (z. B. (const char **) in diesem Fall), obwohl wie immer die Notwendigkeit für eine solche Besetzung eine tiefere anzeigen kann Problem, das die Besetzung nicht wirklich beheben kann.

Referenzen: ISO Sec. 6.1.2.6, Sec. 6.3.16.1, Sek. 6.5.3 H & S sek. . 7.9.1 pp 221-2

+0

Das ist es, cool – Andomar

0

auf Antwort des Rampion basiert bearbeitet. Das Problem ist mit der doppelten const in f() Erklärung.

Code mit der Warnung:

struct_type ** v; 
v = (struct_type **)malloc(10 * sizeof(struct_type *));  
f(v); 

Dies ohne Warnung kompiliert:

const struct_type *const* v; 
v = (const struct_type **)malloc(10 * sizeof(struct_type *)); 
f(v); 
-1

f erwartet als Eingabe ein Array von Zeigern (const struct_type * []) ​​zu erhalten. Sie übergeben einen Zeiger auf einen Zeiger von struct (const struct_type **).

Das Beste, IMO zu tun, ist die Unterschrift von f zu ändern:

void f(const struct_type *const* data); 

Warum brauchen Sie Arrays als Argumente an Funktionen übergeben?

+0

Weil das so erklärt wird :) (es ist einfacher im Falle einer statischen Deklaration von Daten [] Ich denke) – elmarco

+0

In C sind "void ** myvar" und "void * myvar []" ein und dasselbe. – Andomar

+0

Entschuldigung, aber das ist wirklich keine richtige Antwort auf die Frage :) -1 zum Sortieren der Antworten – elmarco

0

sehen, ob dies für Sie arbeiten würde:

f(struct_type *data); 

void test(unsigned n) 
{ 
    struct_type *v = malloc(n * sizeof(struct_type *)); 
    f(v); 
} 

Bitte lassen Sie mich wissen, wie Sie auf bekommen.

+0

leider wird es nicht helfen, weil ich es beim Füllen der Strukturen ändern müssen. – elmarco

+0

Sie können es immer noch ändern, wenn Sie die Struktur füllen. Ich hatte vor kurzem mit sehr ähnlichen Problem zu kämpfen und mein Code sah aus wie: void clearScrorer (SCORER * scorerArray) { \t scorerArray-> name = NULL; \t ScorerArray-> Ergebnis = -1; } Und es funktionierte ohne Probleme. Denken Sie daran, dass Sie mit f (v) den Verweis auf v übergeben. Wenn Sie also v innerhalb von f ändern, wird die Funktion v an anderer Stelle ebenfalls geändert. – Artur