「C言語/データ型と変数の高度な話題」の版間の差分

削除された内容 追加された内容
Ef3 (トーク | 投稿記録)
タグ: 2017年版ソースエディター
Ef3 (トーク | 投稿記録)
cleanup
タグ: 2017年版ソースエディター
1 行
ここでは、ISO/IEC 9899:2011(通称 C11)の ''6.7 Declarations'' で定義されている構文と意味を読み解き、データ型と変数への理解を深めようと思います。
 
== 宣言 ==
宣言(Declarations)
 
== 記憶域クラス指定子 ==
記憶域クラス指定子( ''storage-class specifiers'' )次の6種類あり、いずれもキーワードある。す<ref name="jtc1-sc22-wg14-n1570-6.7.1">{{cite book
| url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
{|class="wikitable"
| title = N1570 Committee Draft — April 12, 2011 ISO/IEC 9899:201x
|+ C言語の記憶域クラス指定子
| 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
|[[#typedef|typedef]] || データ型に別名を付ける。
| url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
|-
| title = N1570 Committee Draft — April 12, 2011 ISO/IEC 9899:201x
|[[#extern|extern]] || 複数のソースファイルで、1つの識別子を共有する。
| page=109, §6.7.2.2 ''Storage-class specifiers''
|-
| publisher = ISO/IEC}}</ref>。
|rowspan="2"|[[#static|static]] || (ファイル有効範囲)他のソースファイルから隠ぺいする。
 
|-
<blockquote class="toccolours" cite="http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf">
<!--||--> | (ブロック有効範囲)前の値が保存されているようにする。
;構文
|-
:1
| [[#auto|auto]] || ふつうは省略する。
:; ''{{Anchor|storage-class-specifier}}''
|-
:: '''[[#typedef|typedef]]'''
| [[#register|register]] || レジスタを使う。
:: '''[[#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;
{
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種類があります。
2回以上宣言された識別子が同じオブジェクト(変数や配列など)または関数を参照することを、結合と呼ぶ。
結合には、外部結合、内部結合、および無結合の3種類がある。
外部結合とは、プログラムが複数のソースファイルからなる場合、それら複数のソースファイルで、1つの識別子の各々の宣言が、同じオブジェクトまたは関数を表すことである。
 
内部結合とは、1つのソースファプログラム全体を構成する翻訳単位やラブラリの集合の中で1つ外部結合された特定の識別子の各々の宣言それぞれ同じオブジェクトまたは関数を意味しまことである
無結合とは、識別子の各々の宣言が、それぞれ別々の実体を表すことである。
記憶域クラス指定子を伴わず宣言されたファイル有効範囲のオブジェクトの識別子は外部結合である。
記憶域クラス指定子externを伴わず宣言されたブロック有効範囲のオブジェクトの識別子は無結合である。
記憶域クラス指定子をもたない関数の識別子の宣言は、記憶域クラス指定子externを伴って宣言されたものとする。
<ref name="識別子の結合">『JISX3010:2003』p.23「6.2.2 識別子の結合」</ref>
 
1つの翻訳単位( ''translation-unit'' )の中で、内部結合を持つ識別子の各宣言は、同じオブジェクトまたは関数を表します。無結合な識別子の各宣言は一意の実体を表します。
記憶域クラス指定子externを伴って宣言された識別子は、
 
以前の宣言において内部結合または外部結合が指定されているならば、以前の宣言と同じ結合をもち、
オブジェクトまたは関数のファイルスコープ識別子の宣言に、記憶域クラス指定子 static が含まれている場合、その識別子は内部リンクを持ちます。
以前の宣言がない場合、または以前の宣言が無結合である場合、外部結合をもつ。
 
<ref name="識別子の結合"/>
識別子の事前宣言が可視化されているスコープにおいて、記憶域クラス指定子 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);
i=1;
function();
printf("最初のiの値は%d。\n", i);
printf("function関数を呼び出した後のiの値は%d。\n", i);
function();
printf("function関数を呼び出した後のiの値は%d。\n", i);
}
 
// file2.c
static int i; // file1.cからは見えない。
 
static void function() // file1.cからは見えない。
{
i = i=2;
}
</syntaxhighlight>
139 ⟶ 172行目:
#include <stdio.h>
 
void function() {
static int i = 0; // staticを使うことで前の値が保持される。
{
++i;
static int i=0;//staticを使うことで前の値が保持される。
printf("iの値は%d。\n", i);
++i;
printf("iの値は%d。\n", i);
}
 
int main(void) {
function();
{
function();
function();
function();
}
</syntaxhighlight>
174 ⟶ 205行目:
//例 autoの使用例
 
int main(void) {
auto int i;//「int i;」と書いても同じである。
{
auto int i;//「int i;」と書いても同じである。
}
</syntaxhighlight>
459 ⟶ 489行目:
 
=== enum ===
列挙体( enumeration )は、名前の付いた整数定数値( ''integer constant values'' )の集合で構成されています。それぞれの列挙は、異なる列挙型( enumerated type )を構成します<ref name="jtc1-sc22-wg14-n1570-6.2.5">{{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
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'' )は次のいずれかの組み合わせです<ref name="jtc1-sc22-wg14-n1570-6.7.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.3 ''Type qualifiers''
| publisher = ISO/IEC}}</ref>。
 
{|class="wikitable"
|+ 型修飾子の一覧
|-
!型修飾子!!意味
595 ⟶ 632行目:
 
=== const ===
const修飾型をもったオブジェクトは初期化以外で変更不可能となります<ref name="jtc1-sc22-wg14-n1570-6.7.3"/>
つまり、そのオブジェクトを定数不変値( ''constant value'' )として扱うことができ、プログラム中で誤って変更するのを防ぐことができます。<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
<ref name="型修飾子">『JISX3010:2003』p.79「6.7.3 型修飾子」</ref>
| 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">
//例 constの誤った使用例
//※このプログラムはコンパイルエラーとなります。
 
例、次の宣言のペアは、「不変値への可変ポインタ」と「可変値への不変ポインタ」の違いを示しています。
int main(void)
:<syntaxhighlight lang="C" line>
{
const int i=1*ptr̲to̲constant;
int *const constant̲ptr;
++i;//iの値は変更不可能。
</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
C11 6.7.2.4 Atomic type specifiers
| url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
 
| title = N1570 Committee Draft — April 12, 2011 ISO/IEC 9899:201x
6.7.2.4 原子型指定子
| page=121, 6.7.2.4 ''Atomic type specifiers''
| publisher = ISO/IEC}}</ref>。
; 構文
:; atomic-type-specifier:
645 ⟶ 702行目:
: キーワード_Atomicの直後に左括弧がある場合は、型修飾子ではなく、(型名を持つ)型指定子として解釈されます。
 
<!-- ライブラリ関数の引数に過ぎないので、『データ型と変数の高度な話題』にはそぐわない。
== フォーマット指定子 ==
:※ 厳密には、型の話題とは違うのですが、この単元を間借りします。
787 ⟶ 845行目:
 
フォーマット指定を「%.7s」と指定しているので、半角スペースも含めて最初から7文字目までが表示されています。
-->
== 脚註 ==
<references/>