Смешение адреса массива C

Скажем, у нас есть следующий код:

int main(){
    int a[3]={1,2,3};
    printf("      E: 0x%x\n", a);
    printf("  &E[2]: 0x%x\n", &a[2]);
    printf("&E[2]-E: 0x%x\n", &a[2] - a);
    return 1;
}

При компиляции и запуске результаты следующие:

      E: 0xbf8231f8
  &E[2]: 0xbf823200
&E[2]-E: 0x2

Я понимаю результат & E [2], который равен 8 плюс адрес массива, поскольку индексируется по 2 и типа int (4 байта на моей 32-битной системе), но я не может понять, почему последняя строка равна 2 вместо 8?

Кроме того, какой тип последней строки должен быть - целым или целым указателем?

Интересно, является ли это системой типа C (kinda casting), которая делает эту причуду?

Ответ 1

Вы должны помнить, что означает выражение a[2]. Это в точности эквивалентно *(a+2). Настолько же, что вполне законно писать 2[a] вместо этого с одинаковым эффектом.

Для того, чтобы работать и иметь смысл, арифметика указателя учитывает тип указываемой вещи. Но об этом заботятся за кулисами. Вы просто используете естественные смещения в своих массивах, и все детали просто работают.

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

Под капотом в вашем примере индекс умножается на sizeof(int), чтобы получить смещение байта, которое добавляется к базовому адресу массива. Вы раскрываете эту деталь в своих двух отпечатках адресов.

Ответ 2

При вычитании указателей одного и того же типа результатом является количество элементов, а не количество байтов. Это по дизайну, чтобы вы могли легко индексировать массивы любого типа. Если вы хотите количество байтов - введите адреса в char *.

Ответ 3

Когда вы увеличиваете указатель на 1 (p + 1), указатель указывает на следующий действительный адрес, добавляя (p + sizeof (Type)) байты в p. (если Type is int, то p + sizeof (int))

Аналогичная логика хороша и для p-1 (конечно, вычесть в этом случае).

Если вы просто примените эти принципы здесь:

Простыми словами:

a[2] can be represented as (a+2)
a[2]-a      ==>  (a+2) - (a)    ==> 2

Итак, за сценой,

a[2] - a[0]  
==> {(a+ (2* sizeof(int)) ) - (a+0) }  / sizeof(int) 
==> 2 * sizeof(int) / sizeof(int) ==> 2

Ответ 4

Линия & E [2] -2 выполняет вычитание указателя, а не целочисленное вычитание. Вычитание указателя (когда оба указателя указывают на данные того же типа) возвращает разность адресов в делении на размер типа, на который они указывают. Возвращаемое значение - это int.

Чтобы ответить на ваш вопрос "обновления", снова выполняется арифметика указателя (это добавление указателя времени). Это делается на C, чтобы упростить "индексирование" фрагмента смежных данных, на которые указывает указатель.

Ответ 5

Возможно, вас заинтересует Арифметика указателей в C вопроса и ответов.

в основном, операторы + и - принимают размер элемента при использовании в указателях.

Ответ 6

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

Если у вас есть указатель int и добавьте номер 2 к нему, он будет продвигать 2 * sizeof (int). Таким же образом, если вы вычтите два указателя int, вы получите результат в единицах sizeof (int), а не в разнице абсолютных адресов.

(У указателей, использующих размер типа данных, довольно удобно, так что вы, например, можете просто использовать p++ вместо того, чтобы каждый раз указывать размер этого типа: p+=sizeof(int).)

Ответ 7

Re: "В добавлении, какой тип последней строки должен быть? Целое число или целочисленный указатель?"

целое число/число. тем самым, что: Сегодня - 1 апреля = число. не дата

Ответ 8

Если вы хотите увидеть разницу в байтах, вам понадобится тип размером 1 байт, например:

printf("&E[2]-E:\t0x%x\n",(char*)(&a[2])-(char*)(&a[0]))