Путаница с адресами массива 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
Я понимаю результат &Е[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]))