Преобразование в void ** на разных компиляторах

9

Я запускаю следующий код через разные компиляторы:

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

Из того, что я был в состоянии собрать, void **это не общий указатель , который означает , что любое преобразование из другого указателя не должен составлять по крайней мере , бросить предупреждение. Тем не менее, вот мои результаты (все сделано на Windows):

  • gcc - выдает предупреждение, как и ожидалось.
  • g ++ - выдает ошибку, как и ожидалось (это связано с менее разрешительной типизацией C ++, верно?)
  • MSVC (cl.exe) - не выдает никаких предупреждений, даже если указан / Wall.

Мой вопрос: я что-то упускаю из всего этого и есть ли какая-то конкретная причина, по которой MSVC не выдает предупреждение? MSVC делает производить предупреждение при преобразовании из void ** в float **.

Еще одно замечание: если я заменю a = bявным преобразованием a = (void **)b, ни один из компиляторов не выдаст предупреждение. Я думал, что это должен быть неправильный актерский состав, так почему бы не было никаких предупреждений?

Причина, по которой я задаю этот вопрос, заключается в том, что я начал изучать CUDA и в официальном руководстве по программированию ( https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#device-memory ). следующий код можно найти:

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

который должен выполнять неявное преобразование в void **for &d_A, поскольку первый аргумент cudaMallocимеет тип void **. Подобный код можно найти по всей документации. Это просто неаккуратная работа на стороне NVIDIA или я опять что-то упускаю? Поскольку nvccиспользует MSVC, код компилируется без предупреждений.

CaptainProton42
источник
3
Ошибки из опубликованных 3 в прямом эфире: godbolt.org/z/GQWMNo
Ричард Криттен
3
Ошибки кода для меня с MSVC. Какую версию вы используете? Но да, void**это не общий указатель. Только void*есть.
Натан Оливер
Спасибо за быстрый ответ! 19.24.28315 для х64 видимо? Я действительно не использовал MSVC раньше.
CaptainProton42
2
(void**)является явным приведением стиля c. Он говорит компилятору не смотреть внимательно на то, что вы делаете, и доверять вам. Это явное переопределение системы безопасности типов, и компиляторы должны принимать практически любые преобразования. Следует избегать бросков в стиле C, они слишком мощные. Используйте приведение C ++, например, static_castкоторое будет жаловаться, если вы пытаетесь сделать что-то, что не имеет смысла.
Франсуа Андриё
@RichardCritten неверный язык - без ошибок godbolt.org/z/RmFpgN C ++ cuda требует явного приведения, как обычно.
P__J__

Ответы:

4

Я что-то упускаю из всего этого, и есть ли какая-то конкретная причина, по которой MSVC не выдает предупреждение? MSVC выдает предупреждение при конвертации из void ** в float **

Это назначение без приведения является нарушением ограничения, поэтому стандартный совместимый компилятор выведет предупреждение или ошибку. Однако MSVC не является полностью совместимой реализацией языка C.

Еще одно замечание: если я заменю a = b явным преобразованием a = (void **) b, ни один из компиляторов не выдаст предупреждение. Я думал, что это должен быть неправильный актерский состав, так почему бы не было никаких предупреждений?

Преобразование указателя с помощью приведения допускается в некоторых ситуациях. Стандарт C гласит следующее в разделе 6.3.2.3p7:

Указатель на тип объекта может быть преобразован в указатель на другой тип объекта. Если результирующий указатель не выровнен правильно для ссылочного типа, поведение не определено. В противном случае при обратном преобразовании результат сравнивается равным исходному указателю. Когда указатель на объект преобразуется в указатель на тип символа, результат указывает на младший адресуемый байт объекта. Последовательные приращения результата, вплоть до размера объекта, дают указатели на оставшиеся байты объекта.

Таким образом, вы можете выполнять конвертацию между типами указателей при условии, что проблем с выравниванием нет, и вы конвертируете только обратно (если цель не a char *).

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

...

Это просто неаккуратная работа на стороне NVIDIA или я опять что-то упускаю?

Предположительно, эта функция разыменовывает данный указатель и записывает адрес некоторой выделенной памяти. Это будет означать, что он пытается написать так, float *как если бы он был void *. Это не то же самое, что типичное преобразование в / из void *. Строго говоря, это выглядит как неопределенное поведение, хотя и «работает», потому что современные процессоры x86 (когда они не в реальном режиме) используют одинаковое представление для всех типов указателей.

dbush
источник
@dbush Очень информативно, спасибо! Я понимаю, почему это работает, если это работает. Тем не менее, большинство компиляторов не выдают предупреждение или даже ошибку, потому &d_Aчто не имеют требуемого типа?
CaptainProton42
3
Будьте осторожны в том, как вы интерпретируете это, между шаблонами CUDA с помощью компилятора C ++ могут быть и есть хитрости шаблонов
talonmies