【C言語】C言語に配列型はあるのか？

## 配列とは...

まずC言語での話以前に、ほとんどすべてのプログラミング言語(以下、言語)においてどのような形であれ実装されている「配列」とはどのようなものか。



> an **array data structure**, or simply an **array**, is a [data structure](https://en.wikipedia.org/wiki/Data_structure)...

(『Array data structure』 en.wikipedia.org)

> **配列**とは、複数のデータを連続的に並べた[データ構造](https://e-words.jp/w/データ構造.html)。各データをその配列の要素といい、自然数などの[添字](https://e-words.jp/w/添字.html)（[インデックス](https://e-words.jp/w/インデックス.html)）で識別される。

(『配列』, e-words.jp)



つまり配列とは、要素を連続的に並べインデックスを用いて各要素にアクセスするような**データ構造**のこと。ほとんどすべての言語においてそのようなデータ構造は実装されている。またそのようなデータ構造の実装の一つとして次のようなものがある。



> *配列* は、**同じデータ型**のオブジェクトのコレクションであり、メモリー内に連続して割り振られます。 配列内の個々のオブジェクト *elements* は、配列内のそれらの位置を基にしてアクセスされます。

(4章『配列』, IBM i 7.1 ILE C/C++ 解説書)



要素をメモリー内に連続して割り振り、同じデータ型すなわち同じサイズとすることによって配列内のそれらの相対位置を基にしてアクセスできるようにし、インデックスでの識別を可能にしている。すべての言語において上記のようなもの[^1]
[^1]: *配列* は、**同じデータ型**のオブジェクトのコレクションであり、メモリー内に連続して割り振られます。 配列内の個々のオブジェクト *elements* は、配列内のそれらの位置を基にしてアクセスされます。

が配列の実装として存在しているとは言い切れないが、少なくともC言語においては上記のように実装されている。

## 配列型とは...

>− 配列型（array type）は，要素型（element type）と呼ぶ特定のメンバオブジェクト型をもつ空でないオブジェクトの集合を連続して割り付けたものを表す(36)。

(P26, JIS X 3010:2003)

> — An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type.

(§ 6.2.5 ISO/IEC 9899:2017)

> An array is a data structure that lets you store one or more elements consecutively in memory.

[(§ 2.5 The GNU C Reference Manual)](https://www.gnu.org/software/gnu-c-manual/gnu-c-manual.html#Arrays)



これがC言語(ANSI C)での配列型の定義である。ではその配列型はどのように宣言すればよいのだろうか？

## 宣言とは...

宣言とは、与えられた*identifiers(識別子)*の解釈を指定します。サイズ、スコープなど。宣言は、*declaration-specifiers* と任意の *init-declarator-list* からなります。そこでそれぞれを次のように置き換えます。

*declaration-specifiers* --> `T`

*init-declarator-list* --> `D1, ...`

`T`には次のようなものが当てはまります。

```c
int
char
extern float
const int
long int
long long int
unsigned char
auto int
inline
struct a
typedef b
```

`D1`には次のようなものが当てはまります。

```
* ptr
* const ptr
* const * ptr
** ptr
num
```



### 配列型の宣言



> If in the declaration T D1, D1 has the form D[ expression_opt ] or D[*], then the contained identifier has type “array of T.” 

(§ 7.7.3, sgi1999c.pdf, C Language Reference Manual)



つまり、宣言`T D1`において、`D1`が、`D[式]`もしくは`D[*]`の場合`D`に含まれるその識別子は**Tの配列型**を持ちます。また、



>You declare an array by specifying the data type for its elements, its name, and the number of elements it can store.  Here is an example that declares an array that can store ten integers:
>
>​		int my_array[10];
>
>For standard C code, the number of elements in an array must be positive.

[(§ 2.5.1 The GNU C Reference Manual)](https://www.gnu.org/software/gnu-c-manual/gnu-c-manual.html#Declaring-Arrays)



また、



> A declarator may have the form
>
> ​		D[constant-expression]
>
> or
>
> ​		D[ ]
>
> In the first case the constant expression is an expression whose value is determinable at compile time, and whose type is `int .` in the second the constant 1 is used.  (Constant expressions are defined precisely in §15.)   Such a declarator makes the contained identifier have type ‘‘array.’

(§ 8.4, Dennis M. Ritchie, C Reference Manual)



とあります。つまり、簡単に言うと配列型は、`D[]`または`D[constant-expression]`または...の、array declarator-配列宣言子`[]`を用いて宣言されたものであるということです。



## malloc()

しかし、配列はポインタ型の仕組みを用いて実装されています。



> Every time an identifier of array type appears in an expression, it is converted into a pointer to the first member of the array.  Because of this conversion, arrays are not lvalues.  By definition, the subscript operator `[]` is interpreted in such a way that ‘‘E1[E2]’’ is identical to ‘‘*( ( E1) + (E2 ) )’’.  Because of the conversion rules which apply to +, if E1 is an array and E2 an integer, then E1[E2] refers to the E2-th member of E1. Therefore, despite its asymmetric appearance, subscripting is a commutative operation.

(§ 14.3, Dennis M. Ritchie, C Reference Manual)



つまりsubscript operator-サブスクリプト演算子(またはindexing operator-インデクシング演算子)`[]`はポインタ型に対して定義されて、

```
int array[3];

== 1 ==  --> == 2 ==
array[1] --> *(array+1) 
```

`== 1 ==`の時点で`array`の型は`Array of int`から`Pointer of int`に変換されています。そして`[]`演算子は`== 2 ==`として定義されています。また、



> A  parameter  declared  to  have  array  or  function  type  is adjusted to have a pointer type as described in 6.9.1.

(§ 6.5.2 ISO/IEC 9899:2017)

> In  either  case,  the  type  of  each  parameter  is  adjusted  as described in 6.7.6.3 for a parameter type list; the resulting type shall be a complete object type.

(§ 6.9.1 ISO/IEC 9899:2017)

>A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’,  where  the  type  qualifiers  (if  any)  are  those  specified  within  the [ and ] of  the array  type  derivation.

(§ 6.7.6.3 ISO/IEC 9899:2017)



つまり、関数の実引数にTの配列型が指定された場合、そのTの配列型はTのポインタ型に変換され、対応する仮引数に代入される。ということは関数`inside`において`a`、`b`はともに配列型ではないものの、配列型はそれの要素型と要素の数によって特徴づけられることから考えて`a`、`b`は同等の配列(

`a`、`b`はポインタ型だが配列を象徴していることが明らかまたは、配列にアクセスするためのポインタ型であることが明らかまたは、配列を象徴していないにしてもオフセットを適用することにより、`sizeof(int)`サイズで`3`要素数の(同等の)配列にアクセスできるから「配列」呼んでもよいのではないだろうか？「グレーな配列」?

)といえる。

```c
void inside(int * a) {
	int *b = (int *) malloc(3 * sizeof(int));
}

void outside(void) {
	int a[3] = {0, 0, 0};
    
    inside(a);
}
```