Окончательный вывод: арифметика на void*
это незаконно и в C и C ++.
GCC допускает это как расширение, см. Арифметические void
и функциональные указатели (обратите внимание, что этот раздел является частью главы «Расширения C» в руководстве). Clang и ICC, вероятно, разрешают void*
арифметику в целях совместимости с GCC. Другие компиляторы (такие как MSVC) не разрешают арифметику void*
, и GCC запрещает ее, если -pedantic-errors
указан флаг или -Werror-pointer-arith
указан флаг (этот флаг полезен, если ваша кодовая база также должна компилироваться с MSVC).
Стандарт C говорит
Цитаты взяты из проекта N1256.
Стандартное описание операции сложения гласит:
6.5.6-2: Кроме того, либо оба операнда должны иметь арифметический тип, либо один операнд должен быть указателем на тип объекта, а другой - целочисленным.
Таким образом, вопрос здесь заключается в том, является ли void*
указатель на «тип объекта» или, что эквивалентно, void
является ли «тип объекта». Определение типа объекта:
6.2.5.1: Типы делятся на типы объектов (типы, которые полностью описывают объекты), типы функций (типы, которые описывают функции) и неполные типы (типы, которые описывают объекты, но не имеют информации, необходимой для определения их размеров).
И стандарт определяет void
как:
6.2.5-19: void
тип содержит пустой набор значений; это неполный тип, который не может быть завершен.
Поскольку void
это неполный тип, это не тип объекта. Поэтому он не является допустимым операндом для операции сложения.
Поэтому вы не можете выполнять арифметику указателя на void
указателе.
Ноты
Первоначально считалось, что void*
арифметика была разрешена из-за этих разделов стандарта C:
6.2.5-27: указатель на void должен иметь те же
требования к представлению и выравниванию, что и указатель на тип символа.
Тем не мение,
Те же
требования к представлению и выравниванию подразумевают взаимозаменяемость в качестве аргументов функций, возвращаемых значений функций и членов объединений.
Таким образом, это означает, что printf("%s", x)
имеет то же значение, x
имеет ли тип char*
или void*
, но это не означает, что вы можете делать арифметику на void*
.
Примечание редактора: этот ответ был отредактирован, чтобы отразить окончательный вывод.