Когда функция имеет параметр массива определенного размера, почему он заменяется указателем?

Учитывая следующую программу,

#include <iostream>

using namespace std;

void foo( char a[100] )
{
    cout << "foo() " << sizeof( a ) << endl;
}

int main()
{
    char bar[100] = { 0 };
    cout << "main() " << sizeof( bar ) << endl;
    foo( bar );
    return 0;
}

выходы

main() 100
foo() 4
  1. Почему массив передается как указатель на первый элемент?
  2. Это наследие от C?
  3. Что говорит стандарт?
  4. Почему в C ++ отказались от строгой типобезопасности?

person CsTamas    schedule 25.08.2009    source источник
comment
Я всегда использую std :: array в этих случаях, чтобы избежать подобных проблем, а также работает с алгоритмами std.   -  person paulm    schedule 08.09.2014
comment
Какая строгая безопасность типов? Кто обещал строгую безопасность типов? В C ++ такого нет.   -  person n. 1.8e9-where's-my-share m.    schedule 08.09.2014
comment
TL; DR для ответов ниже: Массивы становятся указателями при передаче функции, поэтому, когда вы проверяете их размер, все, что вы получаете, - это размер указателя. Если вы работаете только с C, все, что я могу предложить, - это предварительно вычислить любой размер, который вы пытаетесь получить из массива в качестве другого параметра.   -  person Super Cat    schedule 28.12.2015
comment
Соответствующая тирада Линуса   -  person Millie Smith    schedule 05.12.2017
comment
Связано: определить размер массива при передаче в функцию   -  person Gabriel Staples    schedule 25.09.2020


Ответы (3)


Да, это унаследовано от C. Функция:

void foo ( char a[100] );

Параметр будет настроен на указатель, и он станет таким:

void foo ( char * a );

Если вы хотите, чтобы тип массива был сохранен, вы должны передать ссылку на массив:

void foo ( char (&a)[100] );

C ++ '03 8.3.5 / 3:

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

Чтобы объяснить синтаксис:

Проверить в Google правило "право-лево"; Я нашел одно его описание здесь.

К этому примеру это применимо примерно так:

void foo (char (&a)[100]);

Начать с идентификатора "а"

'a' is a

Двигайтесь вправо - мы находим ), поэтому мы меняем направление в поисках (. Двигаясь влево, мы проходим &

'а' - это ссылка

После & мы достигаем отверстия (, так что мы снова разворачиваемся и смотрим направо. Теперь мы видим [100]

'a' - это ссылка на массив из 100

И мы снова меняем направление, пока не достигнем char:

'a' - это ссылка на массив из 100 символов

person Richard Corden    schedule 25.08.2009
comment
Последний имеет недостаток, заключающийся в том, что размер массива фиксируется в сигнатуре функции. Шаблон функции может этого избежать. - person sbi; 25.08.2009
comment
В некотором роде, может кто-нибудь прояснить для меня синтаксис вышеизложенного? Мне явно чего-то не хватает, но я не совсем понимаю, как это соотносится с ссылкой на массив; это больше похоже на массив ссылок. - person suszterpatt; 25.08.2009
comment
вероятно, стоит упомянуть, что использование std :: vector аккуратно устранит все проблемы, связанные с передачей массивов. - person markh44; 25.08.2009
comment
Это сводится к тому, что параметры простого массива в C / C ++ - фикция - на самом деле они указатели. Следует по возможности избегать параметров массива - они действительно только сбивают с толку. - person Michael Burr; 25.08.2009
comment
Придирка: параметр функции не превращается в указатель. Он настроен на указатель. Имя массива, используемое в качестве аргумента функции, может распадаться на указатель, если параметр функции является указателем. - person juanchopanza; 06.04.2015
comment
Браво за объяснение правила, так как ссылка теперь 404. - person gsamaras; 08.04.2015
comment
@ G.Samaras Хороший улов. Я исправил это сейчас. Соответствующий раздел - 2.4. - person Richard Corden; 08.04.2015
comment
@RichardCorden ссылка теперь работает. Однако мне настолько понравилось ваше объяснение, что я не чувствую необходимости читать ссылку в то время. ; p Спасибо за отличный ответ. - person gsamaras; 08.04.2015

да. В C и C ++ нельзя передавать массивы функциям. Просто так оно и есть.

Почему вы вообще делаете простые массивы? Вы смотрели _1 _ / _ 2 _ / _ 3_ или std::vector?

Обратите внимание, что вы можете передать ссылку на массив произвольной длины в функцию template. С верхней части моей головы:

template< std::size_t N >
void f(char (&arr)[N])
{
  std::cout << sizeof(arr) << '\n';
}
person sbi    schedule 25.08.2009
comment
Но вы можете перейти к массиву. - person Richard Corden; 25.08.2009
comment
@ Ричард: Я просто добавлял это, пока вы писали свой комментарий. :) - person sbi; 25.08.2009
comment
Передача по ссылке на массив не ограничивается шаблонами функций. Вы можете передать массив по ссылке не шаблонным функциям. Преимущество использования шаблона функции состоит в том, что вы можете вывести индекс массива, тем самым позволяя вызывать функцию для типов массивов разного размера. - person Richard Corden; 25.08.2009
comment
Если бы я поместил массив в структуру и передал его функции, он сообщил бы тот же размер. Значит, правила передачи массивов и объектов C в качестве параметров разные? - person CsTamas; 25.08.2009
comment
@CsTamas, да, правила передачи массивов и объектов в C. разные. Структуры фактически копируются по значению при передаче в качестве параметра. Массивы рассматриваются как указатель на их первый элемент. (Массивы и указатели в C очень взаимосвязаны. Это не одно и то же, но для целей передачи параметров они идентичны) - person Tyler McHenry; 25.08.2009
comment
@CsTomas: В версии 8.3.5 / 3 стандарт описывает правила, которые применяются к параметрам при объявлении функции. Эти правила определяют, является ли функция повторным объявлением или перегрузкой. В этом разделе явно рассматривается, где тип параметра является массивом (или функцией), и поэтому поведение различается в зависимости от случая массива и структуры. - person Richard Corden; 25.08.2009
comment
@ Ричард: Я знаю. (Также см. Мой комментарий к массиву stackoverflow. com / questions / 1328223 /) Однако, поскольку вопрос касался передачи размеров массивов, я полагаю, что CsTamas хочет передавать массивы произвольной длины. Я отредактировал свой ответ, чтобы это подчеркнуть. - person sbi; 25.08.2009
comment
Теперь есть std :: array. - person Trevor Hickey; 21.04.2016
comment
@Trevor Спасибо, добавил. - person sbi; 21.04.2016

В терминологии C / C ++ есть великолепное слово, которое используется для статических массивов и указателей на функции - decay. Рассмотрим следующий код:

int intArray[] = {1, 3, 5, 7, 11}; // static array of 5 ints
//...
void f(int a[]) {
  // ...
}
// ...
f(intArray); // only pointer to the first array element is passed
int length = sizeof intArray/sizeof(int); // calculate intArray elements quantity (equals 5)
int ptrToIntSize = sizeof(*intArray); // calculate int * size on your system
person nickolay    schedule 21.12.2011
comment
И? Это в лучшем случае косвенно указывает на то, что происходит. OP спрашивал почему язык настроен таким образом. Кроме того, термин «статический массив» сбивает с толку, когда то, что вы на самом деле имеете в виду, распределяется динамически; технически показанный вами массив имеет extern связь, а не static. И я не уверен, какое здесь отношение к указателям на функции? - person underscore_d; 30.08.2016