Конструкция, которую вы описываете для использования указателя на «базовую» структуру в качестве псевдонима для нескольких «производных» структур, хотя часто используется с такими вещами, как struct sockaddr
, не гарантируется, что она будет работать по стандарту C .
Несмотря на то, что есть какой-то язык, который можно предложить, он может поддерживаться, в частности, 6.7.2.1p15:
Внутри объекта структуры, члены не-битового поля иблоки, в которых находятся битовые поля, имеют адреса, которые увеличиваются в порядке их объявления. Указатель на объект структуры, соответствующим образом преобразованный, указывает на его начальный элемент (или, если этот элемент является битовым полем, то на модуль, в котором он находится), и наоборот. Внутри объекта структуры может быть безымянное заполнение, но не в его начале.
Другие части предполагают, что это не так, в частности 6.3.2.3, в котором обсуждаются допустимые преобразования указателей:
1 Указатель на void может быть преобразован в или из указателя на любой тип объекта. Указатель на любой тип объекта может быть преобразован в указатель на void и обратно;результат должен сравниваться равным исходному указателю.
2 Для любого квалификатора q указатель на неквалифицированный тип может быть преобразован в указатель на q-квалифицированную версиютипа;значения, сохраненные в исходных и преобразованных указателях, должны сравниваться равными.
3 Целочисленное константное выражение со значением 0 или такое выражение, приведенное к типу void *, называется нулевой константой указателя,Если константа нулевого указателя преобразуется в тип указателя, результирующий указатель, называемый нулевым указателем, гарантированно сравнивается с неравным указателем на любой объект или функцию.
4 Преобразованиенулевой указатель на другой тип указателя дает нулевой указатель этого типа. Любые два нулевых указателя должны сравниваться равными.
5 Целое число может быть преобразовано в любой тип указателя. За исключением случаев, указанных ранее, результат определяется реализацией, может быть неправильно выровнен, может не указывать на объект ссылочного типа и может быть представлением прерывания.
6 ЛюбойТип указателя может быть преобразован в целочисленный тип. За исключением случаев, указанных ранее, результат определяется реализацией. Если результат не может быть представлен в целочисленном типе, поведение не определено. Результат не обязательно должен находиться в диапазоне значений любого целочисленного типа.
7 Указатель на тип объекта может быть преобразован в указатель на другой тип объекта. Если результирующий указатель неправильно выровнен для ссылочного типа, поведение не определено.
В противном случае при обратном преобразовании результат будет сравниваться равным исходному указателю. Когда указатель на объект преобразуется в указатель на тип символа, результат указывает на младший адресуемый байт объекта. Последовательные приращения результата, вплоть до размера объекта, дают указатели на оставшиеся байты объекта.
8 Указатель на функцию одного типа может быть преобразован вуказатель на функцию другого типа и обратно;результат должен сравниваться равным исходному указателю. Если преобразованный указатель используется для вызова функции, тип которой не совместим с указанным типом, поведение не определено.
Из вышесказанного не следует, что приведение из одной структуры в другую, гдедопускается использование типа первого члена.
Допускается, однако, использование union
для выполнения, по сути, того же самого. В разделе 6.5.2.3p6 говорится:
Для упрощения использования союзов предоставляется одна специальная гарантия: если объединение содержит несколько структур, которые имеют общую начальную последовательность (см. Ниже), и если объект объединения в настоящее время содержит одну из этих структур, разрешается проверять общую начальную часть любой из них везде, где декларациязавершенный тип объединения видим. Две структуры имеют общую начальную последовательность, если соответствующие элементы имеют совместимые типы (и для битовых полей одинаковой ширины) для последовательности из одного или нескольких начальных элементов.
Итак, что вы можете сделать, это определить объединение, которое содержит все возможные типы, а также базовый тип:
union various {
struct base { int num; } b;
struct one { int num; int a; } s1;
struct two { int num; double b; } s2;
struct three { int num; char *c; } s3;
};
Затем вы будете использовать этот союз в любом месте, где вам нужно, из трех подтипов. и вы можете свободно осмотреть базовый элемент, чтобы определить его тип. Например:
void foo(union various *u)
{
switch (u->b.num) {
case 1:
printf("s1.a=%d\n", u->s1.a);
break;
case 2:
printf("s2.b=%f\n", u->s2.b);
break;
case 1:
printf("s3.c=%s\n", u->s3.c);
break;
}
}
...
union various u;
u.s1.num = 1;
u.s1.a = 4;
foo(&u);
u.s2.num = 2;
u.s2.b = 2.5;
foo(&u);
u.s3.num = 3;
u.s3.c = "hello";
foo(&u);