「静的定数」対「#define」対「列挙型」


585

Cの以下のステートメントの中でどちらを使用するのが良いですか?

static const int var = 5;

または

#define var 5

または

enum { var = 5 };

35
興味深いことに、これはstackoverflow.com/questions/1637332/static-const-vs-defineほぼ同じ質問です。唯一の違いは、その質問はC ++に関するものであり、これはCに関するものです。私の答えはC ++固有だったので、私はそれらを同一ではないと言っていますが、他の人は同意しないかもしれません。
TED

53
まったく同じではありません。互換性の理由から、C ++がC構文を許可する領域はたくさんあります。そのような場合、「Xを行うための最良の方法は何か」などの質問は、C ++では異なる答えになります。たとえば、オブジェクトの初期化。
MSalters 2009年


これは意見に基づいていないのですか?彼らはそれぞれ異なる目的を持っています
サムハマミー

1
@RobertSsupportsMonicaCellio、はい。親密にしていただきありがとうございます
ビジェイ

回答:


690

それはあなたが値を必要とするものに依存します。あなた(そして今までのところ全員)は3番目の選択肢を省略しました:

  1. static const int var = 5;
  2. #define var 5
  3. enum { var = 5 };

名前の選択に関する問題を無視すると、次のようになります。

  • ポインタを渡す必要がある場合は、(1)を使用する必要があります。
  • (2)は明らかにオプションであるため、ポインタを渡す必要はありません。
  • (1)と(3)の両方がデバッガーのシンボルテーブルにシンボルを持っている-これにより、デバッグが容易になります。(2)には記号がない可能性が高く、それが何であるか疑問に思います。
  • (1)グローバルスコープでは配列の次元として使用できません。(2)と(3)の両方ができます。
  • (1)関数スコープでは静的配列の次元として使用できません。(2)と(3)の両方ができます。
  • C99では、これらすべてをローカル配列に使用できます。技術的には、(1)を使用すると、VLA(可変長配列)を使用することになりますが、 'var'によって参照される次元は、もちろんサイズ5に固定されます。
  • (1)switchステートメントのような場所では使用できません。(2)と(3)の両方ができます。
  • (1)静的変数の初期化には使用できません。(2)と(3)の両方ができます。
  • (2)プリプロセッサで使用されているため変更したくないコードを変更できます。(1)と(3)の両方に、このような予期しない副作用はありません。
  • (2)がプリプロセッサーで設定されているかどうかを検出できます。(1)も(3)もそれを許可しません。

したがって、ほとんどのコンテキストでは、選択肢よりも「列挙型」を優先します。それ以外の場合、最初と最後の箇条書きが制御要因となる可能性が高く、両方を同時に満たす必要がある場合は、より深く考える必要があります。

C ++について質問している場合は、毎回オプション(1)(静的const)を使用します。


111
素晴らしいリスト!の1つの欠点enumは、int([C99] 6.7.2.2/3)として実装されることです。Aを#define使用すると、unsignedおよびlong with ULsuffix を指定でき、型を指定constできます。enum通常の型変換で問題が発生する可能性があります。
Gauthier

37
(2)人々は常に型の安全性について文句を言う。なぜ "#define var((int)5)"を使用しないのか理解できません。
Ingo Blackman

6
@RedX:スペースを気にするためには、非常に特殊な環境にいる必要があります。とはいえ、それ自体は余分なスペースenum#define使用しません。値は、データセグメント、ヒープ、スタックのストレージに割り当てられるのではなく、命令の一部としてオブジェクトコードに表示されます。にいくつかのスペースが割り当てられますがstatic const int、アドレスを取得しない場合、コンパイラーはそれを最適化する可能性があります。
Jonathan Leffler、2014

15
enums(およびstatic const)のもう1つの「投票」:変更することはできません。defineすることができ#undefine「D enumとがstatic const与えられた値に固定されています。
Daan Timmer 14

15
@QED:いいえ、ありがとうございます。単純な定数は、括弧の外で安全です。または、かっこ内に5がないと、コンパイルが正当に期待できるプログラムがどのように変更されるかを教えてください。関数スタイルのマクロの引数である場合、または式に演算子が含まれている場合は、かっこを含めなかったとしても、私を非難するのは正しいでしょう。しかし、ここではそうではありません。
Jonathan Leffler、2015年

