C言語/データ型と変数の高度な話題

ここでは、ISO/IEC 9899:2017(通称 C17)の 6.7 Declarations で定義されている構文と意味を読み解き、データー型と変数への理解を深めようと思います。

宣言編集

N2176 C17 ballot ISO/IEC 9899:2017 §6.7 Declarations:宣言[1].

形式
declaration
declaration-specifiers init-declarator-listopt ;
static_assert-declaration
declaration-specifiers
storage-class-specifier declaration-specifiersopt
type-specifier declaration-specifiersopt
type-qualifier declaration-specifiersopt
function-specifier declaration-specifiersopt
alignment-specifier declaration-specifiersopt
init-declarator-list
init-declarator
init-declarator-list , init-declarator
init-declarator
declarator
declarator = initializer

記憶域クラス指定子編集

記憶域クラス指定子( storage-class specifiers )は6種類あり、いずれもキーワードです[2]

ISO/IEC 9899:2017(通称 C17)の §6.11.5 Storage-class specifiers(記憶域クラス指定子)を抄訳/引用します[2]

構文
1
storage-class-specifier
typedef
extern
static
_Thread_local
auto
register
制約事項
2 1つの宣言の中では、最大で1つの記憶域クラス指定子を与えることができます。ただし、_Thread_local は static または extern と共に使用することができます。
3 ブロックスコープを持つオブジェクトの宣言では、宣言指定子に _Thread_local が含まれる場合、 static または extern も含まれなければなりません。オブジェクトのいずれかの宣言に _Thread_local が含まれている場合、そのオブジェクトのすべての宣言に含まれていなければなりません。
4 _Thread_local は関数宣言の宣言指定子には含まれません。
セマンティクス
5 typedef 指定子は、構文上の利便性のためだけに 記憶域クラス指定子 と呼ばれていますが、これについては 6.7.8 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).

typedef編集

typedefとは、データー型に型定義名をつけるためのキーワードである。

形式
typedef データー型 型定義名;
//例 typedefの使用例

typedef unsigned int UINT; // unsigned int型にUINTという別名をつける。

int main(void) {
  UINT value; 
}

typedefを使うことで、マルチプラットホームにおいて可搬性を高めたり (データー型を変更したい場合、typedefの部分を変更するだけで、すべてのデーター型を変更できる。)、 データー型を隠蔽し、カプセル化を向上できる。

extern編集

externを使うことで、複数のソースファイルで、1つの識別子を共有することができる。

externの使用例
myheader.h
#ifndef H_MYHEADER
#define H_MYHEADER
extern int i;               //外部結合をもつ。
extern void function(void); //外部結合をもつ。このexternは省略可能。

#endif
file1.c
#include <stdio.h>
#include "myheader.h"

int main(void) {
  i = 1;
  printf("最初のiの値は%d。\n", i);
  function();
  printf("function関数を呼び出した後のiの値は%d。\n", i);
}
file2.c
#include "myheader.h"

int i;

void function(void) { i = 2; }
実行結果
% cc file1.c file2.c -o prog
% ./prog
最初のiの値は1。
function関数を呼び出した後のiの値は2。

上の例では、myheader.h で外部参照宣言を一括して行い、定義元の file2.c でも myheader.h をインクルードしています。 この事により、宣言と定義で方が食い違っていないことを言語処理系が検査する機会を与えています。

extern を、.h でなく .c で使う時は、宣言と定義の食い違いのリスクを負っていることを意識すべきです。


異なるスコープで宣言された識別子や、同じスコープで2回以上宣言された識別子は、結合(Linkages[3])と呼ばれるプロセスによって、同じオブジェクトや関数を参照するようにすることができます[4][4]

結合には、外部結合( external linkage )、内部結合( internal linkage )、および無結合( none linkage )の3種類があります。

プログラム全体を構成する翻訳単位やライブラリの集合の中では、外部結合された特定の識別子の宣言は、それぞれ同じオブジェクトや関数を意味します。

