Подтвердить что ты не робот

Путаница вычитания указателя

Когда мы вычитаем указатель из другого указателя, разница не равна количеству байтов, которые они разделены, но равна количеству целых чисел (если указывать на целые числа), они разнесены. Почему так?

4b9b3361

Ответ 1

Идея состоит в том, что вы указываете на блоки памяти

+----+----+----+----+----+----+
| 06 | 07 | 08 | 09 | 10 | 11 | mem
+----+----+----+----+----+----+
| 18 | 24 | 17 | 53 | -7 | 14 | data
+----+----+----+----+----+----+

Если у вас int* p = &(array[5]), тогда *p будет 14. Переход p=p-3 сделает *p равным 17.

Итак, если у вас есть int* p = &(array[5]) и int *q = &(array[3]), тогда p-q должно быть 2, потому что указатели указывают на память, разделяющую 2 блока.

При работе с необработанной памятью (массивы, списки, карты и т.д.) нарисуйте много ящиков! Это действительно помогает!

Ответ 2

Потому что все в области указателя - это смещения. Когда вы говорите:

int array[10];
array[7] = 42;

Что вы на самом деле говорите во второй строке:

*( &array[0] + 7 ) = 42;

Буквально переводится как:

* = "what at"
(
  & = "the address of"
  array[0] = "the first slot in array"
  plus 7
)
set that thing to 42

И если мы можем добавить 7, чтобы сделать точку смещения в нужном месте, мы должны иметь возможность иметь противоположное место, иначе у нас нет симметрии в нашей математике. Если:

&array[0] + 7 == &array[7]

Затем для здравомыслия и симметрии:

&array[7] - &array[0] == 7

Ответ 3

Таким образом, ответ будет одинаковым даже на платформах, где целые числа имеют разную длину.

Ответ 4

Скажем, у вас есть массив из 10 целых чисел:

int intArray[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

Затем вы берете указатель на intArray:

int *p = intArray;

Затем вы увеличиваете p:

p++;

Что вы ожидаете, потому что p начинается с intArray[0], для приращенного значения p будет intArray[1]. Вот почему так выглядит арифметика указателя. Смотрите код здесь

Ответ 5

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

Проверьте больше здесь.

Ответ 6

Этот способ вычитания указателя ведет себя в соответствии с поведением сложения указателя. Это означает, что p1 + (p2 - p1) == p2 (где p1 и p2 - указатели на один и тот же массив).

Добавление указателя (добавление целого числа к указателю) ведет себя аналогичным образом: p1 + 1 дает вам адрес следующего элемента в массиве, а не следующего байта в массиве - что было бы довольно бесполезно и небезопасно для делать.

Язык мог бы быть спроектирован таким образом, чтобы указатели добавлялись и вычитались так же, как целые числа, но это означало бы, что арифметика указателей будет по-разному написана, и при этом потребуется учитывать размер типа, на который указывают:

  • p2 = p1 + n * sizeof(*p1) вместо p2 = p1 + n
  • n = (p2 - p1)/sizeof(*p1) вместо n = p2 - p1

Таким образом, результатом будет более длинный и трудный для чтения код, в котором легче будет ошибаться.

Ответ 7

При применении арифметических операций над указателями определенного типа вы всегда хотите, чтобы результирующий указатель указывал на адрес "допустимый" (то есть правый размер шага) адрес памяти относительно исходной точки. Это очень удобный способ доступа к данным в памяти независимо от базовой архитектуры.

Если вы хотите использовать другой "шаг-размер", вы всегда можете указать указатель на нужный тип:

int a = 5;
int* pointer_int = &a;
double* pointer_double = (double*)pointer_int; /* totally useless in that case, but it works */

Ответ 8

@fahad Арифметика указателя проходит по размеру типа данных, который он указывает. Поэтому, когда указатель ur имеет тип int, вы должны ожидать арифметики указателя в размере int (4 байта). Аналогично для указателя char все операции над указатель будет в 1 байт.