Арифметика указателя для указателя void в C

Когда указатель на определенный тип (например, int , char , float , ..) увеличивается, его значение увеличивается на размер этого типа данных. Если указатель void указывающий на данные размера x , увеличивается, как он достигает точки x байтов впереди? Как компилятор знает, чтобы добавить x к значению указателя?

Окончательный вывод: арифметика на void* является незаконной как в C, так и в C ++.

GCC позволяет использовать его как расширение, см. Арифметику на указателях void и Function-Указатели (обратите внимание, что этот раздел является частью главы «C Расширения» руководства). Clang и ICC, вероятно, допускают арифметику void* для целей совместимости с GCC. Другие компиляторы (например, MSVC) отклоняют арифметику на void* , а GCC запрещает ее, если -pedantic-errors флаг -pedantic-errors , или если -Werror-pointer-arith флаг -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* .

Примечание редактора: этот ответ был отредактирован, чтобы отразить окончательный вывод.

Арифметика указателя не допускается по указателям void* .

нарисуйте указатель на char, указав указатель вперед x байтов вперед.

Именно по этой причине вы не можете выполнять арифметику указателей на типах void * !

Перед выполнением арифметики указателя вы должны направить ее на другой тип указателя.

Указатели Void могут указывать на любой fragment памяти. Следовательно, компилятор не знает, сколько байтов для увеличения / уменьшения при попытке арифметики указателя на указателе пустоты. Поэтому указатели void должны быть сначала приведения типов к известному типу, прежде чем они могут быть вовлечены в любую арифметику указателя.

 void *p = malloc(sizeof(char)*10); p++; //compiler does how many where to pint the pointer after this increment operation char * c = (char *)p; c++; // compiler will increment the c by 1, since size of char is 1 byte. 

Стандарт C не допускает арифметики указателей пустот . Тем не менее, GNU C допускается, учитывая размер void : 1 .

Стандарт С11 §6.2.5

Пункт – 19

Тип void содержит пустой набор значений; это неполный тип объекта, который не может быть завершен.

Следующая программа отлично работает в компиляторе GCC.

 #include int main() { int arr[2] = {1, 2}; void *ptr = &arr; ptr = ptr + sizeof(int); printf("%d\n", *(int *)ptr); return 0; } 

Может быть, другие компиляторы генерируют ошибку.

Компилятор знает по типу. Учитывая void *x :

  • x+1 добавляет один байт в x , указатель переходит к байту x+1
  • (int*)x+1 добавляет sizeof(int) байты, указатель переходит к байту x + sizeof(int)
  • (float*)x+1 байты addres sizeof(float) и т. д.

Хотя первый элемент не является переносимым и противоречит Galateo C / C ++, он тем не менее корректен на языке C, то есть он скомпилирует что-то на большинстве компиляторов, возможно, требуя соответствующего флага (например, -Wpointer-arith)

Interesting Posts

Есть ли уловка для подключения и использования Windows 8 / 8.1 с Samba 4 (4.1.6)?

Изменение времени перезапуска обновления обновления в Windows XP

VirtualBox из существующего раздела

Есть ли все-таки, чтобы предотвратить ошибку «Outlook использует старый файл .ost»?

Существуют ли какие-либо различия между окончательными и проектными спецификациями 802.11n?

Как называется дефис-разделенный случай?

Как поместить комментарий линии для многострочной команды

Как экспортировать адреса электронной почты из Apple Mail?

Как реализовать FSM-конечный автомат в Java

Я пожарил мою материнскую плату или мой блок питания?

jquery Event.stopPropagation (), похоже, не работает

Как маршрутизировать трафик через определенный прокси-сервер SOCKS для каждого приложения?

Xcode 8 / Swift 3: простой код UIPicker не работает

Что случилось с переопределяемыми вызовами метода в конструкторах?

Получить IP-адрес удаленного хоста

Давайте будем гением компьютера.