Различие между arr и &arr – как в C определить размер массива без sizeof
от kalterfx
Hey folks, Long time no C.
Обычно в C мы находим длину массива arr так:
Здесь мы получаем размер массива в байтах; затем происходит деление этого размера на размер каждого элемента в массиве. Давайте попробуем избавиться от sizeof.
Никто из вас никогда не задавался вопросом насчёт разницы между arr и &arr? Это не одно и то же.
Мы можем определиться в следующем:
(arr + 1) указывает на 0x244fdc8 – это значение уходит на 4 байта вперёд от arr, который указывает на 0x244fdc4.
Так как переменные типа int занимают 4 байта, (arr + 1) указывает на второй элемент массива.
(&arr + 1) указывает на 0x244fdd8 – это значение уходит на 20 байт вперёд от arr, который указывает на 0x244fdc4.
(0x244fdd8 - 0x244fdc4 = 0x14 = 20)
Теперь можно сделать вывод о том, что не смотря на то, что arr и &arr указывают на одно и то же место в памяти, их применение совершенно разное.
arr имеет тип int *, в то время как &arr имеет тип int (*)[size].
&arr указывает на весь массив, в то время как arr указывает на первый элемент массива.
Из этого можно извлечь один полезный опыт – получение длины массива.
*(&arr + 1) даёт нам адрес за последним элементом массива, а arr адрес первого элемента массива.
Таким образом, вычитание второго из первого может дать нам длину массива.
Мы можем это упростить, используя индексы массива (так как x[1] == *(x + 1) ):
PS:
Этот метод работает только для массивов, но не для указателей (вроде char *str).
В этом случае &str является указателем, который указывает на указатель str. Стоит запомнить, что массивы в С не являются указателями.
Послесловие:
Я люблю цитировать этот вопрос‐ответ, который я нашёл на reddit / SO об обсужденной теме.
Q: Доступ к первому адресу после массива, кажется, предшествует неопределённому поведению (undefined behavior). Например, если массив располагается в самом конце всего адресного пространства, адрес, указывающий на первый элемент после массива, может вызвать переполнение и результирующий размер массива может быть любым. Как в этом случае можно обратиться к (&arr)[1]?
A: C не позволяет получить доступ к памяти за последним элементом массива. Тем не менее, C позволяет указателю указывать на один элемент вперёд за последним элементом массива. Разница важна.
Это мой вольный перевод статьи от Arjun Sreedharan, изначально написанной в блоге arjunsreedharan.org
Обычно в C мы находим длину массива arr так:
- int n = sizeof(arr) / sizeof(arr[0]);
Здесь мы получаем размер массива в байтах; затем происходит деление этого размера на размер каждого элемента в массиве. Давайте попробуем избавиться от sizeof.
Никто из вас никогда не задавался вопросом насчёт разницы между arr и &arr? Это не одно и то же.
Мы можем определиться в следующем:
(arr + 1) указывает на 0x244fdc8 – это значение уходит на 4 байта вперёд от arr, который указывает на 0x244fdc4.
Так как переменные типа int занимают 4 байта, (arr + 1) указывает на второй элемент массива.
(&arr + 1) указывает на 0x244fdd8 – это значение уходит на 20 байт вперёд от arr, который указывает на 0x244fdc4.
(0x244fdd8 - 0x244fdc4 = 0x14 = 20)
Теперь можно сделать вывод о том, что не смотря на то, что arr и &arr указывают на одно и то же место в памяти, их применение совершенно разное.
arr имеет тип int *, в то время как &arr имеет тип int (*)[size].
&arr указывает на весь массив, в то время как arr указывает на первый элемент массива.
Из этого можно извлечь один полезный опыт – получение длины массива.
*(&arr + 1) даёт нам адрес за последним элементом массива, а arr адрес первого элемента массива.
Таким образом, вычитание второго из первого может дать нам длину массива.
- int n = *(&arr + 1) - arr;
Мы можем это упростить, используя индексы массива (так как x[1] == *(x + 1) ):
- int n = (&arr)[1] - arr;
PS:
Этот метод работает только для массивов, но не для указателей (вроде char *str).
- void reverseStr(char *str)
- {
- // wrong
- int strlength = (&str)[1] - str;
- }
В этом случае &str является указателем, который указывает на указатель str. Стоит запомнить, что массивы в С не являются указателями.
Послесловие:
Я люблю цитировать этот вопрос‐ответ, который я нашёл на reddit / SO об обсужденной теме.
Q: Доступ к первому адресу после массива, кажется, предшествует неопределённому поведению (undefined behavior). Например, если массив располагается в самом конце всего адресного пространства, адрес, указывающий на первый элемент после массива, может вызвать переполнение и результирующий размер массива может быть любым. Как в этом случае можно обратиться к (&arr)[1]?
A: C не позволяет получить доступ к памяти за последним элементом массива. Тем не менее, C позволяет указателю указывать на один элемент вперёд за последним элементом массива. Разница важна.
Это мой вольный перевод статьи от Arjun Sreedharan, изначально написанной в блоге arjunsreedharan.org