「C言語/データ型と変数の高度な話題」の版間の差分
削除された内容 追加された内容
→型修飾子: +_Atomic タグ: 2017年版ソースエディター |
cleanup タグ: 2017年版ソースエディター |
||
1 行
ここでは、ISO/IEC 9899:2011(通称 C11)の ''6.7 Declarations'' で定義されている構文と意味を読み解き、データ型と変数への理解を深めようと思います。
== 宣言 ==
宣言(Declarations)
== 記憶域クラス指定子 ==
記憶域クラス指定子
| url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
| title = N1570 Committee Draft — April 12, 2011 ISO/IEC 9899:201x
| page=109, §6.7.1 ''Storage-class specifiers''
| publisher = ISO/IEC}}</ref>。
ISO/IEC 9899:2011(通称 C11)の §6.7.1 ''Storage-class specifiers''(記憶域クラス指定子)を抄訳/引用します<ref name="jtc1-sc22-wg14-n1570-6.7.2.2">{{cite book
| url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
| title = N1570 Committee Draft — April 12, 2011 ISO/IEC 9899:201x
| page=109, §6.7.2.2 ''Storage-class specifiers''
| publisher = ISO/IEC}}</ref>。
<blockquote class="toccolours" cite="http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf">
;構文
:1
:; ''{{Anchor|storage-class-specifier}}''
:: '''[[#typedef|typedef]]'''
:: '''[[#extern|extern]]'''
:: '''[[#static|static]]'''
:: '''[[# Thread local|_Thread_local]]'''
:: '''[[#auto|auto]]'''
:: '''[[#register|register]]'''
; 制約事項
:2 1つの宣言の中では、最大で1つの記憶域クラス指定子を与えることができます。ただし、_Thread_local は static または extern と共に使用することができます。
:3 ブロックスコープを持つオブジェクトの宣言では、宣言指定子に _Thread_local が含まれる場合、 static または extern も含まれなければなりません。オブジェクトのいずれかの宣言に _Thread_local が含まれている場合、そのオブジェクトのすべての宣言に含まれていなければなりません。
:4 _Thread_local は関数宣言の宣言指定子には含まれません。
; セマンティクス
:5 typedef 指定子は、構文上の利便性のためだけに ''記憶域クラス指定子'' と呼ばれていますが、これについては 6.7.8 [[#typedef|typedef]] で説明します。様々な結合と保存期間の意味については6.2.2 ''Linkages of identifiers''(識別子の結合) と6.2.4 ''Storage durations of objects''(オブジェクトの記憶域期間) で説明ました。
:6 registor 記憶域クラス指定子を持つオブジェクトの識別子の宣言は、そのオブジェクトへのアクセスが可能な限り高速であることを示唆するものである。このような提案がどの程度有効であるかは、実装で定義される。
:7 ブロックスコープを持つ関数の識別子の宣言には、extern以外の明示的な記憶域クラス指定子をつけてはならない。
:8 集成体( aggregate )や union オブジェクトがtypedef以外の記憶域クラス指定子をつけて宣言された場合、リンクに関するものを除き、記憶域クラス指定子の結果として得られる特性は、オブジェクトのメンバにも適用され、さらに再帰的に集約オブジェクトや組合メンバオブジェクトにも適用される。
前方参照:型定義(''type definitions''; 6.7.8)。
; Constraints
:2 At most, one storage-class specifier may be given in the declaration specifiers in a declaration, except that _Thread_local may appear with static or extern.
:3 In the declaration of an object with block scope, if the declaration specifiers include _Thread_local, they shall also include either static or extern. If _Thread_local appears in any declaration of an object, it shall be present in every declaration of that object.
:4 _Thread_local shall not appear in the declaration specifiers of a function declaration.
;Semantics
:5 The typedef specifier is called a ‘‘storage-class specifier’’ for syntactic convenience only; it is discussed in 6.7.8. The meanings of the various linkages and storage durations were discussed in 6.2.2 and 6.2.4.
:6 A declaration of an identifier for an object with storage-class specifier register suggests that access to the object be as fast as possible. The extent to which such suggestions are effective is implementation-defined.
: 7 The declaration of an identifier for a function that has block scope shall have no explicit storage-class specifier other than extern.
: 8 If an aggregate or union object is declared with a storage-class specifier other than typedef, the properties resulting from the storage-class specifier, except with respect to linkage, also apply to the members of the object, and so on recursively for any aggregate or union member objects.
Forward references: type definitions (6.7.8).
</blockquote>
=== typedef ===
32 ⟶ 63行目:
//例 typedefの使用例
typedef unsigned int UINT; // unsigned int型にUINTという別名をつける。
int main(void) {
UINT value;
}
</syntaxhighlight>
85 ⟶ 115行目:
上の例では、file1.cとfile2.cとでiとfunction()をそれぞれ共有している。
----
異なるスコープで宣言された識別子や、同じスコープで2回以上宣言された識別子は、結合(''Linkages''<ref>演算子の結合性は ''associativity of operators''。JISは、2つの術語に1つの訳を与えてしまっている</ref>)と呼ばれるプロセスによって、同じオブジェクトや関数を参照するようにすることができます<ref>N1570 p.36, §6.2.2 Linkages of identifiers</ref><ref name="識別子の結合">『JISX3010:2003』p.23「6.2.2 識別子の結合」</ref>。
結合には、外部結合( ''external linkage'' )、内部結合( ''internal linkage'' )、および無結合( ''none linkage'' )の3種類があります。
1つの翻訳単位( ''translation-unit'' )の中で、内部結合を持つ識別子の各宣言は、同じオブジェクトまたは関数を表します。無結合な識別子の各宣言は一意の実体を表します。
オブジェクトまたは関数のファイルスコープ識別子の宣言に、記憶域クラス指定子 static が含まれている場合、その識別子は内部リンクを持ちます。
識別子の事前宣言が可視化されているスコープにおいて、記憶域クラス指定子 extern で宣言された識別子について、事前宣言が内部または外部結合を指定している場合、後の宣言における識別子の結合は、事前宣言で指定された結合と同じである。先行宣言が見えない場合、または先行宣言が結合を指定していない場合、その識別子は外部結合を持つ。
関数の識別子の宣言に記憶域クラス指定子がない場合、その結合は、記憶域クラス指定子 extern で宣言された場合と全く同じように決定されます。
オブジェクトの識別子の宣言にファイルスコープがあり、保存クラス指定子がない場合、そのリンク先は外部となります。
オブジェクトまたは関数以外と宣言された識別子、関数のパラメータと宣言された識別子、記憶域クラス指定子 extern を持たずに宣言されたオブジェクトのブロックスコープ識別子は、結合されません。
1つの翻訳単位の中で、同じ識別子が内部リンクと外部リンクの両方で現れた場合、その動作は未定義です。
----
関数宣言にストレージクラス指定子 static を含めることができるのは、ファイルスコープにある場合のみです<ref>6.7.10 storage-class specifiers</ref>。
=== static ===
111 ⟶ 145行目:
//※このプログラムはコンパイルエラーとなる。
// file1.c
#include <stdio.h>
117 ⟶ 151行目:
extern void function();
int main(void) {
i = 1;
printf("最初のiの値は%d。\n", i);
function();
printf("function関数を呼び出した後のiの値は%d。\n", i);
}
// file2.c
static int i; // file1.cからは見えない。
static void function() // file1.cからは見えない。
{
i =
}
</syntaxhighlight>
139 ⟶ 172行目:
#include <stdio.h>
void function() {
static int i = 0; // staticを使うことで前の値が保持される。
++i;
printf("iの値は%d。\n", i);
}
int main(void) {
function();
}
</syntaxhighlight>
174 ⟶ 205行目:
//例 autoの使用例
int main(void) {
auto int i;//「int i;」と書いても同じである。
}
</syntaxhighlight>
459 ⟶ 489行目:
=== enum ===
列挙体( enumeration )は、名前の付いた整数
| url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
| title = N1570 Committee Draft — April 12, 2011 ISO/IEC 9899:201x
544 ⟶ 574行目:
[[C++]]のコードとしてコンパイルすると、dw++ がエラーになります<ref>[https://paiza.io/projects/idW8CifxFGXZ5bG1sdRdWA?language=c static_cast を使えば]、<syntaxhighlight lang="cpp" inline>dw = static_cast<Day_of_the_Week>(dw + 1)</syntaxhighlight>コンパイルできますが、<code>static_cast</code>は、コンパイラーの型チェックを骨抜きにしてしまうので、乱用は避けましょう(operator ++ をオーバーロードするにしても、その中で static_cast が必要)。</ref>。
===タグ===
ISO/IEC 9899:2011(通称 C11)の §6.7.2.3 ''Tags''(タグ)を抄訳/引用します<ref name="jtc1-sc22-wg14-n1570-6.7.2.3">{{cite book
576 ⟶ 607行目:
== 型修飾子 ==
型修飾子( ''Type qualifiers'' )は次のいずれかの組み合わせです
| url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
| title = N1570 Committee Draft — April 12, 2011 ISO/IEC 9899:201x
| page=121, §6.7.3 ''Type qualifiers''
| publisher = ISO/IEC}}</ref>。
{|class="wikitable"
|+ 型修飾子の一覧
|-
!型修飾子!!意味
595 ⟶ 632行目:
=== const ===
const修飾型をもったオブジェクトは初期化以外で変更不可能となります<ref name="jtc1-sc22-wg14-n1570-6.7.3"/>。
つまり、そのオブジェクトを
| url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
| title = N1570 Committee Draft — April 12, 2011 ISO/IEC 9899:201x
| page=130, §6.7.6.1 ''Pointer declarators''
| publisher = ISO/IEC}}</ref>、言語処理系に畳み込みなどの最適化の機会を与えます。
const修飾型をもったオブジェクトは初期化が必須になります。
例、次の宣言のペアは、「不変値への可変ポインタ」と「可変値への不変ポインタ」の違いを示しています。
:<syntaxhighlight lang="C" line>
int *const constant̲ptr;
</syntaxhighlight>
<var>ptr_to_constant</var> が指すオブジェクトの内容は、そのポインタを介して変更してはならないが、<var>ptr_to_constant</var> 自体は別のオブジェクトを指すように変更することができる。
同様に、<var>constant_ptr</var> が指すintの内容は変更されても構いませんが、<var>constant_ptr</var> 自体は常に同じ場所を指すものとします。
;[https://paiza.io/projects/YJ3KIZyACeAfTbJzglHoyA?language=c constの誤った使用例]:<syntaxhighlight lang="C">
int main(void) {
const int i = 1;
i++; // iの値は変更不可能なので、この箇所でコンパイルエラーとなります。
}
</syntaxhighlight>
;コンパイル結果:<syntaxhighlight lang="bash">
Main.c:3:4: error: cannot assign to variable 'i' with const-qualified type 'const int'
i++; // iの値は変更不可能なので、この箇所でコンパイルエラーとなります。
~^
Main.c:2:13: note: variable 'i' declared const here
const int i = 1;
~~~~~~~~~~^~~~~
1 error generated.
</syntaxhighlight>
632 ⟶ 687行目:
== _Atomic ==
原子型指定子( ''Atomic type specifier''s )は、スレッドとともにC11で導入されました<ref name="jtc1-sc22-wg14-n1570-6.7.2.3">{{cite book
| url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
| title = N1570 Committee Draft — April 12, 2011 ISO/IEC 9899:201x
| page=121, 6.7.2.4 ''Atomic type specifiers''
| publisher = ISO/IEC}}</ref>。
; 構文
:; atomic-type-specifier:
645 ⟶ 702行目:
: キーワード_Atomicの直後に左括弧がある場合は、型修飾子ではなく、(型名を持つ)型指定子として解釈されます。
<!-- ライブラリ関数の引数に過ぎないので、『データ型と変数の高度な話題』にはそぐわない。
== フォーマット指定子 ==
:※ 厳密には、型の話題とは違うのですが、この単元を間借りします。
787 ⟶ 845行目:
フォーマット指定を「%.7s」と指定しているので、半角スペースも含めて最初から7文字目までが表示されています。
-->
== 脚註 ==
<references/>
|