Как определяется арифметика с массивом &
Я получаю большую часть арифметики указателей, пока не увидел следующее:
int x[5];
sizeof(x) // equals 20
sizeof(&x) // equals 4 -- sizeof(int))
До сих пор я придаю этому смысловое значение:
указатель на N-элементный массив T - в случае &x
x+1
, мы увеличиваем с sizeof(int)
, а когда мы делаем &x+1
, мы увеличиваем с sizeof(x)
.
Есть ли какая-то лежащая в основе этого логика, то есть некоторые эквивалентности, потому что это кажется очень неинтуитивным.
/ edit, благодаря @WhozCraig я пришел к выводу, что сделал Ошибка:
sizeof(&x) // equals 4 -- sizeof(int))
Должно быть
sizeof(&x) // equals 8 -- sizeof(int))
Урок усвоен: не отправляйте код, который вы не запускали напрямую
4 ответа:
Называется типизированного указателя математика (или типизированный указатель-арифметика), и интуитивно понятен, когда вы получаете одно засело в вашей ДНК: указатель математика регулирует адресов на основе типа указателя, который содержит Саид-адрес.
В вашем примере, что такое тип из
x
? Это массивint
. но каков тип выраженияx
? Хммм. Согласно стандарту, значение выраженияx
является адресом первого элемента массива, а тип является типом указателя на элемент, в данном случае указателем на -int
.Тот же стандарт диктует, что для любых данных var (функции немного странные) использование оператора
&
приводит к адресу с типом указателя на тип, тип которого является любым типом переменной:Например, дано
int a;
Выражение
&a
приводит к адресу, которыйтипа являетсяint *
. Аналогично,struct foo { int x,y,z } s;
Выражение
&s
приводит к адресу, которыйтипа являетсяstruct foo *
.А теперь, пункт вероятной путаницы, учитывая:
int x[5];
Выражение
&x
приводит к адресу, которыйтипа являетсяint (*)[5]
, то есть указатель на массив из пятиint
. Это заметно отличается от простогоx
, который, согласно стандарту, вычисляется как адрес, тип которого является указателем на базовый массив тип элементаПочему разве это имеет значение? Потому что вся арифметика указателей основана на этом фундаментальном типе адреса выражения. Корректировки в нем с использованием типизированной математики указателей зависят от этой фундаментальной концепции.
int x[5]; x + 1
Эффективно делает это:
int x[5]; int *p = x; p + 1 // results is address of next int
Тогда как:
&x + 1
Эффективно делает это:
int x[5]; int (*p)[5] = &x; p + 1 // results in address of next int[5] // (which, not coincidentally, there isn't one)
Что касается
sizeof()
дифференциала, то опять же, эти надоедливые типы возвращаются домой к насесту, и в частности различие, важно отметить чтоsizeof
является оператором времени компиляции, а не времени выполнения:int x[5] size_t n = sizeof(x);
В приведенном выше,
sizeof(x)
приравнивается кsizeof(type-of x)
. Так какx
- этоint[5]
, аint
- это, по-видимому, 4 байта в вашей системе, результат 20. Аналогично,int x[5]; size_t n = sizeof(*x);
Результаты с
sizeof(type-of *x)
начинают присваиватьсяn
. Поскольку*x
относится к типуint
, это синонимичноsizeof(int)
. Аспекты времени компиляции, между прочим, делают следующее одинаково действительным, хотя, по общему признанию, это выглядит немного опасным на первый взгляд взгляд:int *p = NULL; size_t n = sizeof(*p);
Как и раньше,
sizeof(type-of *p)
приравнивается кsizeof(int)
Но как насчет:
int x[5]; size_t n = sizeof(&x);
Здесь снова
sizeof(&x)
приравнивается кsizeof(type-of &x)
. но мы только что рассмотрели, что такое Тип&x
; егоint (*)[5]
. То есть его тип Datapointer , и поэтому его размер будет равен размеру указателя . На вашей установке, по-видимому, есть 32-битные указатели, так как размер сообщения равен 4.Пример того, как
&x
является типом указателя, и это действительно все Данные типы указателей приводят к одинаковому размеру, я закрываю следующим примером:#include <stdio.h> int main() { int x[5]; double y[5]; struct foo { char data[1024]; } z[5]; printf("%zu, %zu, %zu\n", sizeof(x[0]), sizeof(x), sizeof(&x)); printf("%zu, %zu, %zu\n", sizeof(y[0]), sizeof(y), sizeof(&y)); printf("%zu, %zu, %zu\n", sizeof(z[0]), sizeof(z), sizeof(&z)); return 0; }
Выход (Mac OSX 64bit)
4, 20, 8 8, 40, 8 1024, 5120, 8
Обратите внимание, что последние отчеты о размере значенияидентичны .
X имеет тип int[5], поэтому &x-указатель на целочисленный массив из пяти элементов, при добавлении 1 к &x вы увеличиваете до следующего массива из 5 элементов.
Вы сказали: "я получаю большую часть арифметики указателя, пока не увидел следующее:"
int x[5]; sizeof(x) // equals 20 sizeof(&x) // equals 4 -- sizeof(int))
Исследование первого sizeof...
if ((sizeof(int) == 4) == true) { then the size of five tightly packed ints is 5 * 4 so the result of (sizeof(int[5]) is 20. }
Однако...
if (size(int)) == 4) is true then when the size of the memory holding the value of another memory address is 4, ie. when ((sizeof(&(int[5])) == 4) { it is a cooincidence that memory addresses conveniently fit into the same amount of memory as an int. } }
Не обманывайтесь, адреса памяти традиционно имеют тот же размер, что и int на некоторых очень популярных платформах, но если вы когда-нибудь поверите, что они имеют тот же размер, вы предотвратите запуск вашего кода на многих платформах.
Чтобы еще больше загнать точку домой
it is true that (sizeof(char[4]) == 4), but that does not mean that a `char[4]` is a memory address.
Теперь в C оператор смещения для адреса памяти "знают" смещение, основанное на типе указателя, char, int или подразумеваемом размере адреса. Когда вы добавляете указатель, компилятор преобразует это добавление в операцию, которая выглядит примерно так
addressValue += sizeof(addressType)*offsetCount
Где
&x + 1
Становится
x += sizeof(x)*1;
Обратите внимание, что если вы действительно хотите получить удовольствие (очень небезопасное Программирование), вы можете привести свой тип указателя небезопасно и указать смещения, которые действительно "не работают" так, как они должны.
int x[5]; int* xPtr = &x; char* xAsCharPtr = (char*) xPtr; printf("%d", xAsCharPtr + 2);
Распечатаем число, состоящее примерно из 1/2 битов чисел в x [0] и x[1].
Кажется, что неявное преобразование находится в игре, благодаря превосходному ответу в каком-то другом арифметическом вопросе указателя, я думаю, что он сводится к:
Когда
x
является выражением, оно может быть прочитано как&x[0]
из-за неявного преобразования, добавление 1 к этому выражению интуитивно имеет больше смысла, чем мы хотим&x[1]
. При выполненииsizeof(x)
неявное преобразование не происходит, давая общий размер объекта x. арифметика с&x+1
имеет смысл также при рассмотрении того, что&x
является указателем к массиву из 5 элементов.То, что не становится интуитивным, - это sizeof (&x), можно было бы ожидать, что он также будет иметь размер
x
, но это размер элемента в массиве, на который указывают,x
.