Окончательный вывод: арифметика на 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*.
Примечание редактора: этот ответ был отредактирован, чтобы отразить окончательный вывод.