Почему адрес массива равен его значению в C? - PullRequest
170 голосов
/ 27 марта 2010

В следующем бите кода значения указателя и адреса указателя отличаются, как и ожидалось.

Но значения массива и адреса не имеют!

Как это может быть?

выход

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 ]

187 голосов
/ 27 марта 2010

Имя массива обычно оценивается по адресу первого элемента массива, поэтому 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
29 голосов
/ 27 марта 2010

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

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

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

24 голосов
/ 27 марта 2010

В 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 все указывают непосредственно на массив или на первый элемент массива и поэтому имеют одинаковое значение адреса.

3 голосов
/ 14 сентября 2015

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

2 голосов
/ 18 февраля 2015

Причину, по которой 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].
1 голос
/ 28 июня 2013

На самом деле &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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...