вычитание двух адресов дает неверный результат

int main()
{
    int x = 4;
    int *p = &x;
    int *k = p++;
    int r = p - k;
    printf("%d %d %d", p,k,p-k);
    getch();
}

Выход:

2752116 2752112 1

Почему не 4?

А также я не могу использовать p+k или любой другой оператор, кроме - (вычитание).


person Community    schedule 12.06.2017    source источник
comment
Сначала попробуйте это: int r = (int)((char *)p - (char *)k). Сообщите нам о результате. Позже мы обсудим результат.   -  person tilz0R    schedule 12.06.2017
comment
Ваш код вызывает неопределенное поведение. %d ожидает параметр int, вы передаете указатели и ptrdiff_t. И указатели на разные разные массивы (один объект рассматривается как массив из 1 элемента) не могут быть вычтены (соответственно, разница не имеет смысла). Вы должны прочитать указатели на бой в хорошей книге C. Как вы думаете, что должна дать сумма двух указателей?   -  person too honest for this site    schedule 12.06.2017


Ответы (4)


Прежде всего, вы ДОЛЖНЫ использовать правильный тип аргумента для предоставленного спецификатора формата, предоставление несовпадающего типа аргументов приводит к неопределенное поведение.

  • Вы должны использовать спецификатор формата %p и привести аргумент к void * для печати адреса (указателей)

  • Чтобы распечатать результат вычитания указателя, вы должны использовать %td, так как результат имеет тип ptrdiff_t.


Тем не менее, что касается результата 1 для вычитания, арифметика указателя учитывает тип данных. Цитата из C11, глава §6.5.6 (выделено мной)

Когда два указателя вычитаются, оба должны указывать на элементы одного и того же объекта массива или один после последнего элемента объекта массива; результатом является разница индексов двух элементов массива. Размер результата определяется реализацией, и его тип (целочисленный тип со знаком) определяется ptrdiff_t в заголовке <stddef.h>. [....] если выражения P и Q указывают соответственно на i-й и j-й элементы массива, выражение (P)-(Q) имеет значение i−j при условии, что это значение соответствует объекту типа ptrdiff_t. [....]

Итак, в вашем случае индексы для p и k разделены на один элемент, т. е. |i-J| == 1, отсюда и результат.


Наконец, вы не можете сложить (или умножить или разделить) два указателя, потому что это бессмысленно. Указатели — это ячейки памяти, и логически вы не можете иметь смысла добавлять две ячейки памяти. Только вычитание имеет смысл, чтобы найти связанное расстояние между двумя членами/элементами массива.

Связанные ограничения, из C11, глава §6.5.6, аддитивные операторы,

Кроме того, либо оба операнда должны иметь арифметический тип, либо один операнд должен быть указателем на полный тип объекта, а другой должен иметь целочисленный тип. (Увеличение эквивалентно добавлению 1.)

person Sourav Ghosh    schedule 12.06.2017

Вы получаете разницу между индексами двух элементов.
C11-6.5.6p9:

Когда два указателя вычитаются, оба должны указывать на элементы одного и того же объекта массива или один после последнего элемента объекта массива; результатом является разница индексов двух элементов массива.

Также обратите внимание, что заявление

 printf("%d %d %d", p,k,p-k);  

должно быть

printf("%p %p %ld\n", (void*)p,(void*)k, p-k);
person haccks    schedule 12.06.2017

Если ваша переменная имеет тип указателя, то каждое вычисление указателя выполняется путем умножения размера типа указателя.

Например:

//Lets assume char is 1 byte, int is 4 bytes long.
// sizeof(*cp) = 4, sizeof(*ip) = 4;
char *cp = (char *)10;  //Char itself is 1 byte
int *ip = (int *)10;
cp++;          //Increase pointer, let us point to the next char location
ip++;          //Increase pointer, let us point to the next int location

printf("Char: %p\r\n", (void *)cp); //Prints 11
printf("Int: %p\r\n", (void *)ip); //Prints 14

В первом случае печатается 11, а во втором - 14. Это потому, что следующий элемент char идет на 1 байт дальше, а следующий элемент int — на 4 байта вперед.

Если у вас есть 2 указателя одного типа (например, int *, как у вас), то, если один указывает на 14, а другой на 10, между ними находится память 1 int, вычитание дает вам 1.

Если вы хотите получить результат 4, перед вычислением приведите указатели к char *, потому что sizeof(char) всегда равно 1, что означает, что у вас есть 4 элементов между адресуемыми 10 и 14, и вы получите результат 4.

Надеюсь, поможет.

person tilz0R    schedule 12.06.2017
comment
Я согласен с первым, но для второго это будет работать, если uint32_t и длина указателя одинаковы. В противном случае, опять же, я согласен с void * @DavidBowling. Но я добавил это sizeof(*cp) = 4, sizeof(*ip) = 4;, поэтому мы предполагаем, что int * равно 4 байтам, и uint32_t также равно 4 байтам. В этом случае будет работать нормально. Теперь вы можете очистить отрицательный голос ;) - person tilz0R; 12.06.2017

Прежде всего, добавление 2 указателей не определено. поэтому, если вы используете оператор +, вы столкнетесь с компиляцией error.

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

Ты говоришь :

int* p1 = &x;
int* p2 = p1++;

Итак, между p1 и p2 есть 4 байта. они оба типа int. так что между ними только 1 коробка int.

person Fatemeh Karimi    schedule 12.06.2017