Почему мы не можем присвоить адрес массива указателю? - PullRequest
4 голосов
/ 21 марта 2020
int q[10]={0};
cout << q << endl;
cout << &q << endl;
cout << &q[0] << endl;

вывод равен

0x7fffd4d2f860 
0x7fffd4d2f860 
0x7fffd4d2f860 

Теперь, когда я делаю это ->

int *r=q;    // allowed
int *r=&q[0] // allowed
int *r=&q    // not allowed

Почему третье назначение не допускается, когда оно по сути одно и то же?

Ответы [ 4 ]

10 голосов
/ 21 марта 2020

Если у вас есть массив, объявленный как

T a[N];

, где T - некоторый спецификатор типа, тогда указатель на массив будет объявлен как

T ( *p )[N] = &a;

Общее правило: последующий. Если у вас есть многомерный массив (включая одномерные массивы), например,

T a[N1][N2][N3];

, тогда эту декларацию вы можете переписать как

T ( a[N1] )[N2][N3];

Чтобы получить указатель на первый элемент массив просто подставляет содержимое в круглые скобки следующим образом

T ( *p )[N2][N3] = a;

Если вы хотите получить указатель на весь массив, перепишите объявление массива следующим образом:

T ( a )[N1][N2][N3];

и сделать подстановку

T ( *p )[N1][N2][N3] = &a;

Сравните это с объявлением скалярного объекта и указателем на него.

Например,

T obj;

Вы можете переписать объявление как

T ( obj );

Теперь, чтобы получить указатель на объект, вы можете написать

T ( *p ) = &obj;

Конечно, в этом случае круглые скобки являются избыточными, а приведенное выше объявление эквивалентно

T *p = &obj;

Что касается этого фрагмента кода

int q[10]={0};
cout << q << endl;
cout << &q << endl;
cout << &q[0] << endl;

и его вывода

0x7fffd4d2f860 
0x7fffd4d2f860 
0x7fffd4d2f860 

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

Таким образом, на самом деле два выражения q и &q[0] в этих утверждениях

cout << q << endl;
cout << &q[0] << endl;

эквивалентны. С другой стороны, адрес самого массива является адресом объема памяти, который занимает массив. И в начале экстента есть первый элемент массива. Таким образом, три выражения дают один и тот же результат: адрес объема памяти, занятого массивом.

4 голосов
/ 21 марта 2020

Почему третье назначение не допускается, если оно по сути одно и то же?

Поскольку язык C ++ имеет функцию, называемую «безопасность типов». Существует система типов, которая помогает вам поддерживать логи c звука вашей программы.

Одно конкретное правило заключается в том, что произвольные типы указателей не могут использоваться для инициализации указателей других несовместимых типов. В этом случае у вас есть указатель на тип int (.ie int*), который вы пытаетесь инициализировать с помощью выражения указателя типа на массив типа 10 int (то есть int(*)[10]). Один тип неявно преобразуется в другой, поэтому программа плохо сформирована.

Тогда почему cout печатает одни и те же вещи во всех трех случаях?

Потому что все указатели имеют одинаковое значение. Первый байт первого элемента массива совпадает с первым байтом всего массива.

Просто так получается, что оператор вставки потока обрабатывает все типы указателей 1 точно то же самое, поэтому указатели с одинаковым значением, но с другим типом выдают одинаковый вывод.

1 Указатели на типы символов являются исключением. Они обрабатываются совершенно по-разному.


Почему мы не можем присвоить адрес массива указателю?

На самом деле, мы можем присвоить адрес массива указатель. Мы просто не можем присвоить адрес массива (или любого другого объекта в этом отношении) указателю неправильного типа . Нам нужен указатель на массив в этом случае:

int (*r)[10] = &q;
2 голосов
/ 21 марта 2020

q - массив фиксированной длины. Указание q само по себе в выражении распадается в указатель на 1-й элемент q. Таким образом, q затухает до того же значения указателя, которое возвращает &q[0]. &q, с другой стороны, возвращает адрес памяти самой переменной q, а для массива его 1-й элемент занимает тот же адрес памяти.

Для * определено operator<<(void*) 1017 * и void* могут принимать (почти) ЛЮБОЙ тип указателя. Поскольку все три ваших cout звонка разрешаются по одному и тому же адресу памяти, и существует operator<<, который принимает все три типа указателей, поэтому все три вызова печатают один и тот же номер.

Что касается Ваши назначения:

  • q - это int[10], который распадается в int*, поэтому int *r=q; работает.

  • &q[0] разыменовывает q для доступа к своему 1-му элементу, который является int, а затем получает адрес этого элемента, создавая int*, поэтому int *r=&q[0]; работает .

  • , поскольку q является int[10], &q является int(*)[10], который НЕ распад в int*, что почему int *r=&q; не работает. Вы должны будете объявить r, используя правильный тип:

    int (*r)[10] = &q;

2 голосов
/ 21 марта 2020

Вы не можете выполнить третье назначение, потому что тип &q - это int (*)[10], что несовместимо с типом int* r.

Вывод cout << &q не показывает тип &q. См. эту ссылку на документацию.

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