Что такое разложение массива?

Что такое разложение массива? Есть ли какое-либо отношение к указателям на массив?

Ответы

Ответ 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() дает размер, занимаемый массивом, а не размер указателя.