Uruchomiłem następujący kod za pośrednictwem różnych kompilatorów:
int main()
{
float **a;
void **b;
b = a;
}
Z tego co udało mi się zebrać, void **
to nie ogólna wskazówka co oznacza, że każdy z innego wskaźnika konwersji nie powinien skompilować lub przynajmniej rzucić ostrzeżenie. Oto moje wyniki (wszystkie wykonane w systemie Windows):
- gcc - Zgłasza ostrzeżenie, zgodnie z oczekiwaniami.
- g ++ - Zgłasza błąd, zgodnie z oczekiwaniami (wynika to z mniej dopuszczalnego pisania w C ++, prawda?)
- MSVC (cl.exe) - nie generuje żadnych ostrzeżeń, nawet jeśli określono / Wall.
Moje pytanie brzmi: czy brakuje mi czegoś w tym wszystkim i czy jest jakiś konkretny powód, dla którego MSVC nie wyświetla ostrzeżenia? MSVC nie produkują ostrzeżenie podczas konwersji od void **
do float **
.
Kolejna uwaga: jeśli zastąpię a = b
jawną konwersją a = (void **)b
, żaden z kompilatorów nie wyświetli ostrzeżenia. Myślałem, że to powinna być niepoprawna obsada, więc dlaczego nie byłoby żadnych ostrzeżeń?
Zadaję to pytanie, ponieważ zacząłem się uczyć CUDA oraz w oficjalnym Przewodniku programowania ( https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#device-memory ) można znaleźć następujący kod:
// Allocate vectors in device memory
float* d_A;
cudaMalloc(&d_A, size);
który powinien wykonać niejawną konwersję na void **
for &d_A
, ponieważ pierwszy argument cudaMalloc
jest typu void **
. Podobny kod można znaleźć w całej dokumentacji. Czy to po prostu niechlujna praca na końcu NVIDII, czy znowu coś mi brakuje? Ponieważ nvcc
używa MSVC, kod kompiluje się bez ostrzeżeń.
void**
to nie jest ogólny wskaźnik. Tylkovoid*
jest.(void**)
jest jawną obsadą w stylu C. Mówi kompilatorowi, aby nie przyglądał się dokładnie temu, co robisz, i żeby ci zaufał. Jest to wyraźne zastąpienie systemu bezpieczeństwa typu, a kompilatory są zobowiązane do zaakceptowania praktycznie dowolnego rodzaju konwersji. Należy unikać rzutów w stylu C, są one zbyt mocne. Użyj rzutowań C ++,static_cast
które będą narzekać, jeśli spróbujesz zrobić coś, co nie ma sensu.Odpowiedzi:
To przypisanie bez rzutowania stanowi naruszenie ograniczenia, więc standardowy zgodny kompilator wyświetli ostrzeżenie lub błąd. Jednak MSVC nie jest w pełni zgodny z implementacją C.
W niektórych sytuacjach konwersje wskaźnika za pomocą rzutowania są dozwolone. Norma C mówi w sekcji 6.3.2.3p7, co następuje:
Więc może konwertować pomiędzy typami wskaźnik ile istnieją żadne problemy wyrównania i tylko przekonwertować z powrotem (chyba że celem jest
char *
).Przypuszczalnie ta funkcja wykasowuje dany wskaźnik i zapisuje adres pewnej przydzielonej pamięci. To by znaczyło, że próbuje napisać do
float *
tak, jakby to byłvoid *
. To nie to samo, co typowa konwersja do / zvoid *
. Ściśle mówiąc, wygląda to na niezdefiniowane zachowanie, chociaż „działa”, ponieważ nowoczesne procesory x86 (gdy nie są w trybie rzeczywistym) używają tej samej reprezentacji dla wszystkich typów wskaźników.źródło
&d_A
nie ma wymaganego typu?