1つの翻訳単位( translation-unit )の中で、内部結合を持つ識別子の各宣言は、同じオブジェクトまたは関数を表します。無結合な識別子の各宣言は一意の実体を表します。

オブジェクトまたは関数のファイルスコープ識別子の宣言に、記憶域クラス指定子 static が含まれている場合、その識別子は内部リンクを持ちます。

識別子の事前宣言が可視化されているスコープにおいて、記憶域クラス指定子 extern で宣言された識別子について、事前宣言が内部または外部結合を指定している場合、後の宣言における識別子の結合は、事前宣言で指定された結合と同じである。先行宣言が見えない場合、または先行宣言が結合を指定していない場合、その識別子は外部結合を持つ。

関数の識別子の宣言に記憶域クラス指定子がない場合、その結合は、記憶域クラス指定子 extern で宣言された場合と全く同じように決定されます。 オブジェクトの識別子の宣言にファイルスコープがあり、記憶域クラス指定子がない場合、そのリンク先は外部となります。

オブジェクトまたは関数以外と宣言された識別子、関数のパラメータと宣言された識別子、記憶域クラス指定子 extern を持たずに宣言されたオブジェクトのブロックスコープ識別子は、結合されません。

1つの翻訳単位の中で、同じ識別子が内部リンクと外部リンクの両方で現れた場合、その動作は未定義です。


関数宣言に記憶域クラス指定子 static を含めることができるのは、ファイルスコープにある場合のみです[5]

static編集

ファイル有効範囲( file scope )をもつオブジェクト(変数や配列など)や関数を記憶域クラス指定子 static を伴って宣言することで、そのオブジェクトや関数を内部結合とし他の翻訳単位から参照できなくすることができます。 また、ブロック有効範囲のオブジェクトを記憶域クラス指定子 static を伴って宣言することで静的記憶域期間(static storage duration)をもたせることが出来、その生存期間はプログラム実行の全体になります。その値はプログラム開始処理の前に1回だけ初期化します。

//例 staticの使用例1
//※このプログラムはコンパイルエラーとなる。

// file1.c
#include <stdio.h>

extern int i;
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 = 2;
}
//例 staticの使用例2

#include <stdio.h>

void function() {
  static int i = 0; // staticを使うことで前の値が保持される。
  ++i;
  printf("iの値は%d。\n", i);
}

int main(void) {
  function();
  function();
  function();
}

auto編集

autoはふつう省略する。

//例 autoの使用例

int main(void) {
    auto int i;//「int i;」と書いても同じである。
}

記憶域クラス指定子autoを伴って宣言されたブロック有効範囲の識別子は、自動記憶域期間をもつ。 [6]

register編集

registerを使うことで、レジスターを使う。

registerの使用例
#include <stdio.h>

int main(void) {
  register int i, sum = 0;
  for (i = 1; i <= 100; i++)
    sum += i;
  printf("1から100までの総和は%d。\n", sum);
}

記憶域クラス指定子registerを用いて宣言されたオブジェクトは、 そのオブジェクトへのアクセスを可能な限り高速にすべきであることを示唆する。 この示唆が効果を持つ程度は、処理系定義とする。 また、registerを伴って宣言されたオブジェクトのどの部分のアドレスも、計算することはできない(記憶域クラス指定子registerを伴って宣言した変数には、アドレス演算子( & )を適用する事はできない)[2]

変数は一般にメモリー上に確保されるが、メモリーへのアクセスは比較的低速である。 一方、レジスターはCPU内部にあり、レジスターへのアクセスは比較的高速である。

なお、記憶域クラス指定子registerを用いたからといって必ず高速になるとは限らず、 また、用いなくともコンパイラーは自動的に生成コードを最適化するため低速になるとは限らない。

型指定子編集

N2176 C17 ballot ISO/IEC 9899:2017 §6.7.2 Type specifiers:型指定子[7]

