Подтвердить что ты не робот

Является ли использование memcmp в массиве int строго соответствующим?

Является ли следующая программа строго соответствующей программой в C? Меня интересуют c90 и c99, но ответы c11 также приемлемы.

#include <stdio.h>
#include <string.h>

struct S { int array[2]; };

int main () {
    struct S a = { { 1, 2 } };
    struct S b;
    b = a;
    if (memcmp(b.array, a.array, sizeof(b.array)) == 0) {
        puts("ok");
    }
    return 0;
}

В комментариях к моему ответу в другом вопросе, Эрик Postpischil настаивает, что выход программы будет меняться в зависимости от платформы, в первую очередь из-за возможности неинициализированных битов заполнения, Я думал, что назначение struct будет перезаписывать все биты в b таким же, как в a. Но C99, похоже, не предлагает такую ​​гарантию. Из раздела 6.5.16.1 p2:

В простом присваивании (=) значение правильного операнда преобразуется в тип выражения присваивания и заменяет значение, хранящееся в объекте, обозначенном левым операндом.

Что означает "преобразованный" и "заменяет" в контексте составных типов?

Наконец, рассмотрим ту же программу, за исключением того, что определения a и b становятся глобальными. Будет ли эта программа строго соответствовать программе?

Изменить: Просто хотел обобщить часть материала для обсуждения здесь, а не добавить свой собственный ответ, так как у меня действительно нет одного из моих собственных творений.

  • Программа не является строго соответствующей. Поскольку назначение выполняется по значению, а не по представлению, b.array может содержать или не содержать биты, отличные от a.array.
  • a не нужно преобразовывать, так как он является тем же типом, что и b, но замена производится по значению и выполняется членом пользователем.
  • Даже если определения в a и b становятся глобальными, назначение post, b.array может содержать или не содержать биты, отличные от a.array. (Было мало обсуждений байтов заполнения в b, но вопрос не касался сопоставления структуры. В c99 отсутствует упоминание о том, как заполнение инициализируется в статическом хранилище, но c11 явно заявляет, что инициализируется нулем.)
  • На стороне примечания есть согласие, что memcmp корректно определен, если b был инициализирован memcpy из a.

Спасибо всем, кто участвовал в обсуждении.

4b9b3361

Ответ 1

В C99 §6.2.6

§6.2.6.1 Общие положения

1 Представления всех типов не указаны, кроме как указано в этом подпункте.

[...]

4 [..] Два значения (кроме NaN) с одним и тем же представлением объектов сравниваются равными, но значения, сравнивающие одинаковые, могут иметь разные представления объектов.

6 Когда значение хранится в объекте структуры или типа объединения, в том числе в объекте-члене, байты представления объекта, которые соответствуют любым байтам заполнения, принимают неопределенные значения. 42)

42) Таким образом, например, назначение структуры не должно копировать любые биты дополнений.

43) Объекты x и y могут иметь одинаковый эффективный тип T, если они доступны как объекты типа T, но имеют разные значения в других контекстах. В частности, если для типа T определено ==, то x == y не означает, что memcmp (& x, & y, sizeof (T)) == 0. Кроме того, x == y не обязательно означает что x и y имеют одинаковое значение; другие операции над значениями типа T могут различать их.

§6.2.6.2 Целочисленные типы

[...]

2 Для знаковых целых типов биты представления объекта должны быть разделены на три группы: биты значений, биты заполнения и знаковый бит. Не должно быть никаких битов заполнения, [...]

[...]

5 Значения любых дополняющих битов не заданы. [...]

В J.1 Unspecified Behavior

  • Значение байтов заполнения при хранении значений в структурах или объединениях (6.2.6.1).

[...]

  • Значения любых битов заполнения в целых представлениях (6.2.6.2).

Поэтому в представлении a и b могут быть биты, которые отличаются друг от друга, не влияя на значение. Это тот же вывод, что и другой ответ, но я думал, что эти цитаты из стандарта будут хорошим дополнительным контекстом.


Если вы выполните memcpy, тогда memcmp всегда будет возвращать 0, и программа будет строго соответствовать. memcpy дублирует представление объекта a на b.

Ответ 2

Мое мнение таково, что оно строго соответствует. Согласно 4.5, упомянутый Эрик Постпишил:

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

Поведение, о котором идет речь, - это поведение memcmp, и это четко определено без каких-либо неопределенных, undefined или определенных для реализации аспектов. Он работает с сырыми битами представления, не зная ничего о значении, битах заполнения или ловушках. Таким образом, результат (но не ) memcmp в этом конкретном случае зависит от реализации значений, хранящихся в этих байтах.

Сноска 43) в 6.2.6.2:

Объекты x и y могут иметь одинаковый эффективный тип T имеют такое же значение, когда к ним обращаются как объекты типа T, но иметь разные значения в других контекстах. В частности, если == определенный для типа T, то x == y не означает, что memcmp (& x, & y, sizeof (T)) == 0. Кроме того, x == y не обязательно означает, что x и y имеют одинаковое значение; другие операции над значениями типа T могут различать их.

EDIT:

Подумав об этом немного дальше, я не уверен в том, что теперь строго соблюдаю это:

Он не должен производить вывод, зависящий от любого неуказанного [...]

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

Таким образом, это строгое не.

ИЗМЕНИТЬ 2:

Я не уверен, что он будет строго соответствовать, когда memcpy используется для копирования структуры. Согласно Приложению J неуказанное поведение происходит, когда a инициализируется:

struct S a = { { 1, 2 } };

Даже если предположить, что биты заполнения не будут меняться и memcpy всегда возвращает 0, он все равно использует биты заполнения для получения своего результата. И он полагается на предположение, что они не изменятся, но в стандарте об этом нет никакой гарантии.

Мы должны различать байты paddings в structs, используемые для выравнивания, и биты заполнения в определенных нативных типах, таких как int. Хотя мы можем с уверенностью предположить, что байты заполнения не будут меняться, но только потому, что для этого нет никакой реальной причины, то это не относится к битам заполнения. В стандарте упоминается флаг четности как пример бита заполнения. Это может быть программная функция реализации, но это может быть и аппаратная функция. Таким образом, могут быть другие аппаратные флаги, используемые для битов дополнения, в том числе для тех, которые изменяются при обращении к чтению по любой причине.

Нам будет трудно найти такую ​​экзотическую машину и ее реализацию, но я ничего не вижу, что это запрещает. Исправьте меня, если я ошибаюсь.