Arrays in C ++


We continue the series “C ++, digging deeper”. The purpose of this series is to tell as much as possible about the various features of the language, possibly quite special. This is the fourth article in the series, the first three on overloading in C ++ are here , here and here .

This article is about arrays. Arrays can be attributed to the most ancient layers of C ++, they came from the first versions of C. Nevertheless, arrays are included in the object-oriented type system of C ++, although with some caveats. It is important for the programmer to know about these features in order to avoid potential errors. The article also explores another C legacy — trivial types and uninitialized variables. Some of the innovations of C ++ 11 affect the work with arrays; all these new features are also described in detail. So, let's try to tell everything about arrays.



Table of contents


Table of contents

  1.
    1.1.
    1.2.
    1.3.
    1.4.
  2.
    2.1.
    2.2.
  3.
    3.1.
    3.2.
      3.2.1
      3.2.2
      3.2.3
  4.
    4.1.
    4.2.
  5.
  6.
    6.1.
    6.2.
    6.3.
  7.
  8.
  




1. General Provisions


An array is the simplest aggregate type. He models a set of similar elements arranged in a row in a continuous segment of memory. Arrays in one form or another are supported by almost all programming languages, and it is not surprising that they appeared in the first versions of C and then became part of C ++.



1.1. Array declaration


If Tsome type, Nconstant or expression evaluated at compile time, then the instruction


T a[N];

a « N T» (array of N elements of the type T). N std::size_t, , , . sizeof(T) , , , N*sizeof(T) . . T[N], . , , , , .


(regular arrays), , «» C++ .

:


const int N = 8;
constexpr int Square(int n) { return n * n; }

int a1[1];
int a2[N];
int a3['Q'];
int a4[Square(2)];

:


int n;

int b1[0];   //  
int b2[n];   //      
int b3["Q"]; //     size_t

, 0 N-1. :


int a[4];
a[0] = 42;
int t = a[3];

, .


, .


int a[4], b[8];

. typedef:


typedef int I4[4];

(C++11) using:


using I4 = int[4];

:


I4 a, b;

,


int a[4], b[4];


1.2.


sizeof .


sizeof , .


_countof() ( MSVS <cstdlib>) , . ++17 std::size(), ( , ).


int a[4];
std::cout << sizeof(a) << ' ' << std::size(a) << '\n';

: 16 4


C++11 ( ) std::begin() std::end(). std::begin() , std::end() past-the-last . ( : std::cbegin(), std::cend().) for.


int a[4]{ 4, 3, 2, 1 };

for (auto t : a)
{
    std::cout << t << ' ';
}

:


std::sort(std::begin(a), std::end(a));


1.3.


, , , . , , . (, .) . (. 6) .



1.4.


, void.


.


int u, v;
int &rr[2] = { u, v }; // 

.


int * const rr[2] = { &u, &v };

( 3.2.)


C++11 std::reference_wrapper<>. , . , - get(). .


int u = 42, v = 5;
std::reference_wrapper<int> rr[2] = { u, v };
std::cout << rr[0] << ' ' << rr[1] << '\n'; // : 42 5
++rr[0];
rr[1].get() = 125;                          // get() 
std::cout << u << ' ' << v << '\n';         // : 43 125

.


int ff[2](double); // 

.


int (*ff[2])(double);

std::reference_wrapper<> , — , &. — std::function<>, .


auto.


auto x[2] = {1, 2}   // 

const , .


using I4 = int[4];
const I4 ci; //  ,   const int ci[4];


2.


, C++.



2.1.


, , «». (decay, array-to-pointer decay). (Decay .) , . sizeof, & ( ) . sizeof 1.2, 4. decltype , .


, . ( C) :


const int N = 100;
int a[N];
for (int *d = a, *end = d + N; d < end; ++d)
{
    *d = rand();
}

, .


.


void Foo(int a[4]);
void Foo(int a[]);
void Foo(int *a);

— . (, ).


.


// file 1
int A[4];

// file 2
extern int A[];

.


auto .


int a[4];
auto da = a; //  da   int*



template<typename T>
void Foo(T t);

, .


. ( C .) .


