2013-12-09 27 views
14

Ich habe diesen einfachen Code, der ohne Fehler/Warnungen kompiliert:C++ char * [] char ** Umwandlung

void f(int&, char**&){} 

int main() 
{ 
    int argc = 2; 
    char* argv[] = { "", "", nullptr }; 
    f(argc, argv); 
    //@VS2013 error: cannot convert argument 2 from 'char *[3]' to 'char **&' 
    //@GCC error: invalid initialization of non-const reference of type 'char**&' from an rvalue of type 'char**' 
    return 0; 
} 

Warum:

void f(int&, char**&){} 

int main(int argc, char* argv[]) 
{ 
    f(argc, argv); 
    return 0; 
} 

Und neben ähnlichen Code, der nicht kompilieren char*[] kann im ersten Beispiel in char**& konvertiert werden und kann nicht in das zweite Beispiel konvertiert werden? Ist es wichtig, ob die Größe zur Kompilierzeit bekannt ist?

EDIT: Ich denke, es gibt 2 Konvertierungen im zweiten Fall benötigt, und nur eine implizite Konvertierung kann vom Compiler durchgeführt werden.

Dieser Code kompiliert fein:

void f(int&, char**&){} 

int main() 
{ 
    int argc = 2; 
    char* temp[] = { "", "", nullptr }; 
    char** argv = temp; 
    f(argc, argv); 
    return 0; 
} 
+0

Beachten Sie, dass Sie mehr Probleme als das (bezogen auf C++ 11-Kompatibilität) haben [siehe hier Live-Demo] (http://coliru.stacked-crooked.com/a/b27bbe925d88d735). – rubenvb

+3

Im Funktionsparameter sind 'char * name []' und 'char ** name' * äquivalent * - sie bezeichnen dasselbe (genauer gesagt, das erstere wird in das letztere umgewandelt). – Xeo

+0

@rubenvb Ich weiß const char * char * ist in C++ 11 veraltet. Auch andere const Korrektheitsprobleme sind hier vorhanden ... – Felics

Antwort

3

Jefffrey's comment verweist auf den Standard, hier ist es:

4.2 Array-Zeiger-Konvertierung [conv.Array]

Ein Lvalue oder Rvalue des Typs "Array von N T" oder "Array der unbekannten Grenze von T" kann in einen prvalue des Typs "Zeiger auf T" umgewandelt werden. Das Ergebnis ist ein Zeiger auf das erste Element des Arrays .

Und ein prvalue ist:

A prvalue ("reiner" R-Wert) ist ein Ausdruck, der ein temporäres Objekt (oder ein Subobjekt davon) oder ein Wert ist nicht im Zusammenhang mit jedem Objekt identifiziert .

Sie können eine nicht konstante Referenz nicht an eine temporäre Verbindung binden.

int& i = int(); // error 

char* argv[] = { "", "", nullptr }; 
// the result of the conversion is a prvalue 
char**& test = argv; // error 

Daher ist der folgende Code glücklich kompilieren:

#include <iostream> 

void f(int& argc, char** const& argv){ 
    std::cout << argv[0] << std::endl; // a 
} 

int main() 
{ 
    int argc = 2; 
    char* argv[] = { "a", "b", nullptr }; 
    f(argc, argv); 
    return 0; 
} 

Eine wichtige Sache, die ich glasig ist in Kanze's comment hingewiesen.

Im ersten Beispiel im OP sind char* argv[] und char** argv gleichwertig. Daher gibt es keine Konvertierung.

std::cout << std::is_array<decltype(argv)>::value << std::endl; // false 
std::cout << std::is_array<char**>::value << std::endl; // false 
std::cout << std::is_array<char*[]>::value << std::endl; // true 
std::cout << std::is_same<decltype(argv), char**>::value << std::endl; // true 
std::cout << std::is_same<decltype(argv), char*[]>::value << std::endl; // false 
+0

wenn die main() hat argv als prrameter, dann ist das nicht in Zeiger umgewandelt, wenn wir es an f() übergeben? –

+0

Dies beinhaltet nicht das Umschreiben von Funktionsparametern, was ein wichtiger Teil des Verständnisses dessen ist, was vor sich geht. –

+0

@James Der Standard besagt, dass eine Konvertierung stattfindet. Ist es anders als die Behandlung von Funktionsparametern? –

8

Denn trotz Erscheinungen, das zweite Argument zu main hat Typ char**. Wenn es als Deklaration eines Arguments der Funktion verwendet wird, wird ein Array der obersten Ebene in einen Zeiger umgeschrieben, so dass char *[] tatsächlich char** ist. Dies gilt jedoch nur für die Funktion Parameter.

A char*[] (wie im zweiten Fall) zu einem char**, umwandeln kann, aber die Ergebnisse der Umwandlung (wie bei jeder Umwandlung) ein rvalue, und kann nicht verwendet werden können, eine nicht konstante Referenz zu initialisieren. Warum willst du die Referenz? Wenn es den Zeiger zu ändern ist, das char** Argument zu main zu ändern, ist undefiniertes Verhalten (formal, in C, mindestens — Ich habe nicht überprüft, ob C++ mehr liberal hier ist). Und natürlich gibt es keine Möglichkeit, die die konstante Adresse eines Arrays zu ändern. Und wenn Sie nicht wünschen, zu ändern, warum eine Referenz verwenden?

+0

Ich akzeptierte Remys Antwort für das Standardzitat. Vielen Dank! +1 – Felics

0

Die Art der temp in

char* temp[] = { "", "", nullptr };

ist nicht char * [], dann ist es

char*[3]

Letzteres kann nicht implizit in `char ** 'umgewandelt werden.

In main, die Art der argv ist ein ungebundenes char* Array, das entspricht char**

Ich gebe zu, es ist verwirrend :)