6.7.2.1 В параграфе 14 моего проекта стандарта C99 сказано следующее о соединениях и указателях (выделение, как всегда, добавлено):
Достаточно указать размер объединениясамый большой из его членов.Значение не более одного из членов может быть сохранено в объекте объединения в любое время. Указатель на объект объединения, соответствующим образом преобразованный, указывает на каждый из его элементов (или, если элемент является битовым полем, затем на единицу, в которой он находится), и наоборот.
Все хорошо, это означает, что законно делать что-то вроде следующего, чтобы копировать подписанное или неподписанное целое в объединение, предполагая, что мы только хотим скопировать его в данные того же типа:
union ints { int i; unsigned u; };
int i = 4;
union ints is = *(union ints *)&i;
int j = is.i; // legal
unsigned k = is.u; // not so much
7.15.1.1 параграф 2 имеет следующее:
Макрос va_arg
расширяется до выражения, которое имеет указанный тип и значение следующего аргумента ввызов.Параметр ap
должен быть инициализирован макросом va_start
или va_copy
(без промежуточного вызова макроса va_end
для sameap).Каждый вызов макроса va_arg
изменяет ap
, так что значения последовательных аргументов возвращаются по очереди.Параметр type
должен быть именем типа, указанным так, чтобы тип указателя на объект, имеющий указанный тип, можно было получить простым постфиксом от *
до type
.Если фактического следующего аргумента нет, или если тип не совместим с типом фактического следующего аргумента (как продвигается в соответствии с продвижением аргумента по умолчанию), поведение не определено, за исключением следующих случаев:
- один тип является целочисленным типом со знаком, другой тип является соответствующим целочисленным типом без знака, и значение может быть представлено в обоих типах;
- один тип являетсяуказатель на void, а другой указатель на тип символа.
Я не собираюсь приводить часть о продвижении аргументов по умолчанию.Мой вопрос: это определенное поведение:
void func(int i, ...)
{
va_list arg;
va_start(arg, i);
union ints is = va_arg(arg, union ints);
va_end(arg);
}
int main(void)
{
func(0, 1);
return 0;
}
Если это так, то кажется, что преодоление требования "и значение совместимо с обоими типами" преобразования целых чисел со знаком (без знака) (это совместимо с обоими типами) (хотя это довольно сложно сделать с юридической точки зрения).Если нет, то было бы безопасно использовать unsigned
в этом случае, но что, если в union
было больше элементов с более несовместимыми типами?Если мы можем гарантировать, что мы не получим доступ к объединению по элементу (т.е. мы просто скопируем его в другой union
или пространство хранения, которое мы рассматриваем как union
), и что все элементы объединения имеют одинаковый размерэто разрешено с varargs?Или это будет разрешено только с указателями?
На практике я ожидаю, что этот код почти никогда не потерпит неудачу, но я хочу знать, определено ли его поведение.Мое текущее предположение состоит в том, что это, кажется, не определено, но кажется невероятно глупым.