Почему char[] и char* как определения типов различаются, а иногда нет?

Следующее наблюдение возникло, когда я следил за этим вопросом о char[] и char* различий.

#include <iostream>

typedef char ar[];
typedef char* pr;
void f2(ar x, pr y)
{
    std::cout << std::is_same<decltype(x), decltype(y)>::value << '\n';
    std::cout << std::is_same<ar, pr>::value << '\n';
}

int main()
{
    char data[] = "data";
    char *ptr = data;
    f2(data,ptr);
    return 0;
}

Вывод (в Apple LLVM версии 4.2 (clang-425.0.28))

1
0

Почему они сообщают о разных типах, а не о разных decltype()? Я подозреваю, что они на самом деле являются разными типами из-за их объявлений typedef, но тогда почему переменные сообщаются как одинаковые типы?


person WhozCraig    schedule 05.09.2013    source источник
comment
Разлагающийся? (как параметры функции)   -  person dyp    schedule 05.09.2013
comment
(Разрабатывая мой комментарий :) Типы параметров функции распадаются в соответствии с [dcl.fct]/5: после определения типа каждого параметра корректируется любой параметр типа «массив T» или «функция, возвращающая T». быть «указателем на T» или «указателем на функцию, возвращающую T» соответственно. Следовательно, decltype(x) является указателем на char, а не массивом неизвестных границ char (в отличие от ar).   -  person dyp    schedule 05.09.2013
comment
@DyP Спасибо за стандартную ссылку, сэр.   -  person WhozCraig    schedule 05.09.2013
comment
@WhozCraig Возможно, вы захотите проверить std::decay (на en.cppreference.com/ w/cpp/types/decay).   -  person Red XIII    schedule 05.09.2013
comment
Пусть единственный вопрос, который вы когда-либо задавали, привел вас к волшебным 20к!!! И позвольте мне быть первым, чтобы поздравить вас...   -  person Floris    schedule 26.10.2013
comment
@Флорис, хех. Спасибо! очень мило с твоей стороны. Я оглядываюсь назад на этот вопрос сейчас и удивляюсь, почему я никогда не видел ответ сам =)   -  person WhozCraig    schedule 26.10.2013
comment
Вот это да. Наверное, я кого-то там раздражал.   -  person WhozCraig    schedule 10.08.2014


Ответы (3)


В C++, как и в C, параметр, объявленный как тип массива, настраивается (во время компиляции) так, чтобы он имел тип указателя, в частности, указатель на тип элемента массива.

Это происходит независимо от того, указан ли тип массива напрямую или через typedef (помните, что typedef не создает новый тип, а только псевдоним для существующего типа).

Итак, это:

typedef char ar[];
typedef char* pr;
void f2(ar x, pr y)
{
    // ...
}

действительно означает:

void f2(char* x, char* y)
{
    // ...
}

Другое правило, также разделяемое C и C++, заключается в том, что выражение типа массива в большинстве, но не во всех контекстах неявно преобразуется в указатель на первый элемент массива. объект массива. Это означает, что если вы определяете объект массива:

char arr[10];

вы можете использовать имя этого объекта в качестве аргумента функции, которая принимает параметр char* (который теряет информацию о границах).

В C случаи, когда это неявное преобразование не выполняется:

  1. Когда выражение массива является операндом sizeof (sizeof arr дает размер массива, а не размер указателя);
  2. Когда выражение массива является операндом унарного & (&arr является указателем на массив, а не указателем на указатель); а также
  3. Когда выражение массива является строковым литералом, используемым для инициализации объекта типа массива (char s[] = "hello"; инициализирует s как массив, а не как указатель).

Ни один из этих случаев (или других случаев, встречающихся в C++) не появляется в вашей программе, поэтому ваш вызов:

f2(data,ptr);

передает два значения указателя типа char* на f2.

Внутри f2 объекты параметров x и y имеют тип char*, поэтому std::is_same<decltype(x), decltype(y)>::value истинно.

Но типы ar и pr различны. ar — тип неполного массива char[], а pr — тип указателя char*.

Что объясняет вывод вашей программы. Странность возникает из-за того, что параметр x, который вы определили с типом массива ar, на самом деле имеет тип char*, который совпадает с типом pr.

person Keith Thompson    schedule 05.09.2013
comment
Спасибо, Кит. Это было очень хорошо представлено и в соответствии со стандартной ссылкой Dyp из общего комментария точно объясняет, что видно. Очень ценю. - person WhozCraig; 05.09.2013

Семейство C передается по значению, а значение C массива является указателем на его первый элемент. Когда вы передаете элемент, объявленный как массив, в функцию, на самом деле передается этот указатель, и C обрабатывает прототип так, как если бы вы объявили его таким образом.

person jthill    schedule 05.09.2013

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

    typedef char ar[];
typedef char* pr;
void f2(ar x, pr y)
{
    cout << is_same<decltype(x), decltype(y)>::value << '\n'; //same type
}

int main()
{
    ar data = "data";
    pr ptr = data;
    cout << is_same<decltype(data), decltype(ptr)>::value << '\n'; // different
    f2(data,ptr);
    return 0;
}

вывод равен 0 0. Как говорят @jthill, @Dyp и @keith Thompson, это происходит из-за распада массива на указатель.

person Testing    schedule 05.09.2013
comment
Еще одна визуализация вопроса: is_same<decltype(x), char[]> и is_same<ar, char[]> внутри f2 (и конечно же is_same<decltype(x), char*>). - person dyp; 05.09.2013
comment
О, и кое-что интересное: is_same<decltype(data), ar> также false (потому что ar неполное, а [basic.types]/6 говорит, что они разные). - person dyp; 05.09.2013