Арифметика указателя для пустого указателя в C - PullRequest
153 голосов
/ 19 августа 2010

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

Ответы [ 8 ]

253 голосов
/ 19 августа 2010

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

Примечание редактора: Этот ответ был отредактирован с учетом окончательного заключения.

57 голосов
/ 19 августа 2010

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

16 голосов
/ 19 августа 2010

приведите его к указателю на символ, увеличивая указатель вперед на х байтов.

14 голосов
/ 19 августа 2010

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

11 голосов
/ 15 сентября 2017

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

стандарт C11 §6.2.5

Абзац - 19

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

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

#include<stdio.h>

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

Возможно, другие компиляторы выдают ошибку.

8 голосов
/ 19 августа 2010

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

5 голосов
/ 22 января 2013

Указатели Void могут указывать на любой фрагмент памяти. Следовательно, компилятор не знает, сколько байтов будет увеличиваться / уменьшаться, когда мы пытаемся использовать арифметику указателя на пустом указателе. Следовательно, указатели типа 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.
0 голосов
/ 13 марта 2015

Компилятор знает тип приведения.Если void *x:

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

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

...