Ваш пример может быть сводились к
typedef void * (*VF)(void *);
typedef int * (*IF)(int *);
IF a = 0;
VF b = a; //Warning
В тех C standard указателей на функции менее универсален, чем указатель на объекты.
Вы можете преобразовать указатель на объект в указатель на void
(и обратно) без броска (и предупреждения), потому что есть пункт в стандарте, который явно позволяет это
Указатель к мочеиспусканию может быть преобразуется в указатель или из указателя на любой тип объекта.
Указатель на любой тип объекта может быть преобразован в указатель на void и обратно;
результат должен быть сравнить с исходным указателем.
Обратите внимание, что функция не является объектом.
Что касается указателей на функцию, единственное, что стандартные гарантии являются:
Указатель на функцию одного типа может быть преобразован в указатель на функцию другого типа и обратно; результат сравнивается с исходным указателем.
Если переделанный указатель используется для вызова функции, тип которого не совместим с ссылочного типа, поведение не определено
Позже стандарт уточняет, что это значит для двух функций, чтобы быть совместимым:
Для двух типов функций, которые должны быть совместимыми, оба должны указывать совместимые типы возврата.
Кроме того, списки параметров, если они присутствуют, согласуются с количеством параметров и использованием терминатора с многоточием; соответствующие параметры должны иметь совместимые типы.
Теперь вы можете думать, что void*
и struct location_t*
совместимые типы, в конце концов вы можете назначить друг другу.
Но стандарт кристально ясно:
Два типа имеют совместимый тип, если их типы совпадают.
В дальнейшем это расширение распространяется на более сложные типы и квалифицированные типы.
Это может показаться удивительным, но int*
и void*
несовместимы.
Они могут быть назначены, но не совместимы, ведь void*
может указывать на float
или объект с различными требованиями к выравниванию.
Когда речь заходит о указателях разных типов, стандарт в основном касается назначения их взад и вперед.
Он не запрещает преобразование между несовместимыми типами указателей, но использование их - неопределенное поведение, поэтому предупреждение.
Чем лучше подход, чтобы выполнить бросок внутри функции, as suggested in this answer.
Вся ваша функция должна иметь подпись void* (void*)
, так что не требуется переключение между указателями на функции.
SetElement locationCopy(SetElement element)
{
Location location = (Location)element;
Location new_location = locationCreate(location->name);
return new_location;
}
Брикеты внутри функции снова подвержены непредсказуемому поведению, если SetElement
указатели отлиты несовместимых указателей, но это не должно произойти, если вы будете называть locationCopy
только с указателями на Location
с (как вы на самом деле ожидать делать).
Как примечание стороны, некоторые программист может неодобрительно с помощью typedef
, чтобы скрыть тип указателя.
Не скрывайте природу указателя за 'typedef', за исключением, возможно, указателей на функцию. Это смущает всех, включая вас. –