2008-10-29 6 views
6

Я видел функцию, прототип:Является ли void ** приемлемым типом в ANSI-C?

int myfunc(void** ppt) 

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

где mystruct является typedef для одной из структуры, которую мы имеем.

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

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

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

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

-AD

ответ

9

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

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

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

+3

Это вызывает неопределенное поведение (изменение объекта через указатель на неправильный тип). API просто сломан; любая функция типа распределения, которая выделяет общую память (`void *`), должна возвращать указатель, а не принимать аргумент с двумя указателями. К счастью, такие сломанные API можно просто обернуть, чтобы исправить их ... – 2011-11-06 08:49:08

1

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

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

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

0

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

3

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

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

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

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

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

0

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

20

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

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

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