Вопрос

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

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

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

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

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

Это было полезно?

Решение

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

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

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

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

Другие советы

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

Когда вы увеличиваете указатель на 1 (p+1), указатель будет указывать на следующий действительный адрес путем добавления (p + sizeof(Type)) байтов к p.(если тип — 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

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

Чтобы ответить на ваш «обновленный» вопрос, еще раз выполняется арифметика указателей (на этот раз сложение указателей).В C это сделано таким образом, чтобы упростить «индексацию» фрагмента смежных данных, на который указывает указатель.

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

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

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

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

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

Ре:«Кроме того, какой тип последней строки должен быть? Целочисленный или целочисленный указатель??»

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

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

printf("&E[2]-E:\t0x%x\n",(char*)(&a[2])-(char*)(&a[0]))
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top