282

一般的に言って:

static const

スコープを尊重し、タイプセーフであるためです。

私が見ることができる唯一の警告:コマンドラインで変数を定義する可能性がある場合。まだ代替手段があります:

#ifdef VAR // Very bad name, not long enough, too general, etc..
  static int const var = VAR;
#else
  static int const var = 5; // default value
#endif

可能な限り、マクロ/省略記号の代わりに、タイプセーフな代替手段を使用してください。

本当にマクロを使用する必要がある場合(たとえば、__FILE__または__LINE__)、マクロには非常に慎重に名前を付ける ことをお勧めします。その命名規則では、Boostはプロジェクトの名前で始まるすべての大文字を推奨しています(ここではBOOST_ )、ライブラリを閲覧すると、これは(通常)特定の領域(ライブラリ)の名前が続き、次に意味のある名前が続くことに気づくでしょう。

それは一般的に長い名前になります:)


2
同意-プリプロセッサは構文を認識しないため、#defineを使用すると、コードをマングル化する一般的な危険があります。
NeilDurant 2009年

10
#ifdefよりも#ifを使用することをお勧めしますが、それ以外の場合は同意します。+1。
Tim Post

58
これは標準のC ++伝道です。以下の答えは、オプションが実際に何を意味しているのかを説明する上ではるかに明確です。特に、「静的定数」で問題が発生しました。誰かがヘッダーファイルで約2000の「定数」を定義するためにそれを使用しました。次に、このヘッダーファイルは約100個の「.c」および「.cpp」ファイルに含まれていました。=> "consts"の場合は8Mバイト。すごい。はい私はあなたがリンカーを使用して参照されていないconstを削除するかもしれないことを知っていますが、それでもこれはあなたが参照している「consts」をあなたに残します スペースが足りなくなりました。この答えの何が問題になっていますか。
Ingo Blackman

2
@IngoBlackman:優れたコンパイラではstatic、アドレスが取得されたものだけが残るはずです。アドレスが取得された#define場合、orまたはenum(アドレスなし)を使用することはできませんでした。「コンパイル時評価」を廃止できる場合は、extern const代わりに探している可能性があります。
Matthieu M.

15
@Timポスト:#if望ましい超えるかもしれません#ifdefブールフラグのが、この場合には、それはそれは不可能定義することになるだろうvarとして、0コマンドラインから。したがって、この場合は、の正当な値である#ifdef限り、より意味0がありますvar
Maarten

108

特にCでは?Cでの正解は次のとおりです。使用#define(または、適切な場合はenum

constオブジェクトのスコープと型のプロパティを持つことは有益ですが、実際にconstは、Cのオブジェクト(C ++ではなく)は真の定数ではないため、通常、ほとんどの実用的なケースでは役に立ちません。

したがって、Cでは、定数をどのように使用するかによって、選択を決定する必要があります。たとえば、const intオブジェクトをcaseラベルとして使用することはできません(マクロは機能します)。const intオブジェクトをビットフィールド幅として使用することはできません(マクロは機能します)。C89 / 90では、constオブジェクトを使用して配列サイズを指定することはできません(マクロは機能します)。C99でもconst、非VLA配列が必要な場合、オブジェクトを使用して配列サイズを指定することはできません。

これがあなたにとって重要である場合、それはあなたの選択を決定します。ほとんどの場合#define、Cで使用するしかありません。また、C-で真の定数を生成する別の代替方法を忘れないでくださいenum

C ++ではconstオブジェクトは真の定数なので、C ++ではほとんどの場合、constバリアントを使用する方が適切です(staticただし、C ++では明示的にする必要はありません)。


6
"const intオブジェクトをケースラベルとして使用することはできません(マクロは機能します)" --->このステートメントに関して、Cでconst int変数をスイッチでテストしました
john

8
@john:さて、テストしたコードを提供し、特定のコンパイラに名前を付ける必要があります。const intcase-labelsでオブジェクトを使用することは、C言語のすべてのバージョンで違法です。(もちろん、コンパイラーは非標準のC ++のような言語拡張としてそれを自由にサポートします。)
AnT