class B {/* ... */};
class D : public B {/* ... */};
void Foo(B[], int size); //     B

.


D d[4];
Foo(d, _countof(d));

sizeof(B) < sizeof(D), Foo() d ( , ) , , Foo() . , , .



2.2.


( ) , «». , :


using I4 = int[4];
I4 a;
I4 b = a; // 
I4 b2;
b2 = a; // 

.


I4 Foo(); // 

//, ( ) .


struct X 
{ 
    int A[4]; 
};

, .


X Foo();
X x;
X x2 = x;
X x3;
x3 = Foo();


3.


.



3.1.


++. , — . , . , . , ++, . , , , , , , . : , , , , .


, - . . , . , - , .


: (), , , , . , . .


, , .


++11 , ( <type_traits>). , . std::is_trivial<>::value true, T false .



3.2.



3.2.1.


, . , , . , , .


C :


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

++11 (uniform initialization) :


int a[4]{ 1, 2, 3, 4 };

=, , , , .


, .


int a[] { 1, 2, 3, 4 };

, . , (, , ), . , , .


int a[4]{};

.


const int a[4] = { 3, 2, 1 };

, .


.


const char str[] = "meow";
const wchar_t wstr[] = L"meow";

, .


3.2.2.


++11 , . : .


class X
{
    int a[4]{ 1, 2, 3, 4 };
    int b[2];
// ...
public:
    X(int u, int v) : b{ u, v } 
    {}
// ...
};

, .


, , , .


class X
{
    static int A[];
// ...
};

int X::A[] = { 1, 2, 3, 4 };


3.2.3.


, , , (, , constexpr). , , — . :


T a[] = {x1 /*, ... */};



T a[]{x1 /*, ... */};



T t = x1;

. .


.


class Int
{
    int m_Value;
public:
    Int(int v) : m_Value(v) {}
// ...
};
// ...
int x, y;
// ...
Int rr[] = { x, y };

Int explicit, .


Int rr[] = { Int(x), Int(y) };

. .



4.



4.1.




T a[N];

:


T(*pa)[N] = &a;

&. T(*)[N].


, N T.


— ( , , ), . , «» . .


int a[4];
int(*pa)[4] = &a; // OK
int(*p2)[2] = &a; // ,   

, .


* .


(*pa)[3] = 42;

.


using I4 = int[4];
I4 a{ 1, 2, 3, 4 };
I4 *pa = &a;

auto, .


int a[4];
auto pa = &a; //  pa   int(*)[4]

, .



4.2.




T a[N];

:


T(&ra)[N] = a;

, . T(&)[N].


.


T(*pa)[N] = &a;
T(&ra)[N] = *pa;

, «» . .


int a[4];
int(&ra)[4] = a; // OK
int(&r2)[2] = a; // ,   

, .


ra[3] = 0;

, .




void Foo(T(&a)[N]);

T[N], .


.


using I4 = int[4];
I4 a{ 1, 2, 3, 4 };
I4 &ra = a;

auto, .


int a[4];
auto &ra = a; //  ra   int(&)[4]

& auto, , ra int*.




template<typename T>
void Foo(T& t);

, .


.


template<typename T, std::size_t N>
void Foo(T(&a)[N]);

T N ( ). , . _countof() std::size(), std::begin() std::end(), for . 5 .



5.


C++ , a[N, M] , « », a[N][M].


T , N M , ,


T a[N][M];

a , N , M T. . a[i][j], i 0 N-1, j 0 M-1, . , . N , M . T[N][M].


a[i] M T. , .


T *dai = a[i];
T(*pai)[M] = &a[i];
T(&rai)[M] = a[i];

. , .


T a[N][M];
T(*da)[M] = a;

, :


void Foo(T a[N][M]);
void Foo(T a[][M]);
void Foo(T(*a)[M]);

, .


.


using I4 = int[4];
I4 b[2];

,


int b[2][4];

:


int b[2][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}};

, {}. .


int b[][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}}; // 
int b[][] = {{1, 2, 3, 4}, {5, 6, 7, 8}};  // 

:


T a[N][M];
T(*pa)[N][M] = &a;

. .


