Что такое разложение массива?
Что такое разложение массива? Есть ли какое-либо отношение к указателям на массив?
Ответы
Ответ 1
Он сказал, что массивы "распадаются" на указатели. Массив С++, объявленный как int numbers [5]
, не может быть перенаправлен, т.е. Вы не можете сказать numbers = 0x5a5aff23
. Что еще более важно, термин "распад" означает потерю типа и размерности; numbers
распадайтесь на int*
, потеряв информацию о размере (число 5), и тип уже не int [5]
. Посмотрите здесь случаи, когда распад не происходит.
Если вы передаете массив по значению, то, что вы действительно делаете, это копирование указателя - указатель на первый элемент массива копируется в параметр (тип которого также должен быть указателем типа элемента массива). Это работает из-за разрушения массива; раз затухает, sizeof
больше не дает полный размер массива, поскольку он по существу становится указателем. Именно поэтому он предпочел (среди прочего) пройти по ссылке или указателю.
Три способа передачи в массив 1:
void by_value(const T* array) // const T array[] means the same
void by_pointer(const T (*array)[U])
void by_reference(const T (&array)[U])
Последние два будут содержать соответствующую информацию sizeof
, а первая не будет, так как аргумент массива распался для назначения этому параметру.
1 Константа U должна быть известна во время компиляции.
Ответ 2
Массивы в основном такие же, как указатели на C/С++, но не совсем. После преобразования массива:
const int a[] = { 2, 3, 5, 7, 11 };
в указатель (который работает без кастинга и, следовательно, может случиться неожиданно в некоторых случаях):
const int* p = a;
вы теряете способность оператора sizeof
подсчитывать элементы в массиве:
assert( sizeof(p) != sizeof(a) ); // sizes are not equal
Эта потерянная способность называется "распад".
Для получения дополнительной информации ознакомьтесь с этой статьей о распаде массива.
Ответ 3
Вот что говорит стандарт (C99 6.3.2.1/3 - Другие операнды - Lvalues, массивы и обозначения функций):
За исключением случаев, когда это операнд оператора sizeof или унарный и оператор, или строковый литерал, используемый для инициализации массива, выражение, которое имеет тип '' массив типа, является преобразуется в выражение с типом '' указателем на тип, указывающим на начальный элемент объект массива и не является lvalue.
Это означает, что в любом случае имя массива используется в выражении, оно автоматически преобразуется в указатель на 1-й элемент массива.
Обратите внимание, что имена функций действуют аналогичным образом, но указатели на функции используются гораздо меньше и гораздо более специализированным образом, что это не вызывает почти столько же путаницы, как автоматическое преобразование имен массивов в указатели.
Стандарт С++ (4.2 Преобразование массива в указатель) ослабляет требование преобразования к (выделение мое):
Значение lvalue или rvalue типа "массив из N T" или "массив неизвестной границы T" может быть преобразован в rvalue типа "указатель на T".
Таким образом, преобразование не должно происходить, как это почти всегда происходит в C (это позволяет перегрузкам функций или шаблонам соответствовать типу массива).
Вот почему в C вам следует избегать использования параметров массива в прототипах/определениях функций (на мой взгляд - я не уверен, есть ли какое-либо общее соглашение). Они вызывают путаницу и в любом случае являются фикцией - используют параметры указателя, и путаница может не исчезнуть целиком, но, по крайней мере, объявление параметра не лежит.
Ответ 4
"Распад" означает неявное преобразование выражения из типа массива в тип указателя. В большинстве случаев, когда компилятор видит выражение массива, он преобразует тип выражения из "N-element array of T" в "указатель на T" и устанавливает значение выражения в адрес первого элемента массива, Исключения из этого правила заключаются в том, что массив является операндом операторов sizeof
или &
, или массив является строковым литералом, который используется в качестве инициализатора в объявлении.
Предположим, что следующий код:
char a[80];
strcpy(a, "This is a test");
Выражение a
имеет тип "80-элементный массив из char", а выражение "This is the test" имеет тип "16-элементный массив char" (в C; в строке С++ литералы являются массивами const char). Однако в вызове strcpy()
ни одно выражение не является операндом sizeof
или &
, поэтому их типы неявно преобразуются в "указатель на char", а их значения устанавливаются на адрес первого элемент в каждом. Что strcpy()
получает не массивы, а указатели, как показано в его прототипе:
char *strcpy(char *dest, const char *src);
Это не то же самое, что указатель на массив. Например:
char a[80];
char *ptr_to_first_element = a;
char (*ptr_to_array)[80] = &a;
Оба ptr_to_first_element
и ptr_to_array
имеют одинаковое значение; базовый адрес. Тем не менее, они являются разными типами и обрабатываются по-разному, как показано ниже:
a[i] == ptr_to_first_element[i] == (*ptr_to_array)[i] != *ptr_to_array[i] != ptr_to_array[i]
Помните, что выражение a[i]
интерпретируется как *(a+i)
(оно работает только в том случае, если тип массива преобразуется в тип указателя), поэтому оба a[i]
и ptr_to_first_element[i]
работают одинаково. Выражение (*ptr_to_array)[i]
интерпретируется как *(*a+i)
. Выражения *ptr_to_array[i]
и ptr_to_array[i]
могут приводить к предупреждениям или ошибкам компилятора в зависимости от контекста; они определенно сделают неправильную вещь, если вы ожидаете, что они оценят значение a[i]
.
sizeof a == sizeof *ptr_to_array == 80
Опять же, когда массив является операндом sizeof
, он не преобразуется в тип указателя.
sizeof *ptr_to_first_element == sizeof (char) == 1
sizeof ptr_to_first_element == sizeof (char *) == whatever the pointer size
is on your platform
ptr_to_first_element
- простой указатель на char.
Ответ 5
Массивы в C не имеют значения.
Где бы ни ожидалось значение объекта, но объектом является массив, вместо него используется адрес его первого элемента с типом pointer to (type of array elements)
.
В функции все параметры передаются по значению (массивы не являются исключением). Когда вы передаете массив в функции, он "распадается на указатель" (sic); когда вы сравниваете массив с чем-то другим, снова он "распадается на указатель" (sic);...
void foo(int arr[]);
Функция foo ожидает значение массива. Но в C массивы не имеют ценности! Таким образом, foo
получает вместо этого адрес первого элемента массива.
int arr[5];
int *ip = &(arr[1]);
if (arr == ip) { /* something; */ }
В приведенном выше сравнении arr
не имеет значения, поэтому он становится указателем. Он становится указателем на int. Этот указатель можно сравнить с переменной ip
.
В синтаксисе индексирования массива, который вы используете для просмотра, опять же arr "разлагается на указатель"
arr[42];
/* same as *(arr + 42); */
/* same as *(&(arr[0]) + 42); */
Единственный раз, когда массив не распадается на указатель, это когда он является операндом оператора sizeof или оператором and (оператор "operator" ) или как строковый литерал, используемый для инициализации массива символов.
Ответ 6
Это когда массив гниет и указывает на; -)
На самом деле, это просто, если вы хотите передать массив где-нибудь, но вместо этого указатель передается (потому что кто, черт возьми, передаст вам весь массив), люди говорят, что плохой массив распался на указатель.
Ответ 7
Затухание массива означает, что, когда массив передается как параметр функции, он обрабатывает тождественно ("decays to") указатель.
void do_something(int *array) {
// We don't know how big array is here, because it decayed to a pointer.
printf("%i\n", sizeof(array)); // always prints 4 on a 32-bit machine
}
int main (int argc, char **argv) {
int a[10];
int b[20];
int *c;
printf("%zu\n", sizeof(a)); //prints 40 on a 32-bit machine
printf("%zu\n", sizeof(b)); //prints 80 on a 32-bit machine
printf("%zu\n", sizeof(c)); //prints 4 on a 32-bit machine
do_something(a);
do_something(b);
do_something(c);
}
Существует два осложнения или исключения из вышеизложенного.
Во-первых, при работе с многомерными массивами в C и C++ теряется только первое измерение. Это связано с тем, что массивы сложены в памяти, поэтому компилятор должен знать все, кроме первого измерения, чтобы вычислить смещения в этом блоке памяти.
void do_something(int array[][10])
{
// We don't know how big the first dimension is.
}
int main(int argc, char *argv[]) {
int a[5][10];
int b[20][10];
do_something(a);
do_something(b);
return 0;
}
Во-вторых, в C++ вы можете использовать шаблоны для вывода размера массивов. Microsoft использует это для C++ версий функций Secure CRT, таких как strcpy_s, и вы можете использовать подобный трюк, чтобы надежно получить количество элементов в массиве.
Ответ 8
tl; dr: Когда вы используете массив, который вы определили, вы фактически будете использовать указатель на его первый элемент.
Таким образом:
- Когда вы пишете
arr[idx]
вы действительно говорите *(arr + idx)
. - функции никогда не принимают массивы в качестве параметров, а только указатели, даже если вы укажете параметр массива.
Сортировка исключений из этого правила:
- Вы можете передавать массивы фиксированной длины в функции внутри
struct
. -
sizeof()
дает размер, занимаемый массивом, а не размер указателя.