diff options
| author | Craig Jennings <c@cjennings.net> | 2024-04-07 13:41:34 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2024-04-07 13:41:34 -0500 |
| commit | 754bbf7a25a8dda49b5d08ef0d0443bbf5af0e36 (patch) | |
| tree | f1190704f78f04a2b0b4c977d20fe96a828377f1 /devdocs/c/language%2Farray.html | |
new repository
Diffstat (limited to 'devdocs/c/language%2Farray.html')
| -rw-r--r-- | devdocs/c/language%2Farray.html | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/devdocs/c/language%2Farray.html b/devdocs/c/language%2Farray.html new file mode 100644 index 00000000..34623494 --- /dev/null +++ b/devdocs/c/language%2Farray.html @@ -0,0 +1,198 @@ + <h1 id="firstHeading" class="firstHeading">Array declaration</h1> <p>Array is a type consisting of a contiguously allocated nonempty sequence of objects with a particular <i>element type</i>. The number of those objects (the array size) never changes during the array lifetime.</p> +<h3 id="Syntax"> Syntax</h3> <p>In the <a href="declarations" title="c/language/declarations">declaration grammar</a> of an array declaration, the <i>type-specifier</i> sequence designates the <i>element type</i> (which must be a complete object type), and the <i>declarator</i> has the form:</p> +<table class="t-sdsc-begin"> <tr class="t-sdsc"> <td> <code>[</code> <code>static</code><span class="t-mark">(optional)</span> <span class="t-spar">qualifiers</span> <span class="t-mark">(optional)</span> <span class="t-spar">expression</span> <span class="t-mark">(optional)</span> <code>]</code> <span class="t-spar">attr-spec-seq</span> <span class="t-mark">(optional)</span> </td> <td> (1) </td> <td class="t-sdsc-nopad"> </td> +</tr> <tr class="t-sdsc"> <td> <code>[</code> <span class="t-spar">qualifiers</span> <span class="t-mark">(optional)</span> <code>static</code><span class="t-mark">(optional)</span> <span class="t-spar">expression</span> <span class="t-mark">(optional)</span> <code>]</code> <span class="t-spar">attr-spec-seq</span> <span class="t-mark">(optional)</span> </td> <td> (2) </td> <td class="t-sdsc-nopad"> </td> +</tr> <tr class="t-sdsc"> <td> <code>[</code> <span class="t-spar">qualifiers</span> <span class="t-mark">(optional)</span> <code>*</code> <code>]</code> <span class="t-spar">attr-spec-seq</span> <span class="t-mark">(optional)</span> </td> <td> (3) </td> <td class="t-sdsc-nopad"> </td> +</tr> +</table> <div class="t-li1"> +<span class="t-li">1,2)</span> General array declarator syntax</div> <div class="t-li1"> +<span class="t-li">3)</span> Declarator for VLA of unspecified size (can appear in function prototype scope only) where</div> <table class="t-par-begin"> <tr class="t-par"> <td> <span class="t-spar">expression</span> </td> <td> - </td> <td> any expression other than <a href="operator_other#Comma_operator" title="c/language/operator other">comma operator</a>, designates the number of elements in the array </td> +</tr> <tr class="t-par"> <td> <span class="t-spar">qualifiers</span> </td> <td> - </td> <td> any combination of <a href="const" title="c/language/const"><code>const</code></a>, <a href="restrict" title="c/language/restrict"><code>restrict</code></a>, or <a href="volatile" title="c/language/volatile"><code>volatile</code></a> qualifiers, only allowed in function parameter lists; this qualifies the pointer type to which this array parameter is transformed </td> +</tr> <tr class="t-par"> <td> <span class="t-spar">attr-spec-seq</span> </td> <td> - </td> <td> <span class="t-mark-rev t-since-c23">(C23)</span>optional list of <a href="attributes" title="c/language/attributes">attributes</a>, applied to the declared array </td> +</tr> +</table> <div class="c source-c"><pre data-language="c">float fa[11], *afp[17]; // fa is an array of 11 floats + // afp is an array of 17 pointers to floats</pre></div> <h3 id="Explanation"> Explanation</h3> <p>There are several variations of array types: arrays of known constant size, variable-length arrays, and arrays of unknown size.</p> +<h4 id="Arrays_of_constant_known_size"> Arrays of constant known size</h4> <p>If <span class="t-spar">expression</span> in an array declarator is an <a href="constant_expression#Integer_constant_expression" title="c/language/constant expression">integer constant expression</a> with a value greater than zero <span class="t-rev-inl t-since-c99"><span>and the element type is a type with a known constant size (that is, elements are not VLA)</span><span><span class="t-mark-rev t-since-c99">(since C99)</span></span></span>, then the declarator declares an array of constant known size:</p> +<div class="c source-c"><pre data-language="c">int n[10]; // integer constants are constant expressions +char o[sizeof(double)]; // sizeof is a constant expression +enum { MAX_SZ=100 }; +int n[MAX_SZ]; // enum constants are constant expressions</pre></div> <p>Arrays of constant known size can use <a href="array_initialization" title="c/language/array initialization">array initializers</a> to provide their initial values:</p> +<div class="c source-c"><pre data-language="c">int a[5] = {1,2,3}; // declares int[5] initialized to 1,2,3,0,0 +char str[] = "abc"; // declares char[4] initialized to 'a','b','c','\0'</pre></div> <table class="t-rev-begin"> <tr class="t-rev t-since-c99"> +<td> <p>In function parameter lists, additional syntax elements are allowed within the array declarators: the keyword <code>static</code> and <span class="t-spar">qualifiers</span>, which may appear in any order before the size expression (they may also appear even when the size expression is omitted).</p> +<p>In each <a href="operator_other#Function_call" title="c/language/operator other">function call</a> to a function where an array parameter uses the keyword <code>static</code> between <code>[</code> and <code>]</code>, the value of the actual parameter must be a valid pointer to the first element of an array with at least as many elements as specified by <span class="t-spar">expression</span>:</p> +<div class="c source-c"><pre data-language="c">void fadd(double a[static 10], const double b[static 10]) +{ + for (int i = 0; i < 10; i++) + { + if (a[i] < 0.0) return; + a[i] += b[i]; + } +} +// a call to fadd may perform compile-time bounds checking +// and also permits optimizations such as prefetching 10 doubles +int main(void) +{ + double a[10] = {0}, b[20] = {0}; + fadd(a, b); // OK + double x[5] = {0}; + fadd(x, b); // undefined behavior: array argument is too small +}</pre></div> <p>If <span class="t-spar">qualifiers</span> are present, they qualify the pointer type to which the array parameter type is transformed:</p> +<div class="c source-c"><pre data-language="c">int f(const int a[20]) +{ + // in this function, a has type const int* (pointer to const int) +} +int g(const int a[const 20]) +{ + // in this function, a has type const int* const (const pointer to const int) +}</pre></div> <p>This is commonly used with the <a href="restrict" title="c/language/restrict"><code>restrict</code></a> type qualifier:</p> +<div class="c source-c"><pre data-language="c">void fadd(double a[static restrict 10], + const double b[static restrict 10]) +{ + for (int i = 0; i < 10; i++) // loop can be unrolled and reordered + { + if (a[i] < 0.0) + break; + a[i] += b[i]; + } +}</pre></div> <h4 id="Variable-length_arrays"> Variable-length arrays</h4> <p>If <span class="t-spar">expression</span> is not an <a href="constant_expression#Integer_constant_expression" title="c/language/constant expression">integer constant expression</a>, the declarator is for an array of variable size.</p> +<p>Each time the flow of control passes over the declaration, <span class="t-spar">expression</span> is evaluated (and it must always evaluate to a value greater than zero), and the array is allocated (correspondingly, <a href="lifetime" title="c/language/lifetime">lifetime</a> of a VLA ends when the declaration goes out of scope). The size of each VLA instance does not change during its lifetime, but on another pass over the same code, it may be allocated with a different size.</p> +<div class="c source-c"><pre data-language="c">#include <stdio.h> + +int main(void) +{ + int n = 1; +label:; + int a[n]; // re-allocated 10 times, each with a different size + printf("The array has %zu elements\n", sizeof a / sizeof *a); + if (n++ < 10) + goto label; // leaving the scope of a VLA ends its lifetime +}</pre></div> <p>If the size is <code>*</code>, the declaration is for a VLA of unspecified size. Such declaration may only appear in a function prototype scope, and declares an array of a complete type. In fact, all VLA declarators in function prototype scope are treated as if <span class="t-spar">expression</span> were replaced by <code>*</code>.</p> +<div class="c source-c"><pre data-language="c">void foo(size_t x, int a[*]); +void foo(size_t x, int a[x]) +{ + printf("%zu\n", sizeof a); // same as sizeof(int*) +}</pre></div> <p>Variable-length arrays and the types derived from them (pointers to them, etc) are commonly known as "variably-modified types" (VM). Objects of any variably-modified type may only be declared at block scope or function prototype scope.</p> +<div class="c source-c"><pre data-language="c">extern int n; +int A[n]; // Error: file scope VLA +extern int (*p2)[n]; // Error: file scope VM +int B[100]; // OK: file-scope array of constant known size +void fvla(int m, int C[m][m]); // OK: prototype-scope VLA</pre></div> <p>VLA must have automatic or allocated storage duration. Pointers to VLA, but not VLA themselves may also have static storage duration. No VM type may have linkage.</p> +<div class="c source-c"><pre data-language="c">void fvla(int m, int C[m][m]) // OK: block scope/auto duration pointer to VLA +{ + typedef int VLA[m][m]; // OK: block scope VLA + int D[m]; // OK: block scope/auto duration VLA +// static int E[m]; // Error: static duration VLA +// extern int F[m]; // Error: VLA with linkage + int (*s)[m]; // OK: block scope/auto duration VM + s = malloc(m * sizeof(int)); // OK: s points to VLA in allocated storage +// extern int (*r)[m]; // Error: VM with linkage + static int (*q)[m] = &B; // OK: block scope/static duration VM} +}</pre></div> <p>Variably-modified types cannot be members of structs or unions.</p> +<div class="c source-c"><pre data-language="c">struct tag +{ + int z[n]; // Error: VLA struct member + int (*y)[n]; // Error: VM struct member +};</pre></div> </td> <td><span class="t-mark-rev t-since-c99">(since C99)</span></td> +</tr> <tr class="t-rev t-since-c11 t-until-c23"> +<td> <p>If the compiler defines the macro constant <code>__STDC_NO_VLA__</code> to integer constant <code>1</code>, then VLA and VM types are not supported.</p> +</td> <td> +<span class="t-mark-rev t-since-c11">(since C11)</span><br><span class="t-mark-rev t-until-c23">(until C23)</span> +</td> +</tr> <tr class="t-rev t-since-c23"> +<td> <p>If the compiler defines the macro constant <code>__STDC_NO_VLA__</code> to integer constant <code>1</code>, then VLA objects with automatic storage duration are not supported.</p> +<p>The support for VM types and VLAs with allocated storage durations is mandated.</p> +</td> <td><span class="t-mark-rev t-since-c23">(since C23)</span></td> +</tr> </table> <h4 id="Arrays_of_unknown_size"> Arrays of unknown size</h4> <p>If <span class="t-spar">expression</span> in an array declarator is omitted, it declares an array of unknown size. Except in function parameter lists (where such arrays are transformed to pointers) and when an <a href="array_initialization" title="c/language/array initialization">initializer</a> is available, such type is an <a href="type#Incomplete_types" title="c/language/type">incomplete type</a><span class="t-rev-inl t-since-c99"><span> (note that VLA of unspecified size, declared with <code>*</code> as the size, is a complete type)</span><span><span class="t-mark-rev t-since-c99">(since C99)</span></span></span>:</p> +<div class="c source-c"><pre data-language="c">extern int x[]; // the type of x is "array of unknown bound of int" +int a[] = {1,2,3}; // the type of a is "array of 3 int"</pre></div> <table class="t-rev-begin"> <tr class="t-rev t-since-c99"> +<td> <p>Within a <a href="struct" title="c/language/struct">struct</a> definition, an array of unknown size may appear as the last member (as long as there is at least one other named member), in which case it is a special case known as <i>flexible array member</i>. See <a href="struct" title="c/language/struct">struct</a> for details:</p> +<div class="c source-c"><pre data-language="c">struct s { int n; double d[]; }; // s.d is a flexible array member +struct s *s1 = malloc(sizeof (struct s) + (sizeof (double) * 8)); // as if d was double d[8]</pre></div> </td> <td><span class="t-mark-rev t-since-c99">(since C99)</span></td> +</tr> </table> <h4 id="Qualifiers"> Qualifiers</h4> <table class="t-rev-begin"> <tr class="t-rev t-until-c23"> +<td> <p>If an array type is declared with a <a href="const" title="c/language/const"><code>const</code></a>, <a href="volatile" title="c/language/volatile"><code>volatile</code></a><span class="t-rev-inl t-since-c99"><span>, or <a href="restrict" title="c/language/restrict"><code>restrict</code></a></span><span><span class="t-mark-rev t-since-c99">(since C99)</span></span></span> qualifier (which is possible through the use of <a href="typedef" title="c/language/typedef">typedef</a>), the array type is not qualified, but its element type is:</p> +</td> <td><span class="t-mark-rev t-until-c23">(until C23)</span></td> +</tr> <tr class="t-rev t-since-c23"> +<td> <p>An array type and its element type are always considered to be identically qualified, except that an array type is never considered to be <a href="atomic" title="c/language/atomic"><code>_Atomic</code></a>-qualified.</p> +</td> <td><span class="t-mark-rev t-since-c23">(since C23)</span></td> +</tr> </table> <div class="c source-c"><pre data-language="c">typedef int A[2][3]; +const A a = {{4, 5, 6}, {7, 8, 9}}; // array of array of const int +int* pi = a[0]; // Error: a[0] has type const int* +void* unqual_ptr = a; // OK until C23; error since C23 +// Notes: clang applies the rule in C++/C23 even in C89-C17 modes</pre></div> <table class="t-rev-begin"> <tr class="t-rev t-since-c11"> +<td> <p><a href="atomic" title="c/language/atomic"><code>_Atomic</code></a> is not allowed to be applied to an array type, although an array of atomic type is allowed.</p> +<div class="c source-c"><pre data-language="c">typedef int A[2]; +// _Atomic A a0 = {0}; // Error +// _Atomic(A) a1 = {0}; // Error +_Atomic int a2[2] = {0}; // OK +_Atomic(int) a3[2] = {0}; // OK</pre></div> </td> <td><span class="t-mark-rev t-since-c11">(since C11)</span></td> +</tr> </table> <h4 id="Assignment"> Assignment</h4> <p>Objects of array type are not <a href="value_category" title="c/language/value category">modifiable lvalues</a>, and although their address may be taken, they cannot appear on the left hand side of an assignment operator. However, structs with array members are modifiable lvalues and can be assigned:</p> +<div class="c source-c"><pre data-language="c">int a[3] = {1,2,3}, b[3] = {4,5,6}; +int (*p)[3] = &a; // okay, address of a can be taken +// a = b; // error, a is an array +struct { int c[3]; } s1, s2 = {3,4,5}; +s1 = s2; // okay: can assign structs holding array members</pre></div> <h4 id="Array_to_pointer_conversion"> Array to pointer conversion</h4> <p>Any <a href="value_category" title="c/language/value category">lvalue expression</a> of array type, when used in any context other than</p> +<ul> +<li> as the operand of the <a href="operator_member_access" title="c/language/operator member access">address-of operator</a> </li> +<li> as the operand of <a href="sizeof" title="c/language/sizeof"><code>sizeof</code></a> </li> +<li> as the operand of <a href="typeof" title="c/language/typeof"><code>typeof</code></a> and <a href="https://en.cppreference.com/mwiki/index.php?title=c/language/typeof_unqual&action=edit&redlink=1" class="new" title="c/language/typeof unqual (page does not exist)"><code>typeof_unqual</code></a> <span class="t-mark-rev t-since-c23">(since C23)</span> </li> +<li> as the string literal used for <a href="array_initialization" title="c/language/array initialization">array initialization</a> </li> +</ul> <table class="t-rev-begin"> <tr class="t-rev t-since-c11"> +<td> <ul><li> as the operand of <a href="_alignof" title="c/language/ Alignof"><code>_Alignof</code></a> </li></ul> </td> <td><span class="t-mark-rev t-since-c11">(since C11)</span></td> +</tr> </table> <p>undergoes an <a href="conversion" title="c/language/conversion">implicit conversion</a> to the pointer to its first element. The result is not an lvalue.</p> +<p>If the array was declared <a href="storage_duration" title="c/language/storage duration"><code>register</code></a>, the behavior of the program that attempts such conversion is undefined.</p> +<div class="c source-c"><pre data-language="c">int a[3] = {1,2,3}; +int* p = a; +printf("%zu\n", sizeof a); // prints size of array +printf("%zu\n", sizeof p); // prints size of a pointer</pre></div> <p>When an array type is used in a function parameter list, it is transformed to the corresponding pointer type: <code>int f(int a[2])</code> and <code>int f(int* a)</code> declare the same function. Since the function's actual parameter type is pointer type, a function call with an array argument performs array-to-pointer conversion; the size of the argument array is not available to the called function and must be passed explicitly:</p> +<div class="t-example"> <div class="c source-c"><pre data-language="c">#include <stdio.h> + +void f(int a[], int sz) // actually declares void f(int* a, int sz) +{ + for (int i = 0; i < sz; ++i) + printf("%d\n", a[i]); +} + +void g(int (*a)[10]) // pointer to array parameter is not transformed +{ + for (int i = 0; i < 10; ++i) + printf("%d\n", (*a)[i]); +} + +int main(void) +{ + int a[10] = {0}; + f(a, 10); // converts a to int*, passes the pointer + g(&a); // passes a pointer to the array (no need to pass the size) +}</pre></div> </div> <h4 id="Multidimensional_arrays"> Multidimensional arrays</h4> <p>When the element type of an array is another array, it is said that the array is multidimensional:</p> +<div class="c source-c"><pre data-language="c">// array of 2 arrays of 3 ints each +int a[2][3] = {{1,2,3}, // can be viewed as a 2x3 matrix + {4,5,6}}; // with row-major layout</pre></div> <p>Note that when array-to-pointer conversion is applied, a multidimensional array is converted to a pointer to its first element, e.g., pointer to the first row:</p> +<div class="c source-c"><pre data-language="c">int a[2][3]; // 2x3 matrix +int (*p1)[3] = a; // pointer to the first 3-element row +int b[3][3][3]; // 3x3x3 cube +int (*p2)[3][3] = b; // pointer to the first 3x3 plane</pre></div> <table class="t-rev-begin"> <tr class="t-rev t-since-c99"> +<td> <p>Multidimensional arrays may be variably modified in every dimension<span class="t-rev-inl t-since-c11"><span> if VLAs are supported</span><span><span class="t-mark-rev t-since-c11">(since C11)</span></span></span>:</p> +<div class="c source-c"><pre data-language="c">int n = 10; +int a[n][2*n];</pre></div> </td> <td><span class="t-mark-rev t-since-c99">(since C99)</span></td> +</tr> </table> <h3 id="Notes"> Notes</h3> <p>Zero-length array declarations are not allowed, even though some compilers offer them as extensions (typically as a pre-C99 implementation of <a href="struct" title="c/language/struct">flexible array members</a>).</p> +<p>If the size <span class="t-spar">expression</span> of a VLA has side effects, they are guaranteed to be produced except when it is a part of a sizeof expression whose result doesn't depend on it:</p> +<div class="c source-c"><pre data-language="c">int n = 5; +size_t sz = sizeof(int (*)[n++]); // may or may not increment n</pre></div> <h3 id="References"> References</h3> <ul> +<li> C23 standard (ISO/IEC 9899:2023): </li> +<ul><li> 6.7.6.2 Array declarators (p: TBD) </li></ul> +<li> C17 standard (ISO/IEC 9899:2018): </li> +<ul><li> 6.7.6.2 Array declarators (p: 94-96) </li></ul> +<li> C11 standard (ISO/IEC 9899:2011): </li> +<ul><li> 6.7.6.2 Array declarators (p: 130-132) </li></ul> +<li> C99 standard (ISO/IEC 9899:1999): </li> +<ul><li> 6.7.5.2 Array declarators (p: 116-118) </li></ul> +<li> C89/C90 standard (ISO/IEC 9899:1990): </li> +<ul><li> 3.5.4.2 Array declarators </li></ul> +</ul> <h3 id="See_also"> See also</h3> <table class="t-dsc-begin"> <tr class="t-dsc"> <td colspan="2"> <span><a href="https://en.cppreference.com/w/cpp/language/array" title="cpp/language/array">C++ documentation</a></span> for <span class=""><span>Array declaration</span></span> </td> +</tr> </table> <div class="_attribution"> + <p class="_attribution-p"> + © cppreference.com<br>Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.<br> + <a href="https://en.cppreference.com/w/c/language/array" class="_attribution-link">https://en.cppreference.com/w/c/language/array</a> + </p> +</div> |