構文
type-specifier
void
char
short
int
long
float
double
signed
unsigned
_Bool
_Complex
atomic-type-specifier
struct-or-union-specifier
enum-specifier
typedef-name
制約事項
少なくとも1つの型指定子は、各宣言の宣言指定子と、各構造体の宣言と型名の指定子修飾子リストで与えられなければならない。各構造体の宣言と型名の指定子修飾子リストで与えなければならない。型指定子の各リストは型指定子の各リストは、以下の多項式のうちの1つでなければならない(各項目に複数の多項式がある場合は、カンマで区切る)。型指定子はどのような順序でもよく、他の宣言型指定子と混在してもよい。

固定幅の整数型編集

C99では、プログラムの移植性を高めるために、いくつかの新しい整数型が定義されています[8]。 すでに利用可能な基本的な整数型では、実際のサイズが実装によって定義され。システムによって異なる可能性があるため、(移植性を確保する上で)不十分であると考えられました。 新しい型は、ハードウェアが通常いくつかの型しかサポートしておらず、そのサポートが環境によって異なる組み込み環境において特に有用です。すべての新しい型は、<inttypes.h> ヘッダー(及び、<stdint.h> ヘッダー)で定義されています。 型は、以下のカテゴリーに分類されます。

正確な幅の整数型
すべての実装で同じビット数 n を持つことが保証されている整数型(実装で利用可能な場合にのみ含まれます)。
最小幅の整数型
少なくとも指定されたビット数 n を持つ、実装で利用可能な最小の型であることが保証されています。少なくとも N=8,16,32,64 で指定されていることが保証されます。
最速の整数型
少なくとも指定されたビット数 n を持つ,実装で利用可能な最速の整数型であることが保証されています。少なくとも N=8,16,32,64 で指定されていることが保証されます。
ポインター整数型
ポインター保持できることが保証されている整数型(実装で利用可能な場合にのみ含まれます)。
最大幅の整数型
実装上最大の整数型であることが保証されている整数型。

以下の表は、型と実装の詳細を取得するためのインターフェースをまとめたものです(nはビット数を意味します)。

型と実装の詳細を取得するためのインターフェース
型カテゴリ 符号付き型 符号なし型
最小値 最大値 最小値 最大値
正確な幅 intn_t INTn_MIN INTn_MAX uintn_t 0 UINTn_MAX
最小幅 int_leastn_t INT_LEASTn_MIN INT_LEASTn_MAX UINT_LEASTN_T 0 UINT_LEASTN_MAX
最速 int_fastn_t INT_FASTn_MIN INT_FASTn_MAX UINT_FASTn_T 0 UINT_FASTn_MAX
ポインター intptr_t・ INTPTR_MIN INTPTR_MAX</コード UINTPTR_T 0 UINTPTR_MAX
最大幅 intmax_t INTMAX_MIN INTMAX_MAX</コード UINTMAX_T 0 UINTMAX_MAX

union編集

enum編集

列挙体( enumeration )は、名前の付いた整数定数値( integer constant values )の集合で構成されています。それぞれの列挙は、異なる列挙型( enumerated type )を構成します[9]

ISO/IEC 9899:2017(通称 C17)の §6.7.2.2 Enumeration specifiers(列挙型指定子)を抄訳/引用します[10]

構文
enum-specifier
enum identifieropt { enumerator-list }
enum identifieropt { enumerator-list , }
enum identifier
enumerator-list
enumerator
enumerator-list , enumerator
enumerator
enumeration-constant
enumeration-constant = constant-expression
enumeration-constant[11]
identifier
制約条件
列挙定数の値を定義する式は、intとして表現可能な値を持つ整数定数式でなければならない。
セマンティクス
列挙子リスト( enumerator-list )の識別子( identifier )は、int型の定数として宣言されており、許容される範囲内であればどこにでも現れることができます。
=を伴った持つ列挙子( enumerator )は、その列挙定数( enumeration-constant )を定数式( constant-expression )の値として定義します。
最初の列挙子に=がない場合、その列挙定数の値は 0 です。
=がない後続の各列挙子は、前の列挙定数の値に1を加えた定数式の値としてその列挙定数を定義します。
(=付きの列挙子を使用すると、同じ列挙内の他の値と重複する値を持つ列挙定数が生成されることがあります)。
列挙体の列挙子は、そのメンバーーとしても知られています。