11
「…… したがって、通常、ほとんどの実際的なケースでは役に立たない。」同意しません。名前を定数式として使用する必要がない限り、これらは完全に役立ちます。Cの「定数」という言葉は、コンパイル時に評価できるものを意味します。const読み取り専用を意味します。const int r = rand();完全に合法です。
キーストンプソン

C ++では、またはのようなコンテナconstexprconst特別に使用する場合と比較して使用する方が良いです。stlarraybitset
Mayukh Sarkar

1
@johnあなたはswitch()ステートメントではなく、ステートメントでテストしたはずですcase。私は☺あまりにもこの1に巻き込まれました
のHi-エンジェル

32

違いstatic constとは、#defineかつての用途メモリーことと、後は保存用のメモリを使用していないです。次に、anのアドレスを渡すことはできませんが、#defineaのアドレスを渡すことはできますstatic const。実際の状況によって異なりますが、この2つから1つを選択する必要があります。どちらもさまざまな状況下で最高です。一方が他方よりも優れているとは思わないでください... :-)

もしそうなら、デニス・リッチー は最高のものを一人で残していたでしょう...ははは... :-)


6
メモリについて言及するための+1、一部の組み込みシステムにはまだそれほど多くはありませんが、おそらく静的constを使用して開始し、必要な場合にのみ#definesに変更します。
fluffyben 2012年

3
私はそれをテストしました。実際、const intは#defineまたはenumと比較して追加のメモリを使用します。組み込みシステムをプログラムするため、追加のメモリ使用量を購入する余裕はありません。したがって、#defineまたはenumの使用に戻ります。
Davide Andrea

2
実際にa constがメモリを使用することは(もう)真実ではありません。GCC(4.5.3およびいくつかの新しいバージョンでテスト済み)const intは、-O3を使用するときに、コード内のを直接リテラルに簡単に最適化します。したがって、低RAM組み込み開発(AVRなど)を行う場合、GCCまたは他の互換コンパイラを使用すれば、C constsを安全に使用できます。私はそれをテストしていませんが、Clangが同じことを行うと期待しています。
Raphael

19

C #defineでははるかに人気があります。これらの値を使用して、配列サイズを宣言できます。次に例を示します。

#define MAXLEN 5

void foo(void) {
   int bar[MAXLEN];
}

ANSI Cではstatic const、私の知る限り、このコンテキストでを使用することはできません。C ++では、これらのケースではマクロを使用しないでください。あなたは書ける

const int maxlen = 5;

void foo() {
   int bar[maxlen];
}

またstatic、内部リンケージはconst既に[C ++のみ]で暗黙指定されているため、除外することもできます。


1
「内部連携」とはどういう意味ですか?私が持つことができるconst int MY_CONSTANT = 5;一つのファイルにし、それにアクセスするextern const int MY_CONSTANT;別に。const「6.2.2:5オブジェクトの識別子の宣言にファイルスコープがあり、ストレージクラスの指定子がない場合、そのリンケージは外部です」というデフォルトの動作の変更に関する標準(少なくともC99)には情報が見つかりませんでした。
Gauthier '19年

@Gauthier:すみません、それについて。「C ++言語では既にconstによって暗示されている」と私は言ったはずです。これはC ++に固有です。
11

道に沿っていくつかの引数を参照してください代わりのトンのためにそれの素敵を@sellibitze 見解真の引数のボーナスがあることになる場合、あなたはそれを得ました!
ポール

1
C99以降、2番目のスニペットは合法です。barVLA(可変長配列)です。コンパイラーは、その長さが一定であるかのようにコードを生成する可能性があります。
キース・トンプソン、

14

constC のもう1つの欠点は、その値を別のの初期化に使用できないことですconst

static int const NUMBER_OF_FINGERS_PER_HAND = 5;
static int const NUMBER_OF_HANDS = 2;

// initializer element is not constant, this does not work.
static int const NUMBER_OF_FINGERS = NUMBER_OF_FINGERS_PER_HAND 
                                     * NUMBER_OF_HANDS;

コンパイラは定数として認識しないため、これでもconstでは機能しません。

