Почему arr и & arr одинаковы? - PullRequest
21 голосов
/ 19 января 2012

Я программировал на c / c ++ в течение многих лет, но сегодняшнее случайное обнаружение вызвало у меня некоторое любопытство ... Почему оба вывода дают одинаковый результат в приведенном ниже коде?(arr - это, конечно, адрес arr[0], то есть указатель на arr[0]. Я бы ожидал, что &arr будет адресом этого указателя, но он имеет то же значение, что и arr)

  int arr[3];
  cout << arr << endl;
  cout << &arr << endl;

Примечание: Этот вопрос был закрыт, но теперь он снова открыт.(Спасибо?)

Я знаю, что &arr[0] и arr оценивают одно и то же число, но это не мой вопрос!Вопрос в том, почему &arr и arr оценивают одно и то же число.Если arr является литералом (не хранится никаким программным обеспечением), то компилятор должен пожаловаться и сказать, что arr не является lvalue.Если адрес arr хранится где-то, тогда &arr должен дать мне адрес этого места.(но это не так)

если я напишу

const int * arr2 = arr;

затем arr2[i]==arr[i] для любого целого числа i, но &arr2 != arr.

Ответы [ 6 ]

17 голосов
/ 19 января 2012
#include <cassert>

struct foo {
    int x;
    int y;
};

int main() {    
    foo f;
    void* a = &f.x;
    void* b = &f;
    assert(a == b);
}

По той же причине два адреса a и b над совпадают .Адрес объекта совпадает с адресом его первого члена (однако их типы различны).

                            arr
                      _______^_______
                     /               \
                    | [0]   [1]   [2] |
--------------------+-----+-----+-----+--------------------------
      some memory   |     |     |     |        more memory
--------------------+-----+-----+-----+--------------------------
                    ^
                    |
           the pointers point here

Как видно на этой диаграмме, первый элемент массива находится натот же адрес, что и у самого массива.

13 голосов
/ 19 января 2012

Они не одинаковы. Они просто находятся в одном месте памяти. Например, вы можете написать arr+2, чтобы получить адрес arr[2], но не (&arr)+2, чтобы сделать то же самое.

Кроме того, sizeof arr и sizeof &arr отличаются.

6 голосов
/ 19 января 2012

Два имеют одинаковое значение, но разные типы.

Когда он используется сам по себе (не операнд & или sizeof), arr вычисляет указатель на int, содержащий адрес первого int в массиве. &arr вычисляет указатель на массив из трех int s, содержащий адрес массива. Поскольку первый int в массиве должен находиться в самом начале массива, эти адреса должны быть равны.

Разница между ними становится очевидной, если вы подсчитаете результаты:

arr+1 будет равно arr + sizeof(int).

((&arr) + 1) будет равен arr + sizeof(arr) == arr + sizeof(int) * 3

Редактировать: Относительно того, как / почему это происходит, ответ довольно прост: потому что стандарт говорит так. В частности, говорится (§6.3.2.1 / 3):

За исключением случаев, когда он является операндом оператора sizeof или унарного оператора &, или является строковый литерал, используемый для инициализации массива; выражение, имеющее тип '' массив типа '', преобразуется в выражение с типом '' указатель на тип '', которое указывает на начальный элемент объекта массива и не является lvalue.

[примечание: эта конкретная цитата взята из стандарта C99, но я считаю, что во всех версиях стандартов C и C ++ есть эквивалентный язык].

В первом случае (сам по себе * 1034) arr не используется в качестве операнда sizeof, унарного & и т. Д., Поэтому он преобразуется (не повышается) в тип «указатель на тип» (в данном случае «указатель на int»).

Во втором случае (&arr) очевидно, что имя является , используемым в качестве операнда унарного оператора & - так что преобразование не имеет место .

5 голосов
/ 19 января 2012

Адрес тот же, но оба выражения разные. Они просто начинаются в одной и той же ячейке памяти. Типы обоих выражений разные.

Значение arr имеет тип int *, а значение &arr имеет тип int (*)[3].

& - оператор адреса, а адрес объекта - указатель на этот объект. Указатель на объект типа int [3] имеет тип int (*)[3]

4 голосов
/ 19 января 2012

Они не совпадают.

Немного более строгое объяснение:

arr - это lvalue типа int [3].Попытка использовать arr в некоторых выражениях, таких как cout << arr, приведет к преобразованию lvalue в rvalue, которое, так как нет значений r типа массива, преобразует его в значение r * типа int * со значением, равнымдо &arr[0].Это то, что вы можете отобразить.

&arr - это rvalue типа int (*)[3], указывающее на сам объект массива.Никакой магии здесь :-) Этот указатель указывает на тот же адрес, что и &arr[0], потому что объект массива и его первый член начинаются в том же месте в памяти.Вот почему вы получаете тот же результат при их печати. ​​


Простой способ подтвердить, что они различаются, - это сравнение *(arr) и *(&arr): первым является lvalue типа int ивторой - lvalue типа int[3].

2 голосов
/ 19 января 2012

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...