Почему адрес массива равен его значению в C?
В следующем фрагменте кода значения указателя и адреса указателя отличаются, как и ожидалось.
Но значения массива и адреса не имеют!
Как это может быть?
Выход
my_array = 0022FF00
&my_array = 0022FF00
pointer_to_array = 0022FF00
&pointer_to_array = 0022FEFC
#include <stdio.h>
int main()
{
char my_array[100] = "some cool string";
printf("my_array = %p\n", my_array);
printf("&my_array = %p\n", &my_array);
char *pointer_to_array = my_array;
printf("pointer_to_array = %p\n", pointer_to_array);
printf("&pointer_to_array = %p\n", &pointer_to_array);
printf("Press ENTER to continue...\n");
getchar();
return 0;
}
6 ответов
Имя массива обычно вычисляется по адресу первого элемента массива, поэтому array
а также &array
имеют одинаковое значение (но разные типы, так array+1
а также &array+1
не будет равен, если длина массива превышает 1 элемент).
Есть два исключения к этому: когда имя массива является операндом sizeof
или одинарный &
(address-of), имя относится к самому объекту массива. таким образом sizeof array
дает размер в байтах всего массива, а не размер указателя.
Для массива, определенного как T array[size]
, будет иметь тип T *
, Когда / если вы увеличиваете его, вы переходите к следующему элементу в массиве.
&array
вычисляет по тому же адресу, но, учитывая то же определение, создает указатель типа T(*)[size]
т.е. это указатель на массив, а не на отдельный элемент. Если вы увеличите этот указатель, он добавит размер всего массива, а не размер одного элемента. Например, с таким кодом:
char array[16];
printf("%p\t%p", (void*)&array, (void*)(&array+1));
Мы можем ожидать, что второй указатель будет на 16 больше первого (потому что это массив из 16 символов). Поскольку%p обычно преобразует указатели в шестнадцатеричное, это может выглядеть примерно так:
0x12341000 0x12341010
Это потому, что имя массива (my_array
) отличается от указателя на массив. Это псевдоним адреса массива, а его адрес определяется как адрес самого массива.
Однако указатель является обычной переменной C в стеке. Таким образом, вы можете взять его адрес и получить другое значение от адреса, который он содержит внутри.
Я писал об этой теме здесь - пожалуйста, посмотрите.
В C, когда вы использовали имя массива в выражении (включая передачу его функции), если только он не является операндом address-of (&
) оператор или sizeof
оператор, он распадается на указатель на свой первый элемент.
То есть в большинстве случаев array
эквивалентно &array[0]
как по типу, так и по стоимости.
В вашем примере my_array
имеет тип char[100]
который распадается на char*
когда вы передаете его в printf.
&my_array
имеет тип char (*)[100]
(указатель на массив из 100 char
). Как это операнд &
это один из случаев, когда my_array
не сразу распадается на указатель на свой первый элемент.
Указатель на массив имеет то же значение адреса, что и указатель на первый элемент массива, поскольку объект массива является просто непрерывной последовательностью его элементов, но указатель на массив имеет тип, отличный от указателя на элемент этот массив. Это важно, когда вы выполняете арифметику указателей для двух типов указателей.
pointer_to_array
имеет тип char *
- инициализируется, чтобы указывать на первый элемент массива, так как это то, что my_array
распадается в выражении инициализатора - и &pointer_to_array
имеет тип char **
(указатель на указатель на char
).
Из этих: my_array
(после распада до char*
), &my_array
а также pointer_to_array
все они указывают непосредственно на массив или первый элемент массива и поэтому имеют одинаковое значение адреса.
Причина по которой my_array
а также &my_array
Результат в одном и том же адресе можно легко понять, если взглянуть на структуру памяти массива.
Допустим, у вас есть массив из 10 символов (вместо 100 в вашем коде).
char my_array[10];
Память для my_array
выглядит примерно так:
+---+---+---+---+---+---+---+---+---+---+
| | | | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array.
В C/C++ массив распадается на указатель на первый элемент в выражении, таком как
printf("my_array = %p\n", my_array);
Если вы посмотрите, где находится первый элемент массива, вы увидите, что его адрес совпадает с адресом массива:
my_array[0]
|
v
+---+---+---+---+---+---+---+---+---+---+
| | | | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array[0].
В языке программирования B, который был непосредственным предшественником C, указатели и целые числа были свободно взаимозаменяемыми. Система будет вести себя так, как будто вся память представляет собой гигантский массив. С каждым именем переменной связан либо глобальный, либо относящийся к стеку адрес, для каждого имени переменной единственное, что должен отслеживать компилятор, это глобальная или локальная переменная и его адрес относительно первой глобальной или локальной переменной. переменная.
Учитывая глобальную декларацию, как i;
[нет необходимости указывать тип, так как все было целым числом / указателем] будет обрабатываться компилятором как: address_of_i = next_global++; memory[address_of_i] = 0;
и заявление как i++
будет обработан как: memory[address_of_i] = memory[address_of_i]+1;
,
Декларация как arr[10];
будет обработан как address_of_arr = next_global; memory[next_global] = next_global; next_global += 10;
, Обратите внимание, что как только это объявление было обработано, компилятор мог сразу же забыть о arr
будучи массивом. Заявление как arr[i]=6;
будет обработан как memory[memory[address_of_a] + memory[address_of_i]] = 6;
, Компилятору было бы все равно, arr
представляет массив и i
целое число или наоборот. Действительно, было бы все равно, если бы они были массивами или обоими целыми числами; он с удовольствием сгенерирует код, как описано, независимо от того, будет ли полученное поведение полезным.
Одна из целей языка программирования C состояла в том, чтобы быть в значительной степени совместимым с B. В B имя массива [называемое "вектором" в терминологии B] идентифицировало переменную, содержащую указатель, который первоначально был назначен для указания на к первому элементу выделения заданного размера, поэтому, если это имя появилось в списке аргументов функции, функция получит указатель на вектор. Несмотря на то, что C добавил "реальные" типы массивов, чье имя было жестко связано с адресом выделения, а не с переменной-указателем, которая первоначально указывала бы на выделение, наличие массивов, разлагаемых на созданный указателями код, который объявил массив типа C, ведет себя идентично в код B, который объявил вектор, а затем никогда не изменял переменную, содержащую ее адрес.
На самом деле &myarray
а также myarray
both are the base address.
If you want to see the difference instead of using
printf("my_array = %p\n", my_array);
printf("my_array = %p\n", &my_array);
использование
printf("my_array = %s\n", my_array);
printf("my_array = %p\n", my_array);