Во многих случаях использование двойных указателей позволяет функции предлагать лучшую семантику ошибок, чем использование возвращаемого значения. Например, функция изменения размера выделения, которая должна оставлять выделение неизменным в случае сбоя, может просто воздержаться от изменения внешнего указателя на выделение, вместо того, чтобы требовать, чтобы вызывающий объект сохранял копию исходного указателя, а затем используйте это вместо возвращаемого значения функции, когда функция возвращает null.
К сожалению, хотя язык, описанный в K & R2, позволяет функции, которая принимает struct ANYTHING **ref
, использовать его напрямую для обновления указателя на любой вид структура [поскольку указатели структур могут быть преобразованы в void*
и обратно в единицах компиляции, которые не содержат определений для этих структур, все указатели на типы структур должны иметь одинаковое представление], и хотя все версии стандарта C будут разрешить компиляторам поддерживать такие конструкции, никакие версии Стандарта не запрещают компиляторам требовать, чтобы программисты перепрыгивали через обруч для выполнения sh таких вещей; авторы clang и g cc рассматривают отсутствие запрета как приглашение.
Следовательно, если кто-то хочет использовать такие конструкции способом, совместимым с -fstrict-aliasing
диалектом, обработанным clang и g cc необходимо использовать memcpy
для обновления указателей вместо использования присваиваний. В зависимости от используемой реализации это может дать или не дать код, который будет столь же эффективным, как простое присвоение. Если идти по этому маршруту, Я бы предложил:
#ifdef JUMP_THROUGH_SILLY_HURDLES_FOR_CLANG_AND_GCC
memcpy(origRef, &newPointerValue, sizeof newPointerValue);
#else
*((STRUCT ANYTHING**)origRef) = newPointerValue;
#endif
, чтобы можно было добиться эффективного и правильного поведения при использовании качественного коммерческого компилятора, который предполагает, что можно будет использовать memcpy
только в тех случаях, когда источник и место назначения не были известно, что оба они выровнены (и, таким образом, будут генерировать более медленный код для этого случая на платформах, которые изначально не обрабатывают невыровненный доступ), или один из них использует компиляторы, которые требуют использования memcpy
даже в тех случаях, когда простая выровненная по словам загрузка и сохранение хватит.