Почему адрес массива равен его значению в 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;
}

Ответы

Ответ 1

Имя массива обычно оценивает адрес первого элемента массива, поэтому array и &array имеют одинаковое значение (но разные типы, поэтому array+1 и &array+1 не будут равны если массив имеет длину более 1 элемента).

Есть два исключения: если имя массива является операндом sizeof или унарным & (адресом), имя относится к самому массиву. Таким образом, sizeof array дает размер в байтах всего массива, а не размер указателя.

Для массива, определенного как T array[size], он будет иметь тип T *. Когда/если вы увеличиваете его, вы получаете следующий элемент в массиве.

&array оценивается по одному и тому же адресу, но при том же определении он создает указатель типа T(*)[size] - то есть это указатель на массив, а не на один элемент. Если вы увеличите этот указатель, он добавит размер всего массива, а не размер одного элемента. Например, с кодом, подобным этому:

char array[16];
printf("%p\t%p", (void*)&array, (void*)(&array+1));

Мы можем ожидать, что второй указатель будет 16 больше первого (потому что это массив из 16 char). Поскольку% p обычно преобразует указатели в шестнадцатеричном виде, он может выглядеть примерно так:

0x12341000    0x12341010

Ответ 2

Это потому, что имя массива (my_array) отличается от указателя на массив. Это псевдоним адреса массива, и его адрес определяется как адрес самого массива.

Указатель является обычной переменной C в стеке. Таким образом, вы можете взять свой адрес и получить другое значение от адреса, который он хранит внутри.

Я писал об этом тему здесь - пожалуйста, посмотрите.

Ответ 3

В C, когда вы использовали имя массива в выражении (включая передачу его функции), если оно не является операндом оператора адреса (&) или оператора 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 все указывают непосредственно либо на массив, либо на первый элемент массива, и поэтому имеют одинаковое значение адреса.

Ответ 4

Из часто задаваемых вопросов о comp.lang.c:

Или прочитайте весь раздел Массивы и указатели.

Ответ 5

В языке программирования 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, который объявил вектор, а затем никогда не изменял переменную, содержащую ее адрес.

Ответ 6

Причина, по которой my_array и &my_array приводит к тому же адресу, может быть легко понята, когда вы смотрите на макет памяти массива.

Скажем, у вас есть массив из 10 символов (вместо 100 в вашем коде).

char my_array[10];

Память для my_array выглядит примерно так:

+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array.

В C/С++ массив распадается на указатель на первый элемент в выражении, таком как

printf("my_array = %p\n", my_array);

Если вы посмотрите, где находится первый элемент массива, вы увидите, что его адрес совпадает с адресом массива:

my_array[0]
|
v
+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array[0].

Ответ 7

Фактически &myarray и myarray оба являются базовым адресом.

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

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);