У указателей на указатели на структуры подразумевается взаимозаменяемость?

Согласно C99 §6.2.5p27 и C11 §6.2.5p28:

Все указатели на типы структуры должны иметь одинаковое представление и согласования друг с другом.

С примечанием (#39 и #48 соответственно):

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

(Обратите внимание, что C89 §3.1.2.5 не указывает о указателях на структуры)

-

Известно, что указатель на void, например, C11 §6.3.2.3p1:

A pointer to void may be converted to or from a pointer to any object type.

Не означает, что указатель на указатель на void совпадает с указателем на void и не подразумевает то же, что и другие указатели на указатели на объекты данных.

(Я прошу прощения, если я использую термин "подразумеваю одно и то же" слишком свободно, он предназначен для использования только в указанном контексте)

Ниже приведен пример кода, демонстрирующий общий указатель на указатель на struct:

#include <stdio.h>
#include <stdlib.h>

int allocate_struct(void *p, size_t s) {
    struct generic { char placeholder; };    

    if ( ( *(struct generic **) p = malloc(s) ) == NULL ) {
        fprintf(stderr, "Error: malloc();");
        return -1;
    }

    printf("p:  %p;\n", (void *) *(struct generic **) p);
    return 0;
}

int main(void) {
    struct s1 { unsigned int i;  } *s1;

    if (allocate_struct(&s1, sizeof *s1) != 0)
        return -1;

    printf("s1: %p;\n\n", (void *) s1);

    s1->i = 1;
    free(s1);
    return 0;
}

GCC:

-std=c89 -pedantic-errors -Wall -Wextra -fstrict-aliasing -Wstrict-aliasing=3 -O3

Результат: (без предупреждений)

p:  0x800103a8;
s1: 0x800103a8;

.

Вопрос

Является ли подразумеваемая взаимозаменяемость указателей на структуры применимы также к указателям на указатели структур?

Просто пояснить: вопрос о struct x ** vs other struct y **, а не о struct x * vs struct y **.

Ответ 1

Полный текст §6.2.5p28 в C11 уже отвечает вам:

Указатель на void должен иметь те же требования к представлению и выравниванию, что и указатель на тип символа. 48) Аналогично, указатели на квалифицированные или неквалифицированные версии совместимых типов должны иметь одинаковые требования к представлению и выравниванию. Все указатели на типы структуры должны иметь одинаковые требования к представлению и согласованию друг с другом. Все указатели на типы объединения должны иметь одинаковые требования к представлению и согласованию друг с другом. Указатели на другие типы не должны иметь одинаковые требования к представлению или выравниванию.

Чтобы упорядочить абзац немного (определите "==" как "имеют те же требования к представлению и выравниванию" ):

  • void * == char *
  • type-qualifier(s) X * == X *
  • struct X * == struct Y *
  • union X * == union Y *

В конце параграф явно говорит:

Указатели на другие типы не должны иметь одинаковые требования к представлению или выравниванию.

Так как struct X * и struct Y * являются двумя отдельными типами, которые не являются ни char, struct, ни union, то указатели на них необязательно должны иметь одинаковые требования к представлению и выравниванию. Следовательно, struct X ** и struct Y ** не гарантируют одинаковые требования к представлению и выравниванию.

Ответ 2

x** не совпадает с y** от уровня типа, первая цитата говорит о размере указателя по сравнению с его типом.

Указатель может быть отличен из x * в void *, говорящий "кто заботится о том, что это такое", чтобы вы могли передать его как адрес памяти, и, сообщая компилятору, доверяет мне, я знаю, что делаю.

Как только вы сообщите об отключении компилятора, я знаю, что делаю, и вы ошибаетесь, вы сами.

Ответ 3

На многих платформах с отключенным строгим псевдонимом возможно и полезно иметь возможность принимать массив указателей на структуры неизвестного типа; в большинстве случаев такой код также сможет принимать массив указателей на произвольные типы, хотя некоторые системы могут использовать другое представление для указателей "char", чем для указателей на вещи с более грубым выравниванием.

К сожалению, создатели C89 включали правило, иногда называемое "строгим правилам псевдонимов", которое отравило язык, поскольку оно слишком долго игнорировалось авторами компилятора и программистами, что отсутствие протеста побудило некоторых людей думать он может быть применен без каких-либо проблем. В отсутствие этого правила код может принимать массив указателей на структуры неизвестного типа, первое поле которых было, например, поле "int", называемое id, и писать код типа item_id = items[index]->id; и эффективно работать. Если кто-то находится в неудачной позиции того, что не может избежать ограничений на сглаживание, нужно написать такой код, как:

ID_ONLY_ITEM *temp;
memcpy(temp, items+index, sizeof temp);
item_id = temp->id;

Я должен признаться, что я озадачен тем, почему люди, похоже, считают, что есть причины, чтобы компиляторы налагали строгий псевдоним, когда более эффективные оптимизации можно безопаснее с помощью restrict; Я бы предположил, что лучше просто убедиться, что код документирован как требующий строгого псевдонимов и бойкотировать строгие ограничения на сглаживание.