構文中で、列挙子リスト( enumerator-list )の後ろに , がないケースとあるケースの両方が書かれているので、どちらの書き方も許されます。

列挙体の使用例
#include <stdio.h>

enum Day_of_the_Week {
  dwSUNDAY,
  dwMONDAY,
  dwTUESDAY,
  dwWEDNESDAY,
  dwTHURSDAY,
  dwFRIDAY,
  dwSATURDAY,
  nDay_of_the_Week,
};

int main(void) {
  const char *names[nDay_of_the_Week] = { // 順位を使った配列変数の初期化
      [dwSUNDAY] = "日曜日", 
      [dwMONDAY] = "月曜日",
      [dwTUESDAY] = "火曜日",
      [dwWEDNESDAY] = "水曜日",
      [dwTHURSDAY] = "木曜日",
      [dwFRIDAY] = "金曜日",
      [dwSATURDAY] = "土曜日",
  };
  for (enum Day_of_the_Week dw = dwSUNDAY; dw < nDay_of_the_Week; dw++) {
    printf("%i: %s\n", dw, names[dw]);
  }
}
実行結果
0: 日曜日
1: 月曜日
2: 火曜日
3: 水曜日
4: 木曜日
5: 金曜日
6: 土曜日

列挙子(enumで定義する定数)は、同じスコープでは別の列挙体でも同じ名前空間を使うので、衝突を避けるため列挙子に列挙体のニーモニックを前置するなどの対策が必要です。

ここでは、列挙子の名前が dw で始まるのはタグ Day_of_the_Week の列挙子であることを示し、最後の列挙し nDay_of_the_Week 列挙子の総数で Number of Day_of_the_Week のつもり。列挙型の変数、dw はもちろん enum Day_of_the_Week 型であることを示しています。

この命名ルールは一例ですが、実際のプログラミングでは多くの識別子の名前を考える必要があるので、一貫性のある命名ルールを作ってコードを書くことで可読性・保守性が向上します。

C++のコードとしてコンパイルすると、dw++ がエラーになります[12]

タグ編集

ISO/IEC 9899:2017(通称 C17)の §6.7.2.3 Tags(タグ)を抄訳/引用します[13]

制約事項
特定の型は、その内容が最大で一度だけ定義されなければならない。
以下の形式の型指定子は
enum identifier
それが指定する型が完成した後にのみ現れるものとする。
セマンティクス
同じスコープで同じタグを使用する structunion、または enum の宣言はすべて同じスコープを持ち、同じタグを使用する structunionenum のすべての宣言は、同じ型を宣言する。
型は、内容を定義するリストの閉じ括弧までは不完全で、それ以降は完全な型となります。
structunionenum の宣言のうち、スコープが異なるものや、異なるタグを使用しているものが2つあると、異なる型を宣言することになります。
異なるスコープまたは異なるタグを使用する2つの structunionenum の宣言は、異なる型を宣言します。
structunion、または enum の各宣言でタグを含まない structunionenum の宣言は、それぞれ個別の型を宣言します。
次のような形式の型指定子
struct-or-union identifieropt { struct-declaration-list }。
struct-or-union identifieropt { member-declaration-list }
または
enum identifier { enumerator-list }
の形式の型指定子。または
enum identifier { enumerator-list , }
は、 structunion、または enum を宣言します。
リストは、struct のコンテンツ、union のコンテンツ、または enum のコンテンツを定義します。
識別子が提供されている場合、型指定子は識別子をその型のタグであるとも宣言する。

型修飾子編集

型修飾子( Type qualifiers )は次のいずれかの組み合わせです[14].。