template<typename T, std::size_t N, std::size_t M>
void Print2dArray(T(&a)[N][M])
{
    for (int i = 0; i < N; ++i)
    {
        for (int j = 0; j < M; ++j)
        {
            std::cout << a[i][j] << ' ';
        }
        std::cout << '\n';
    }
}
// ...
int b[2][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}};
Print2dArray(b);

:


1 2 3 4
5 6 7 8

.


T mtx[N][M];

N , M , mtx[i][j] i- j- , mtx[i] M, i- . , . , .



6.


C++ « ». , ( ). . C++ .



6.1.


T , n , ,


T *pa = new T[n];

. n std::size_t, . , , n*sizeof(T), . pa .


T , , .


C++11 .


int *pa = new int[n]{1, 2, 3, 4};

, ( , n ). , , . , , .


new[] . , , T , . - , , , . std::bad_alloc.


delete[], , new[].


delete[] pa;

, , , ( ), .


pa, new[], , ( «») , . for.



6.2.


std::unique_ptr<> (. [Josuttis]). , [] -> delete[] . :


int n = 100; 
std::unique_ptr<int[]> aptr(new int[n]);
for (int i = 0; i < n; ++i)
{
    aptr[i] = i; 
}

: , , for. std::unique_ptr<> , std::vector<>. std::shared_ptr<> .



6.3.


, new T[n][m], n m , . , , . M , , :


T(*pa)[M] = new T[n][M];

new[] . pa[i][j], pa[i] M T.


.


using I4 = int[4];
I4 *pa = new I4[n];

[] , , . .


// 2D interface to a buffer
template<typename T>
class MatrixView 
{
    T * const m_Buff;
    int const m_RowCount;
    int const m_ColCount;
public:
    MatrixView(T* buff, int rowCount, int colCount)
        : m_Buff(buff)
        , m_RowCount(rowCount)
        , m_ColCount(colCount)
    {}
    T *operator[](int rowInd) const
    {
        return m_Buff + rowInd * m_ColCount;
    }
};
// buffer owner
template<typename T>
class DynBuffer 
{
    T* const m_Buff;
protected:
    T* Buff() const { return m_Buff; };
    DynBuffer(int length) : m_Buff(new T[length]{}) {}
    ~DynBuffer() { delete[] m_Buff; }
    DynBuffer(const DynBuffer&) = delete;
    DynBuffer& operator=(const DynBuffer&) = delete;
};

template<typename T>
class MatrixSimple 
    : DynBuffer<T>, public MatrixView<T>
{
    using Buff = DynBuffer<T>;
    using View = MatrixView<T>;
public:
    MatrixSimple(int rowCount, int colCount)
        : Buff(rowCount * colCount)
        , View(Buff::Buff(), rowCount, colCount)
    {}
};

:


MatrixSimple<int> mtx(3, 3);
mtx[1][2] = 42; //  ,  

proxy-, , RowProxy, . , , , - begin(), end(), etc. .



7.


, «». T[]. .


//  
template <typename T>
class U
{
public:
    const char* Tag() const { return "base"; }
};

//    
template <typename T>
class U<T*>
{
public:
    const char* Tag() const { return "pointer"; }
};

//    
template <typename T>
class U<T[]>
{
public:
    const char* Tag() const { return "array"; }
};

U<int> u1;
U<int*> u2;
U<int[]> u3;

std::cout << u1.Tag() << ' ' << u2.Tag() << ' ' << u3.Tag();

: base pointer array


std::unique_ptr<>, , . 6.2.



8.


( ), .


std::array<>. ( C++11, . [Josuttis].) , : . , . :


std::array<int, 4> a{1, 2, 3, 4};

.


for (int i = 0; i < a.size(); ++i)
{
    std::cout << a[i] << ' ';
}
for (auto it = a.begin(); it != a.end(); ++it)
{
    std::cout << *it << ' ';
}
for (auto t : a)
{
    std::cout << t << ' ';
}

std::vector<>. , ( №1), - .


std::valarray<>. .




[Josuttis]
Josattis, Nikolai M. C ++ Standard Library: Reference Guide, 2nd ed .: Per. from English - M .: LLC “I.D. Williams, 2014.




All Articles