Является ли void ** приемлемым типом в ANSI-C? - PullRequest
7 голосов
/ 29 октября 2008

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

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**, но это не сработало]

-AD

Ответы [ 6 ]

22 голосов
/ 30 октября 2008

FAQ comp.lang.c подробно решает эту проблему в Вопрос 4.9 . Короче говоря, они говорят, что не является строго переносимым , чтобы привести произвольный указатель к указателю на void **; они продолжают объяснять, что «подобный код может работать и иногда рекомендуется, но он опирается на все типы указателей, имеющие одинаковое внутреннее представление (что является распространенным, но не универсальным)». Они продолжают объяснять, что «любое значение void **, с которым вы играете, должно быть адресом действительного значения void * где-либо; приведение типа (void **)&dp, хотя они могут выключить компилятор, являются непереносимыми (и могут даже не делать что хочешь). "

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

some_type *var1 = foo();
void *tmp_void_ptr = (void *)var1;
myfunc(&tmp_void_ptr);
8 голосов
/ 29 октября 2008

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

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

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

3 голосов
/ 29 октября 2008

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

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

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

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

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

1 голос
/ 29 октября 2008

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

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

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

0 голосов
/ 29 октября 2008

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

0 голосов
/ 29 октября 2008

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

...