構文
type-qualifier
const
restrict
volatile
_Atomic
制約事項
参照される型がオブジェクト型であるポインター型以外の型は、制限修飾してはならない。
実装がアトミック型をサポートしていない場合、_Atomic修飾子は使用してはならない(6.10.8.3 Conditional feature macros 参照)。
_Atomic修飾子によって変更される型は、配列型や関数型であってはならない。
型修飾子の一覧
型修飾子 意味
const 初期化以降の代入を禁止します。
restrict aliasが存在しないと仮定した最適化を許します。
volatile 未知の方法でインスタンスが書き換えられうる事を示し、最適化を抑制します。
_Atomic 不可分操作を提供します。

const編集

const修飾型をもったオブジェクトは初期化以外で変更不可能となります[14]。 つまり、そのオブジェクトを不変値( constant value )として扱うことができ[15]、言語処理系に畳み込みなどの最適化の機会を与えます。 const修飾型をもったオブジェクトは初期化が必須になります。


例、次の宣言のペアは、「不変値への可変ポインター」と「可変値への不変ポインター」の違いを示しています。

const int *ptr̲to̲constant;   
int *const constant̲ptr;

ptr_to_constant が指すオブジェクトの内容は、そのポインターを介して変更してはならないが、ptr_to_constant 自体は別のオブジェクトを指すように変更することができる。 同様に、constant_ptr が指すintの内容は変更されても構いませんが、constant_ptr 自体は常に同じ場所を指すものとします。

constの誤った使用例
int main(void) {
  const int i = 1;
  i++; // iの値は変更不可能なので、この箇所でコンパイルエラーとなります。
}
コンパイル結果
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.

restrict編集

restrictとは、ポインターに用いる型修飾子であり、そのポインターが指す先を、同一関数・ブロック内の別のポインターが指さないという情報をコンパイラーに伝え、コンパイラーの最適化を促進することができます。 restrict型修飾子を付けても付けなくても、目に見える動作は変わりません。 [16]

restrictの例として、標準ライブラリstring.hのmemcpy関数とmemmove関数が挙げられる。

#include<string.h>

void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
void *memmove(void *s1, const void *s2, size_t n);

これら2つの関数は、どちらもs2をs1にn文字コピーしますが、memcpyは領域の重なり合わないオブジェクト間でコピーする必要があり、 そのためrestrict型修飾子を用いることができます。

volatile編集

volatileとは、変数がコンパイラーに未知の方法で変更され、又はその他の未知の副作用を持つことをコンパイラーに伝え、コンパイラーの最適化を抑制する型修飾子です。 volatileで型修飾する動機として:

  • メモリーマップドI/O
  • 異なるスレッド間で共有されるオブジェクト
  • 非同期シグナルのハンドラーによりモディファイされるオブジェクト
  • setjmp が呼ばれてから longjmp が呼ばれるまでに変更され得るスタック上のオブジェクト

などがあります。 これらのオブジェクトの参照が共通部分式削除定数畳み込みなどの言語処理系による畳込みでループの最初に一度だけ評価されたり、あるいは全く評価されなくなることを避ける狙いがあります。

ただし、

  • volatileで型修飾されたオブジェクトとそうでないオブジェクトとの間の実行順序は保証されない
  • volatileで型修飾されたオブジェクトへのアクセスが分割されないことは保証されない(アトミックであることは保証されない)

など、volatileで型修飾する動機になったニーズを完全に充足しているかは検討の余地があります。

_Atomic編集

原子型指定子( Atomic type specifiers )は、スレッドとともにC11で導入されました[17]

構文
atomic-type-specifier
_Atomic ( type-name )
制約事項
実装が原子型をサポートしていない場合、原子型指定子( Atomic type specifiers )を使用してはならない(6.10.8.3 Conditional feature macros 参照)。
原子型指定子の型名は、配列型、関数型、原子型、または修飾型を参照してはならない。
セマンティクス
原子型に関連するプロパティは、lvalues(左辺値)である式に対してのみ意味を持ちます。
キーワード_Atomicの直後に左括弧がある場合は、型修飾子ではなく、(型名を持つ)型指定子として解釈されます。

