Konvertierung in void ** auf verschiedenen Compilern

9

Ich habe den folgenden Code über verschiedene Compiler ausgeführt:

int main()
{
    float **a;
    void **b;
    b = a;
}

Soweit ich feststellen konnte, void **handelt es sich nicht um einen generischen Zeiger, was bedeutet, dass eine Konvertierung von einem anderen Zeiger keine Warnung kompilieren oder zumindest auslösen sollte. Hier sind jedoch meine Ergebnisse (alles unter Windows):

  • gcc - Wirft wie erwartet eine Warnung aus.
  • g ++ - Wirft erwartungsgemäß einen Fehler aus (dies liegt an der weniger zulässigen Eingabe von C ++, oder?)
  • MSVC (cl.exe) - Wirft keinerlei Warnungen, auch wenn / Wall angegeben ist.

Meine Frage ist: Vermisse ich etwas an der ganzen Sache und gibt es einen bestimmten Grund, warum MSVC keine Warnung ausgibt? MSVC gibt beim Konvertieren von void ** nach eine Warnung ausfloat ** .

Noch etwas zu beachten: Wenn ich durch a = bdie explizite Konvertierung ersetze a = (void **)b, gibt keiner der Compiler eine Warnung aus. Ich dachte, dies sollte eine ungültige Besetzung sein. Warum sollte es dann keine Warnungen geben?

Der Grund, warum ich diese Frage stelle, ist, dass ich angefangen habe, CUDA zu lernen, und im offiziellen Programmierhandbuch ( https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#device-memory ). Der folgende Code kann gefunden werden:

// Allocate vectors in device memory
float* d_A;
cudaMalloc(&d_A, size);

die eine implizite Konvertierung nach void **for durchführen sollte &d_A, da das erste Argument von cudaMallocvom Typ ist void **. Ein ähnlicher Code ist in der gesamten Dokumentation zu finden. Ist das nur eine schlampige Arbeit am Ende von NVIDIA oder fehlt mir wieder etwas? Da nvccMSVC verwendet wird, wird der Code ohne Warnungen kompiliert.

CaptainProton42
quelle
3
Fehler von den geposteten 3 live: godbolt.org/z/GQWMNo
Richard Critten
3
Die Codefehler für mich bei MSVC. Welche Version verwenden Sie? Aber ja, void**ist kein generischer Zeiger. Nur void*ist.
NathanOliver
Danke für die schnelle Antwort! 19.24.28315 für x64 anscheinend? Ich habe MSVC noch nicht wirklich benutzt.
CaptainProton42
2
(void**)ist eine explizite Besetzung im C-Stil. Es sagt dem Compiler, er solle nicht genau hinschauen, was Sie tun, und Ihnen vertrauen. Es ist eine explizite Überschreibung des Typensicherheitssystems, und Compiler müssen grundsätzlich jede Art von Konvertierung akzeptieren. Casts im C-Stil sollten vermieden werden, sie sind viel zu kraftvoll. Verwenden Sie C ++ - Casts, static_castdie sich beschweren, wenn Sie versuchen, etwas zu tun, das keinen Sinn ergibt.
François Andrieux
@RichardCritten falsche Sprache - keine Fehler godbolt.org/z/RmFpgN C ++ cuda erfordert wie üblich explizite Casts.
P__J__

Antworten:

4

Vermisse ich etwas an der ganzen Sache und gibt es einen bestimmten Grund, warum MSVC keine Warnung ausgibt? MSVC gibt beim Konvertieren von void ** in float ** eine Warnung aus

Diese Zuweisung ohne Umwandlung stellt eine Einschränkungsverletzung dar, sodass ein standardkonformer Compiler eine Warnung oder einen Fehler ausgibt. MSVC ist jedoch nicht vollständig kompatibel mit der C-Implementierung.

Noch etwas zu beachten: Wenn ich a = b durch die explizite Konvertierung a = (void **) b ersetze, gibt keiner der Compiler eine Warnung aus. Ich dachte, dies sollte eine ungültige Besetzung sein. Warum sollte es dann keine Warnungen geben?

In einigen Situationen sind Zeigerkonvertierungen über eine Besetzung zulässig. Der C-Standard sagt in Abschnitt 6.3.2.3p7 Folgendes aus:

Ein Zeiger auf einen Objekttyp kann in einen Zeiger auf einen anderen Objekttyp konvertiert werden. Wenn der resultierende Zeiger für den referenzierten Typ nicht korrekt ausgerichtet ist, ist das Verhalten undefiniert. Andernfalls wird das Ergebnis bei erneuter Konvertierung mit dem ursprünglichen Zeiger verglichen. Wenn ein Zeiger auf ein Objekt in einen Zeiger auf einen Zeichentyp konvertiert wird, zeigt das Ergebnis auf das niedrigste adressierte Byte des Objekts. Aufeinanderfolgende Inkremente des Ergebnisses bis zur Größe des Objekts ergeben Zeiger auf die verbleibenden Bytes des Objekts.

Sie können also zwischen Zeigertypen konvertieren, sofern keine Ausrichtungsprobleme vorliegen und Sie nur zurückkonvertieren (es sei denn, das Ziel ist a char *).

float* d_A;
cudaMalloc(&d_A, size);

...

Ist das nur eine schlampige Arbeit am Ende von NVIDIA oder fehlt mir wieder etwas?

Vermutlich dereferenziert diese Funktion den gegebenen Zeiger und schreibt die Adresse eines zugewiesenen Speichers. Das würde bedeuten, dass es versucht, an a zu schreiben, float *als wäre es a void *. Dies ist nicht dasselbe wie die typische Konvertierung von / nach a void *. Genau genommen sieht dies nach undefiniertem Verhalten aus, obwohl es "funktioniert", da moderne x86-Prozessoren (wenn sie sich nicht im Real-Modus befinden) für alle Zeigertypen dieselbe Darstellung verwenden.

dbush
quelle
@dbush Sehr informativ, danke! Ich verstehe, warum es funktioniert, wenn es funktioniert. Würden die meisten Compiler nicht eine Warnung oder sogar einen Fehler auslösen, weil &d_Asie nicht den erforderlichen Typ haben?
CaptainProton42
3
Seien Sie vorsichtig, wie Sie dies interpretieren, es kann und gibt Template-Tricks zwischen, wenn Sie CUDA mit einem C ++
Talonmies