static uint8_t const ARRAY_SIZE = 16;
static int8_t const lookup_table[ARRAY_SIZE] = {
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; // ARRAY_SIZE not a constant!

constこれらの場合は、typedを使用できます。それ以外の場合...


5
ゲームには少し遅れましたが、この質問は別の質問で出てきました。static uint8_t const ARRAY_SIZE = 16;特に#define ARRAY_SIZE 256ヘッダーが絡まったウェブの10層に埋め込まれている場合は、突然コンパイルできなくなった理由を追跡するのは少し難しいかもしれません。そのすべての大文字の名前ARRAY_SIZEは問題を求めています。マクロ用にALL_CAPSを予約し、ALL_CAPS形式でないマクロを定義しないでください。
David Hammen、2011

@David:私がフォローする健全なアドバイス。
Gauthier

1
4年後、なぜ「ネスト」できないのかを理解するのに多くの時間を節約してくれましたconst。これはもっと賛成できるかもしれません!
2015年

11

あなたがそれでうまくいくことができるならばstatic const、多くの利点があります。これは、通常のスコープの原則に従い、デバッガーに表示され、変数が従う規則に従います。

ただし、少なくとも元のC標準では、実際には定数ではありません。を使用する場合、宣言として#define var 5書き込むことはできますがint foo[var];、それを行うことはできません(コンパイラ拡張としてを除く)static const int var = 5;。これは、static constバージョンが使用できる場所であればどこでも#defineバージョンを使用できるC ++の場合とは異なります。C99も同様です。

ただし、#define定数に小文字の名前を付けないでください。それは、翻訳単位の終わりまで、その名前の可能な使用を上書きします。マクロ定数は、事実上、独自の名前空間である必要があります。これは、伝統的にすべて大文字であり、おそらくプレフィックスが付いています。


6
残念ながら、これはC99には当てはまりません。constC99ではまだ実際の定数ではありません。constC99では可変長配列をサポートしているため、配列サイズをC99で宣言できます。このため、VLAが許可されている場合にのみ機能します。たとえば、C99でも、を使用しconstてのメンバー配列のサイズを宣言することはできませんstruct
AnT 2009年

C99ではそれができないのは正しいことですが、GCC(4.5.3でテスト済み)const intでは、C ++ constまたはマクロのようにサイズを指定して配列を完全に初期化できます。標準からのGCCのこの偏差に依存するかどうかはもちろんあなたの選択ですが、GCCまたはClang以外のコンパイラの使用が本当に予測できない限り、私は個人的にそれを採用します。後者は同じ機能を備えています(Clangでテスト済み) 3.7)。
Raphael

7

#defineの代わりにconstを使用することが常に望ましいです。これは、constがコンパイラーによって処理され、#defineがプリプロセッサーによって処理されるためです。#define自体が(大まかに言えば)コードの一部ではないようです。

例:

#define PI 3.1416

シンボリック名PIは、コンパイラーには決して表示されません。ソースコードがコンパイラに到達する前に、プリプロセッサによって削除される場合があります。その結果、名前PIがシンボルテーブルに入力されない場合があります。エラーメッセージがPIではなく3.1416を参照している可能性があるため、定数の使用を含むコンパイル中にエラーが発生した場合、これは混乱を招く可能性があります。自分で記述していないヘッダーファイルでPIが定義されている場合、3.1416がどこから来たかはわかりません。

この問題は、シンボリックデバッガでも発生する可能性があります。これも、プログラミングしている名前がシンボルテーブルにない場合があるためです。

解決:

const double PI = 3.1416; //or static const...

6

#define var 5あなたがのようなものがある場合、あなたはトラブルを引き起こしますmystruct.var

例えば、

struct mystruct {
    int var;
};

#define var 5

int main() {
    struct mystruct foo;
    foo.var = 1;
    return 0;
}

プリプロセッサがそれを置き換え、コードはコンパイルされません。このため、従来のコーディングスタイルでは、すべての定数#definesが競合を避けるために大文字を使用することを推奨しています。


6

1つの違いを示すために、簡単なテストプログラムを作成しました。

#include <stdio.h>

enum {ENUM_DEFINED=16};
enum {ENUM_DEFINED=32};

#define DEFINED_DEFINED 16
#define DEFINED_DEFINED 32

int main(int argc, char *argv[]) {

   printf("%d, %d\n", DEFINED_DEFINED, ENUM_DEFINED);

   return(0);
}

これは、次のエラーと警告でコンパイルされます。