脚註編集

  1. ^ N2176 C17 ballot ISO/IEC 9899:2017. ISO/IEC JTC1/SC22/WG14. p. 78, §6.7 Declarations. オリジナルの2018-12-30時点によるアーカイブ。. https://web.archive.org/web/20181230041359/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf. 
  2. ^ 2.0 2.1 2.2 N2176 C17 ballot ISO/IEC 9899:2017. ISO/IEC JTC1/SC22/WG14. p. 130, §6.11.5 Storage-class specifiers. オリジナルの2018-12-30時点によるアーカイブ。. https://web.archive.org/web/20181230041359/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf. 
  3. ^ 演算子の結合性は associativity of operators。JISは、2つの術語に1つの訳を与えてしまっている
  4. ^ 4.0 4.1 N2176 C17 ballot ISO/IEC 9899:2017. ISO/IEC JTC1/SC22/WG14. p. 29, §6.2.2 Linkages of identifiers. オリジナルの2018-12-30時点によるアーカイブ。. https://web.archive.org/web/20181230041359/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf. 
  5. ^ 6.7.10 storage-class specifiers
  6. ^ 引用エラー: 無効な <ref> タグです。「オブジェクトの記憶域期間」という名前の注釈に対するテキストが指定されていません
  7. ^ N2176 C17 ballot ISO/IEC 9899:2017. ISO/IEC JTC1/SC22/WG14. p. 79, §6.7.2 Type specifiers. オリジナルの2018-12-30時点によるアーカイブ。. https://web.archive.org/web/20181230041359/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf. 
  8. ^ 引用エラー: 無効な <ref> タグです。「c99」という名前の注釈に対するテキストが指定されていません
  9. ^ N2176 C17 ballot ISO/IEC 9899:2017. ISO/IEC JTC1/SC22/WG14. p. 31, §6.2.5 Types. オリジナルの2018-12-30時点によるアーカイブ。. https://web.archive.org/web/20181230041359/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf. 
  10. ^ N2176 C17 ballot ISO/IEC 9899:2017. ISO/IEC JTC1/SC22/WG14. p. 84, §6.7.2.2 Enumeration specifiers. オリジナルの2018-12-30時点によるアーカイブ。. https://web.archive.org/web/20181230041359/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf. 
  11. ^ §6.4.4.3 Enumeration constants
  12. ^ static_cast を使えばdw = static_cast<Day_of_the_Week>(dw + 1)コンパイルできますが、static_castは、コンパイラーの型チェックを骨抜きにしてしまうので、乱用は避けましょう(operator ++ をオーバーロードするにしても、その中で static_cast が必要)。
  13. ^ N2176 C17 ballot ISO/IEC 9899:2017. ISO/IEC JTC1/SC22/WG14. p. 85, §6.7.2.3 Tags. オリジナルの2018-12-30時点によるアーカイブ。. https://web.archive.org/web/20181230041359/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf. 
  14. ^ 14.0 14.1 N2176 C17 ballot ISO/IEC 9899:2017. ISO/IEC JTC1/SC22/WG14. p. 87, §6.7.3 Type qualifiers. オリジナルの2018-12-30時点によるアーカイブ。. https://web.archive.org/web/20181230041359/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf. 
  15. ^ N2176 C17 ballot ISO/IEC 9899:2017. ISO/IEC JTC1/SC22/WG14. p. 93, §6.7.6.1 Pointer declarators. オリジナルの2018-12-30時点によるアーカイブ。. https://web.archive.org/web/20181230041359/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf. 
  16. ^ 引用エラー: 無効な <ref> タグです。「型修飾子」という名前の注釈に対するテキストが指定されていません
  17. ^ N2176 C17 ballot ISO/IEC 9899:2017. ISO/IEC JTC1/SC22/WG14. p. 87, §6.7.2.4 Atomic type specifiers. オリジナルの2018-12-30時点によるアーカイブ。. https://web.archive.org/web/20181230041359/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf. 

参考文献編集