У указателей на указатели на структуры подразумевается взаимозаменяемость?
Согласно 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
; Я бы предположил, что лучше просто убедиться, что код документирован как требующий строгого псевдонимов и бойкотировать строгие ограничения на сглаживание.