main.c:6:7: error: redefinition of enumerator 'ENUM_DEFINED'
enum {ENUM_DEFINED=32};
      ^
main.c:5:7: note: previous definition is here
enum {ENUM_DEFINED=16};
      ^
main.c:9:9: warning: 'DEFINED_DEFINED' macro redefined [-Wmacro-redefined]
#define DEFINED_DEFINED 32
        ^
main.c:8:9: note: previous definition is here
#define DEFINED_DEFINED 16
        ^

定義が警告を与えるとき、列挙はエラーを与えることに注意してください。


4

定義

const int const_value = 5;

常に定数値を定義するわけではありません。一部のコンパイラ(たとえば、tcc 0.9.26)は、「const_value」という名前で識別されるメモリを割り当てるだけです。識別子「const_value」を使用して、このメモリを変更することはできません。ただし、別の識別子を使用してメモリを変更することもできます。

const int const_value = 5;
int *mutable_value = (int*) &const_value;
*mutable_value = 3;
printf("%i", const_value); // The output may be 5 or 3, depending on the compiler.

これは定義を意味します

#define CONST_VALUE 5

いかなる方法でも変更できない定数値を定義する唯一の方法です。


8
ポインターを使用して定数値を変更することは、未定義の動作です。そこに進んでよければ#define、マシンコードを編集して修正することもできます。
ugoren 2013年

あなたは部分的に正しいです。Visual Studio 2012でコードをテストしたところ、印刷されました5。しかし#define、これはプリプロセッサマクロであるため変更できません。バイナリプログラムには存在しません。CONST_VALUE使用されているすべての場所を変更したい場合は、1つずつ変更する必要がありました。
user2229691 2013年

3
@ugoren:と書いて#define CONST 5、を書いて、if (CONST == 5) { do_this(); } else { do_that(); }コンパイラがelseブランチを削除するとします。マシンコードを編集しCONSTて6 に変更することをどのように提案しますか?
キーストンプソン

@KeithThompson、私はそれが簡単かつ確実に実行できるとは言いませんでした。ただそれだけで#defineは十分ではありません。
ugoren 2014

3
@ugoren:私のポイントは、「マシンコードの編集」はaの値を変更する効果を複製する賢明な方法ではないということ#defineです。これを行う唯一の実際の方法は、ソースコードを編集して再コンパイルすることです。
キーストンプソン

4

問題は整数に関するものでしたが、定数の構造または文字列が必要な場合は、#defineと列挙型は役に立たないことに注意してください。これらは通常、ポインタとして関数に渡されます。(文字列の場合は必須です。構造体の場合ははるかに効率的です。)

整数に関しては、メモリが非常に限られている組み込み環境にいる場合、定数が格納されている場所と定数へのアクセスがどのようにコンパイルされるかについて心配する必要があるかもしれません。コンパイラーは実行時に2つのconstを追加しますが、コンパイル時に2つの#defineを追加します。#define定数は、1つ以上のMOV [即時]命令に変換できます。これは、定数がプログラムメモリに効果的に格納されることを意味します。const定数は、データメモリの.constセクションに格納されます。ハーバードアーキテクチャのシステムでは、パフォーマンスとメモリ使用量に違いが生じる可能性がありますが、小さい可能性があります。それらは、内部ループのハードコア最適化にとって重要かもしれません。


3

「これが常に最良」の答えがあるとは思わないでください、しかしマシューが言ったように

static const

タイプセーフです。#defineただし、私の最大の不満は、Visual Studioでデバッグしているときに変数を監視できないことです。シンボルが見つからないというエラーが発生します。


1
「変数を見ることができない」そうですね、変数ではありません。それは変わらない、なぜあなたはそれを見る必要があるのですか?ラベルを検索するだけで、使用されているあらゆる場所を見つけることができます。#defineを監視する必要がある(または必要な場合さえある)のはなぜですか?
マーシャルユーバンクス2018

3

ちなみに、#define適切なスコープを提供しますが、「実際の」定数のように動作するの代替は「列挙型」です。例えば:

enum {number_ten = 10;}

多くの場合、列挙型を定義し、それらの型の変数を作成すると便利です。これが行われると、デバッガは列挙名に従って変数を表示できる場合があります。

