Может ли указатель преобразовать в массив во время вызова функции? - PullRequest
0 голосов
/ 18 ноября 2018

Рассмотрим следующий фрагмент кода:

#include <iostream>
#include <typeinfo>

void use_pointer(int *ptr)
{
    std::cout << typeid(ptr).name() << std::endl;
}

void use_array(int arr[])
{
    std::cout << typeid(arr).name() << std::endl;
}

int main()
{
    int *ptr = nullptr;
    // std::cout << typeid(int *).name() << std::endl; // output: Pi
    int arr[1];
    // std::cout << typeid(int[]).name() << std::endl; // output: A_i

    use_pointer(arr);
    use_array(ptr);
}

Компиляция этой программы с использованием g++ 6.5.0 выходных данных:

$ g++ -std=c++11 arrays_2.cpp -Wall
$ ./a.out

Pi
Pi

Теперь при вызове use_pointer(arr) массив распадается на указатель.(Это правильно? Слово «распад» для меня ново.)

И в стандарте C ++ написано [conv.array # 1] :

Значение l или значение типа «массив NT» или «массив неизвестной границы T» может быть преобразовано в значение типа «указатель на T».Временное преобразование материализации ([conv.rval]) применяется.Результатом является указатель на первый элемент массива.

Мне кажется, я понимаю, что мой "массив целых" преобразуется в "указатель на int" .(Это правильно?)

Теперь, что именно происходит при вызове use_array(ptr).Так как тип параметра в этом случае - массив int, "указатель на int" преобразовал в "массив int" ?

Указательк стандарту будет высоко ценится.

Ответы [ 3 ]

0 голосов
/ 18 ноября 2018

Может ли указатель преобразоваться в массив во время вызова функции?

Нет. См. Последний пример при передаче по ссылке.

Тип массива имеет размер (который включает в себя все элементы). Когда он превращается в указатель, эта информация о размере теряется. Поэтому нет способа конвертировать указатель обратно в массив (поскольку вы не знаете, насколько велик массив).

Вы, вероятно, заметили, что параметры массива и указателя на самом деле являются синонимами для компилятора:

> cat b.cpp
void use_pointer(int *ptr)
{}
void use_pointer(int (arr)[])
{}

> g++ b.cpp
b.cpp:8:6: error: redefinition of 'use_pointer'
void use_pointer(int (arr)[])
     ^
b.cpp:4:6: note: previous definition is here
void use_pointer(int *ptr)

Для компилятора не имеет значения, что это одно и то же.

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

Теперь вы можете передавать массив по ссылке, но синтаксис другой.

void use_pointerref(int (&arr)[1])  // You have to specify the exact size
{}                                  // And lace the ampersand in there.

Но вы заметите, что указатель не будет привязываться к этой функции.

use_arrayref(arr);
use_arrayref(ptr);

> g++ b.cpp
b.cpp:31:5: error: no matching function for call to 'use_arrayref'
    use_arrayref(ptr);
    ^~~~~~~~~~~~
b.cpp:14:6: note: candidate function not viable: no known conversion from
      'int *' to 'int (&)[1]' for 1st argument
void use_arrayref(int (&arr)[1])

Некоторые заметки:

Но давайте подумаем об этом. Семантика C ++ обычно передается по значению. Поэтому, если бы вам удалось (или я должен сказать, если язык позволял) передать массив, вы бы сделали копию массива, который был бы передан функции. Это, вероятно, нежелательно, поэтому использование указателя - эффективный способ передачи по значению (хотя вы и получаете изменение типа).

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

template<int S>
void use_pointerref(int (&arr)[S])
{
    std::cout << typeid(arr).name() << " Size: " << S << std::endl;
}
0 голосов
/ 18 ноября 2018

Ваш код имеет мало общего с затуханием указателя.Скорее, он демонстрирует настройку типа параметров функции.

При использовании в качестве типа параметра функции T[] настраивается с на T*.Это не распад, не преобразование, это перезапись .Программа переписана для использования T*, как если бы в исходном коде никогда не было T[].

Повторим, в C ++ есть нет функциональных параметров типа T[].Когда программист пишет один, компилятор немедленно заменяет T* и забывает, что T[] когда-либо был там.Это контрастирует с обычными массивами и их распадом на указатели.В C ++ наиболее определенно есть массивы, которые сильно отличаются от указателей.Они распадаются на указатели только в (многих, но не во всех) выражениях .Это улица с односторонним движением: указатель никогда не распадается на массив

0 голосов
/ 18 ноября 2018

Указатели - это указатели, а массивы - это массивы. Однако массивы естественным образом распадаются на указатели на их первый элемент. Поэтому, когда вы передадите массив arr любой функции, которая у вас есть, он уменьшится до &arr[0].

Также обратите внимание, что при объявлении аргументов функции нотация массива (например, int arr[]) не означает, что это массив, он все равно транслируется компилятором как указатель (т. Е. int* arr).

Относительно затухания от массива к указателю, другого не может быть. Если у вас есть указатель, у вас есть только этот указатель и единственный элемент, на который он указывает.

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