Является ли void ** приемлемым типом в ANSI-C?

StackOverflow https://stackoverflow.com/questions/246280

  •  05-07-2019
  •  | 
  •  

Вопрос

Я видел функцию, прототипом которой является:

int myfunc(void** ppt)

Эта функция вызывается в файле C как a = myfunc(mystruct **var1);

где mystruct - это typedef для одной из имеющихся у нас структур.

Это работает без каких-либо ошибок компиляции в MSVC6.0, Но когда я компилирую его с помощью какого-либо другого компилятора C, он выдает ошибку в том месте, где вызывается эта функция, с сообщением об ошибке:

Аргумент типа mystruct ** несовместим с параметром типа void **

Аргумент myfunc() сохраняется как void **, потому что это, по-видимому, универсальная функция типа malloc, которая вызывается с различными типами структурных переменных для выделения памяти

  1. Есть ли какой-либо тип, такой как void **, разрешенный в стандарте C / любых компиляторах C?
  2. Как мне это исправить?[Я попытался привести аргумент вызова функции к mystruct**, но это не сработало]

-РЕКЛАМА

Это было полезно?

Решение

void** является допустимо, но, основываясь на вашем сообщении об ошибке, вам, вероятно, придется явно привести аргумент следующим образом:

mystruct **var1;
x = myfunc ((void**) var1);

Это потому, что myfunc функция ожидает, что void** Тип.В то время как void* может быть неявно приведен к любому другому указателю, чего нельзя сказать о двойном указателе - вам нужно явно привести его.

Другие советы

В часто ЗАДАВАЕМЫЕ ВОПРОСЫ по comp.lang.c подробно эта проблема рассматривается в Вопрос 4.9.Короче говоря, они говорят это не совсем портативно чтобы привести произвольный указатель на указатель на void **;далее они объясняют, что "подобный код может работать и иногда рекомендуется, но он опирается на все типы указателей, имеющие одинаковое внутреннее представление (которое является общим, но не универсальным)". Они продолжают объяснять , что "любой void ** значение, с которым вы играете, должно быть адресом фактического void * ценность где-то;бросает , как (void **)&dp, хотя они могут отключить компилятор, они непереносимы (и могут даже не делать то, что вы хотите)."

Таким образом, вы можете безопасно / переносимо достичь желаемого поведения с помощью такого кода, как:

some_type *var1 = foo();
void *tmp_void_ptr = (void *)var1;
myfunc(&tmp_void_ptr);

Существует причина, по которой компилятор не может автоматически выполнять приведение из mystruct** Для void**.

Рассмотрим следующий код:

void stupid(struct mystruct **a, struct myotherstruct **b)
{
    void **x = (void **)a;
    *x = *b;
}

Компилятор не будет жаловаться на неявное приведение из myotherstruct* Для void* в *x = *b строка, даже несмотря на то, что эта строка пытается поместить указатель на myotherstruct в месте , где только указатели на mystruct должен быть поставлен.

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

Этот вопрос немного сбивает с толку.Но да, void ** безусловно, является законным и действительным C и означает "указатель на указатель на void" как и ожидалось.

Я не уверен в вашем примере вызова, аргументы ("mystruct **var1") не имеют смысла.Если var1 относится к типу mystruct **, вызов должен просто читать a = func(var1);, это может быть опечатка.

Кастинг должен сработать, но вам нужно кастовать, чтобы void **, поскольку именно этого ожидает функция.

Как бы грязно это ни выглядело:иногда вы не можете решить проблему без использования void **.

ДА, void ** вполне приемлемо и весьма полезно при определенных обстоятельствах.Также учтите, что, учитывая декларацию void **foo и void *bar, компилятор будет знать размер объекта, на который указывает foo (он указывает на указатель, и все указатели имеют одинаковый размер, за исключением нескольких древних платформ, о которых вам не нужно беспокоиться), но он не будет знать размер объекта, на который указывает bar (он может указывать на объект любого размера).Таким образом, вы можете безопасно выполнять арифметику указателей на void ** указатели , но не на void * указатели.Сначала вы должны применить их к чему-то другому, например к char *.GCC позволит вам притвориться, что void * и char * эквивалентны, каждый указывает на объект размером в один байт, но это нестандартно и непереносимо.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top