ただし、C ++では、列挙型には整数との互換性に制限があります。たとえば、デフォルトでは、それらに対して演算を実行することはできません。私はそれが列挙型の好奇心が強いデフォルトの振る舞いであることがわかりました。「厳格な列挙型」があればよかったのですが、C ++がCと一般的に互換性を持つことを望んでいるので、「列挙型」のデフォルトの動作は整数と交換可能であると思います。


1
Cでは、列挙型定数は常に型intであるので、「列挙型ハック」は他の整数型では使用できません。(列挙は、必ずしも実装定義の整数と互換性がありますintが、必ずしもそうではありませんが、この場合、型は匿名であるため、問題ではありません。)
Keith Thompson

@KeithThompson:上記を書いたので、コンパイラがint列挙型の変数(コンパイラに許可されている)以外の型を割り当て、そのような変数に割り当てようとすると、MISRA-Cが不規則に動作することを読みました独自の列挙のメンバー。標準化委員会が、指定されたセマンティクスで整数型を宣言する移植可能な方法を追加してほしいと思います。 ANYプラットフォームは、かかわらずのcharサイズ、コンパイラがたくさん追加する場合であっても、65536 MODをラップする型を宣言などのことができるようにすべきAND R0,#0xFFFFか、同等の指示を。
スーパーキャット2014

uint16_tもちろん使用できますが、これは列挙型ではありません。ユーザーが指定した列挙型を表すために使用される整数型を指定できるようにするにはいいだろうが、あなたは多くのと同じ効果を得ることができtypedefためuint16_tと、一連の#define個々の値について秒。
キース・トンプソン

1
@KeithThompson:私は、我々はいくつかのプラットフォームで評価することとしている立ち往生し、歴史的な理由を理解する2U < -1L偽真、その他として、私たちは今、いくつかのプラットフォームでは、との比較を実装するという事実で立ち往生しているuint32_tし、int32_t署名などを署名されていないものもありますが、すべてのコンパイラでセマンティクスが一貫している型を含む、Cの上位互換の後継を委員会が定義できなかったという意味ではありません。
スーパーキャット2014

1

単純な違い:

前処理時に、定数はその値に置き換えられます。したがって、逆参照演算子を定義に適用することはできませんが、逆参照演算子を変数に適用することはできます。

ご想像のとおり、defineはstatic constよりも高速です。

たとえば、

#define mymax 100

できませんprintf("address of constant is %p",&mymax);

しかし、

const int mymax_var=100

あなたができるprintf("address of constant is %p",&mymax_var);

より明確にするために、定義は前処理段階でその値に置き換えられるため、プログラムには変数が格納されていません。定義が使用されたプログラムのテキストセグメントからのコードだけがあります。

ただし、静的constの場合、どこかに割り当てられている変数があります。gccの場合、静的constはプログラムのテキストセグメントに割り当てられます。

上記では、参照演算子についてお話したかったので、逆参照を参照に置き換えます。


1
あなたの答えは非常に間違っています。これはCに関するものであり、あなたの答えはC ++に関連していますconst。Cにはenum-constants以外の記号定数はありません。A const intは変数です。また、言語と特定の実装を混同します。オブジェクトを配置する場所は必要ありません。また、gccについても当てはまりません。通常constは、修飾された変数を.rodataセクションに配置します。しかし、それはターゲットプラットフォームによって異なります。そして、あなたは演算子のアドレスを意味します&
このサイトには正直すぎます

0

MBF16Xで生成されたアセンブラコードを確認しました...どちらのバリアントも、算術演算(たとえば、ADDイミディエート)に同じコードを生成します。

そうconst intしながら、型チェックのために好ましい#define古いスタイルです。多分それはコンパイラ固有です。したがって、生成されたアセンブラコードを確認してください。


-1

私が正しいかどうかはわかりませんが、私の意見では、#defined値の呼び出しは、他の通常宣言される変数(またはconst値)の呼び出しよりもはるかに高速です。これは、プログラムが実行中で、通常宣言されている変数を使用する必要がある場合、その変数を取得するためにメモリ内の正確な場所にジャンプする必要があるためです。

反対に、#defined値を使用する場合、プログラムは割り当てられたメモリにジャンプする必要はなく、単に値を取得します。#define myValue 7とプログラムが呼び出す場合myValue、それは単に呼び出すときとまったく同じように動作します7

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.