Имя массива это адрес первого элемента или указатель на его первый элемент в Си?

Есть код:

int arrOne[5] = {1,2,3,4,5};

Тут arrOne это адрес его первого элемент ? Или указатель на его первый элемент ? Или в указатель он превращается только когда это нужно (к примеру в арифметике адресов)? Или оба варианта верны ?

Немного другая тема:

int arrTwo[3][2] = {{1,2}, {3,4}, {5,6}};

А с двумерным массивом как ? Тут arrTwo это адрес первого элемента или указатель на его первый элемент т.е {1,2}? И получается arrTwo это адрес самого первого элемента {1,2} или же адрес первого элемента первого подмассива т.е {1}?


Ответы (2 шт):

Автор решения: AlexGlebe

переменная arrOne имеет тип массива int[5]. Может неявно преобразовываться в указатель на первый элемент типа int * в подходящем контексте.

во втором примере первый элемент массива имеет тип int[2], а указатель на него тип : int ( * ) [ 2 ]

int main() 
{ 

  int arrOne[5] = {1,2,3,4,5};
  int * arrOnep = arrOne ;
  int * arrOnep2 = & arrOne [ 0 ] ;

  int arrTwo[3][2] = {{1,2}, {3,4}, {5,6}};
  int ( * arrTwop ) [ 2 ] = arrTwo ;
  int ( * arrTwop2 ) [ 2 ] = & arrTwo [ 0 ] ;

  return 0; 

}
→ Ссылка
Автор решения: Serge3leo

Тут arrOne это адрес его первого элемент ? Или указатель на его первый элемент ? Или в указатель он превращается только когда это нужно (к примеру в арифметике адресов)? Или оба варианта верны ?

Немного запутанный вопрос, верный вариант: arrOne - это массив int длины 5 (т.е. третий или четвёртый). В частности, sizeof(arrOne) == 5*sizeof(int). Так же, для C++ при подстановках шаблонов, а для C в _Generic(), он отличается от указателей на элементы.

До C++17 была популярная идиома _countof(), её расширенная версия включена в стандарт как std::ssize() (std::size() - беззнаковый вариант, для циклов по unsigned):

template<class T, std::ptrdiff_t n>
constexpr static std::ptrdiff_t
_countof(T (&)[n]){ return n; }

Есть её безопасные варианты и для C11 (https://stackoverflow.com/a/57537491/8585880), но они немного длиннее.

А с двумерным массивом как ?

Примерно так же, в том смысле, что arrTwo массив длины 3 массивов int длины 2.

#include <print>
#include <vector>
#include "type_name.hpp"  // https://stackoverflow.com/a/64490578/8585880

#define print_arr(a)  \
                (std::println("std::size({}) = {}, sizeof({}) = {}", \
                              (#a), std::size(a), (#a), sizeof(a)))
#define print_elem(a, e)  \
                (std::println("{:14} type: {}, offset: {}", \
                              (#e), \
                              type_name<decltype(e)>(), \
                              (char*)((void*)e) - (char*)((void*)a)))
using AInt = volatile int;

int main() {
    int arrOne[5] = {1,2,3,4,5};
    int arrTwo[3][2] = {{1,2}, {3,4}, {5,6}};
    AInt aArrOne[5] = {1,2,3,4,5};
    AInt aArrTwo[3][2] = {{1,2}, {3,4}, {5,6}};


    print_arr(arrOne);
    // print_arr(&arrOne[0]);  // Ошибка при компиляции std::size()
    print_elem(arrOne, arrOne);
    print_elem(arrOne, &arrOne[0]);
    print_elem(arrOne, &arrOne[4]);
    print_arr(arrTwo);
    print_arr(arrTwo[1]);
    // print_arr(&arrTwo[1][1]);  // Ошибка при компиляции std::size()
    print_elem(arrTwo, arrTwo);
    print_elem(arrTwo, &arrTwo[0]);
    print_elem(arrTwo, &arrTwo[0][0]);
    print_elem(arrTwo, &arrTwo[2]);
    print_elem(arrTwo, &arrTwo[2][1]);
    print_elem(aArrOne, aArrOne);
    print_elem(aArrOne, &aArrOne[0]);
    print_elem(aArrOne, &aArrOne[4]);
    print_elem(aArrTwo, aArrTwo);
    print_elem(aArrTwo, &aArrTwo[0]);
    print_elem(aArrTwo, &aArrTwo[0][0]);
    print_elem(aArrTwo, &aArrTwo[2]);
    print_elem(aArrTwo, &aArrTwo[2][1]);
}

Выдаст примерно следующее:

std::size(arrOne) = 5, sizeof(arrOne) = 20
arrOne         type: int[5], offset: 0
&arrOne[0]     type: int *, offset: 0
&arrOne[4]     type: int *, offset: 16
std::size(arrTwo) = 3, sizeof(arrTwo) = 24
std::size(arrTwo[1]) = 2, sizeof(arrTwo[1]) = 8
arrTwo         type: int[3][2], offset: 0
&arrTwo[0]     type: int (*)[2], offset: 0
&arrTwo[0][0]  type: int *, offset: 0
&arrTwo[2]     type: int (*)[2], offset: 16
&arrTwo[2][1]  type: int *, offset: 20
aArrOne        type: volatile int[5], offset: 0
&aArrOne[0]    type: volatile int *, offset: 0
&aArrOne[4]    type: volatile int *, offset: 16
aArrTwo        type: volatile int[3][2], offset: 0
&aArrTwo[0]    type: volatile int (*)[2], offset: 0
&aArrTwo[0][0] type: volatile int *, offset: 0
&aArrTwo[2]    type: volatile int (*)[2], offset: 16
&aArrTwo[2][1] type: volatile int *, offset: 20
→ Ссылка