Говорят, что когда массив передается в качестве параметра функции, он превращается в указатель, и длину массива также необходимо передавать в качестве параметра. Это означает, что исходный массив хранит информацию о своем размере.
Действительно, sizeof(arr) внутри тела функции, где arr — аргумент функции, должен возвращать 8 (байты), потому что это размер указателя на 64-битной машине. Между тем, sizeof(arr), если arr не является параметром, возвращает фактический размер всего массива в байтах.
#include <iostream>
void foo(int arr[]) {
// prints 8
std::cout << sizeof(arr) << "\n";
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
// prints 20
std::cout << sizeof(arr) << "\n";
// prints 8
foo(arr);
}
Но имя массива уже является указателем на его первый элемент. arr[i] это просто *(arr + i). Это приводит к противоречию. Или:
Это не означает, что исходный массив хранит информацию о размере — компилятор знает размер массивов во время компиляции.
Вы запутались в типах. int arr[], поскольку переменная, объявленная в стеке с помощью инициализатора, на самом деле для некоторых int[N] является N. int arr[] как параметр функции на самом деле является int*. Почему? Потому что были допущены ошибки проектирования.
«Но тогда что это?...» Это массив! типа T [N]
Хорошо, это объясняет, почему sizeof() возвращает фактический размер массива. Но если имя массива представляет собой массив, а не указатель, почему возможна арифметика указателей типа *(arr+i)?
«почему возможна арифметика указателей типа *(arr+i)?...» Потому что в определенных контекстах массив распадается на указатель на его первый элемент. Обратите внимание только «в определенном контексте», но не во всех контекстах.
Имя массива вообще ничего не значит; это буквально несколько символов исходного кода. Точно так же, как имя «Котака Дански» не является личностью.
@ user12002570 Итак, по сути, arr - это просто имя переменной типа данных массива, а в выражении *(arr + i), arr распадается на указатель, точно так же, как оно распадалось, когда arr был передан в качестве параметра функции?
arr + i эквивалентно &arr[0] + i; Распад указателя происходит в большем количестве ситуаций, чем передача аргументов. Приобретите хорошую книгу и не верьте слухам.
@KotakaDanski Да, именно, arr — это просто имя переменной типа данных массива. Иногда он превращается в указатель на свой первый элемент, а иногда нет. Вот почему мы говорим: «С++ — контекстно-зависимый язык». Значение одной конструкции может зависеть от того, где/как и т. д. она используется.
Что ж, это оказалось гораздо проще, чем я ожидал. Мне жаль, что я даже создал этот вопрос, но использование поисковой системы и даже LLM (Bing) не дало однозначного объяснения, в отличие от ваших комментариев. Спасибо. Отмечаю ответ как принятый, чтобы этот вопрос больше не привлекал лишнего внимания.
@KotakaDanski Это нормально, это часть изучения любого языка и т. д. Только когда мы задаем вопросы и развеиваем свои сомнения, мы действительно чему-то научимся.
Спасибо за понимание и за ваше время (и всем остальным, кто участвовал). Думаю, сегодня мне повезло, потому что в других случаях вопрос сразу же отклонялся и закрывался из-за очевидных вещей. Я в некоторой степени согласен и в этом случае, что это должно было быть очевидно. С другой стороны, иногда мне казалось, что я что-то понял, потому что мне удалось дать себе объяснение, но спустя несколько месяцев случайно обнаружить, что это неправда. Это один из таких случаев. Я никогда не удосужился вдаваться в такие подробности, потому что того, что я уже «знал», было «достаточно» для запуска программы.





Это приводит к противоречию.
Нет. В определенных контекстах массив распадается на указатель на его первый элемент. Например, одним из случаев, когда массив не превращается в указатель, является decltype:
int arr[2];
decltype(arr) i; //i is int[2] and array does not decay to a pointer here
Вот один пример, когда он распадается на указатель на свой первый элемент:
int arr[2]{};
auto j = arr; //j is int*; here array decays to int*
Другой пример, когда массив не распадается:
char arr[10]{};
auto k = sizeof(arr); // k is initialized with 10 and array does not decay to pointer
Еще один очевидный случай, возможно, актуальный в контексте этого вопроса, когда массив не распадается, — это операнд sizeof. Итак, sizeof(arr) — это sizeof(int[2]), что (обычно) не равно sizeof(int *).
«Имя массива на самом деле является указателем на первый элемент...» Нет, это просто неверно. Имя массива представляет собой массив, а не указатель на его первый элемент. В некоторых контекстах имя массива превращается в указатель на его первый элемент, но это не делает массив указателем.