Массивы и sizeof() в C++

Говорят, что когда массив передается в качестве параметра функции, он превращается в указатель, и длину массива также необходимо передавать в качестве параметра. Это означает, что исходный массив хранит информацию о своем размере. Действительно, 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). Это приводит к противоречию. Или:

  1. Имя массива не является указателем на его первый элемент (как мне сказали). Но что тогда? А если это не указатель, то почему с ним возможна арифметика с указателями?
  2. Имя массива на самом деле является указателем на первый элемент. Но тогда почему он хранит информацию, отличную от указателя, в который он превращается?

«Имя массива на самом деле является указателем на первый элемент...» Нет, это просто неверно. Имя массива представляет собой массив, а не указатель на его первый элемент. В некоторых контекстах имя массива превращается в указатель на его первый элемент, но это не делает массив указателем.

user12002570 17.04.2024 09:23

Это не означает, что исходный массив хранит информацию о размере — компилятор знает размер массивов во время компиляции.

molbdnilo 17.04.2024 09:24

Вы запутались в типах. int arr[], поскольку переменная, объявленная в стеке с помощью инициализатора, на самом деле для некоторых int[N] является N. int arr[] как параметр функции на самом деле является int*. Почему? Потому что были допущены ошибки проектирования.

Passer By 17.04.2024 09:25

«Но тогда что это?...» Это массив! типа T [N]

user12002570 17.04.2024 09:26

Хорошо, это объясняет, почему sizeof() возвращает фактический размер массива. Но если имя массива представляет собой массив, а не указатель, почему возможна арифметика указателей типа *(arr+i)?

Kotaka Danski 17.04.2024 09:26

«почему возможна арифметика указателей типа *(arr+i)?...» Потому что в определенных контекстах массив распадается на указатель на его первый элемент. Обратите внимание только «в определенном контексте», но не во всех контекстах.

user12002570 17.04.2024 09:27

Имя массива вообще ничего не значит; это буквально несколько символов исходного кода. Точно так же, как имя «Котака Дански» не является личностью.

molbdnilo 17.04.2024 09:27

@ user12002570 Итак, по сути, arr - это просто имя переменной типа данных массива, а в выражении *(arr + i), arr распадается на указатель, точно так же, как оно распадалось, когда arr был передан в качестве параметра функции?

Kotaka Danski 17.04.2024 09:29
arr + i эквивалентно &arr[0] + i; Распад указателя происходит в большем количестве ситуаций, чем передача аргументов. Приобретите хорошую книгу и не верьте слухам.
molbdnilo 17.04.2024 09:30

@KotakaDanski Да, именно, arr — это просто имя переменной типа данных массива. Иногда он превращается в указатель на свой первый элемент, а иногда нет. Вот почему мы говорим: «С++ — контекстно-зависимый язык». Значение одной конструкции может зависеть от того, где/как и т. д. она используется.

user12002570 17.04.2024 09:31

Что ж, это оказалось гораздо проще, чем я ожидал. Мне жаль, что я даже создал этот вопрос, но использование поисковой системы и даже LLM (Bing) не дало однозначного объяснения, в отличие от ваших комментариев. Спасибо. Отмечаю ответ как принятый, чтобы этот вопрос больше не привлекал лишнего внимания.

Kotaka Danski 17.04.2024 09:34

@KotakaDanski Это нормально, это часть изучения любого языка и т. д. Только когда мы задаем вопросы и развеиваем свои сомнения, мы действительно чему-то научимся.

user12002570 17.04.2024 09:35

Спасибо за понимание и за ваше время (и всем остальным, кто участвовал). Думаю, сегодня мне повезло, потому что в других случаях вопрос сразу же отклонялся и закрывался из-за очевидных вещей. Я в некоторой степени согласен и в этом случае, что это должно было быть очевидно. С другой стороны, иногда мне казалось, что я что-то понял, потому что мне удалось дать себе объяснение, но спустя несколько месяцев случайно обнаружить, что это неправда. Это один из таких случаев. Я никогда не удосужился вдаваться в такие подробности, потому что того, что я уже «знал», было «достаточно» для запуска программы.

Kotaka Danski 17.04.2024 09:50
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
13
93
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это приводит к противоречию.

Нет. В определенных контекстах массив распадается на указатель на его первый элемент. Например, одним из случаев, когда массив не превращается в указатель, является 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 *).

Peter 17.04.2024 14:34

Другие вопросы по теме