Почему char[] и char* как typedefs разные, но иногда... нет?
Следующее наблюдение возникло, когда я следовалэтому вопросу О 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()
s ? Я подозреваю, что они на самом деле разные типы из-за их typedef
объявлений, но тогда почему переменные сообщаются как тот же самый тип?
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 случаи, когда это неявное преобразование не происходит, следующие:
- когда выражение массива является операндом
sizeof
(sizeof arr
возвращает размер массива, а не размер указателя);- когда выражение массива является операндом унарного
&
(&arr
является указателем на массив, а не указателем на указатель); и- когда выражение массива является строковым литералом, используемым для инициализации объекта типа массива (
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
.
Семейство C является передаточным значением, а значение c массива является указателем на его первый элемент. Когда вы передаете в функцию элемент, объявленный массивом, на самом деле передается этот указатель, и C обрабатывает прототип, как если бы вы объявили его таким образом.
Я изменил код, чтобы мы могли видеть, как вызов 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; }
Вывод: Ноль Ноль .Как @jthill, @Dyp и @keith Thompson говорят, что это происходит из-за распада массива на указатель.