定数(コンピュータープログラミング)

ウィキペディアから、無料の百科事典
ナビゲーションにジャンプ 検索にジャンプ

コンピュータプログラミングでは定数は通常の実行中にプログラムによって変更されるべきではないです。つまり、値は定数です。[a]識別子に関連付けられている場合、定数は「名前付き」と呼ばれますが、「定数」と「名前付き定数」という用語はしばしば同じ意味で使用されます。これは、通常の実行中に変更できる値を持つ識別子である変数は対照的です。つまり、値は変数です。定数は、プログラマーとコンパイラーの両方に役立ちます。プログラマーにとって、定数は自己文書化コードの形式であり、正しさ、コンパイラの場合は、コンパイル時実行時のチェックを許可して、不変性の仮定に違反していないことを確認し、一部のコンパイラの最適化を許可または簡素化します。

定数の一般的な概念にはさまざまな具体的な認識があり、微妙な違いは見過ごされがちです。最も重要なものは、コンパイル時(静的に評価される)定数、実行時(動的に評価される)定数、不変オブジェクト、および定数型(const)です。

コンパイル時定数の典型的な例には、数学定数、標準からの値(ここでは最大伝送単位)、または内部構成値(ここでは1行あたりの文字数)が含まれます。たとえば、次のCの例です。

const float PI = 3.1415927 ; //最大単精度浮動小数点精度constunsignedint MTU = 1500 ; //イーサネットv2、RFC 894 const unsigned int COLUMNS = 80 ;      
       
     

時定数の典型的な例は、次のC ++の例のように、関数への入力に基づいて計算された値です。

void f std :: string s {   
  const size_t l = s 長さ();    
  //... 
}

を使用

一部のプログラミング言語では、定数記号と変数記号を明示的に構文的に区別します。たとえば、定数への代入を構文エラーと見なしますが、他の言語では、構文的に同じであると見なされ(両方とも単に識別子)、処理の違いは次のとおりです。セマンティック(識別子への割り当ては構文的に有効ですが、識別子が定数の場合は意味的に無効です)。

定数値は一度定義され、プログラム全体で何度も参照できます。同じ値を複数回指定する代わりに定数を使用すると、コードのメンテナンスを簡素化でき(自分自身を繰り返さないように)、たとえばPI3.1415926の代わりに値に意味のある名前を指定することで、自己文書化できます。

リテラルおよびマクロとの比較

プログラムの実行中に変化しないデータ値を表現する主な方法はいくつかあり、さまざまなプログラミング言語で一貫しています。非常に基本的な方法の1つは、リテラルの数値、文字、または文字列をプログラムコードに書き込むことです。これは、C、C ++、および同様の言語で簡単に実行できます。

アセンブリ言語では、リテラルの数値と文字は、ほとんどのマイクロプロセッサで使用可能な「イミディエートモード」命令を使用して実行されます。「immediate」という名前は、メモリアドレスを検索して間接的にロードするのではなく、命令ストリームからすぐに利用できる値に由来します。[1]一方、文字列や配列など、マイクロプロセッサのワード長より長い値は間接的に処理され、アセンブラは通常、そのようなデータテーブルをプログラムに埋め込むための「データ」疑似演算を提供します。

もう1つの方法は、シンボリックマクロを定義することです。多くの高級プログラミング言語と多くのアセンブラは、プログラマーが一般にソースファイルの先頭または別の定義ファイルでさまざまな値の名前を定義できるマクロ機能を提供します。次に、プリプロセッサはコンパイル前にこれらの名前を適切な値に置き換えます。その結果、リテラルを使用するのと機能的に同じものになり、イミディエートモードの速度の利点が得られます。すべての値が文字通りに記述されているコードを維持するのは難しい場合があるため、値が反復的または非自明な方法で使用される場合、それはマクロとして実行されることがよくあります。

3番目の方法は、変数を「定数」として宣言および定義することです。グローバル変数または静的変数は、、、、、などのキーワード修飾子を使用して宣言(またはアセンブリで定義されたシンボル)できますつまり、その値はコンパイル時に設定され、実行時に変更できないようにする必要があります。コンパイラは通常、非定数で初期化されたデータが保持されるデータセクションとは対照的に、コード自体とともにオブジェクトファイルのテキストセクションに静的定数を配置します。一部のコンパイラは、定数専用のセクションを作成できます。この領域にメモリ保護を適用して、誤ったポインタによるそのような定数の上書きを防ぐことができます。 constconstantfinal

これらの定数は、多くの点でリテラルとは異なります。コンパイラは通常、マクロのように実行可能ファイル全体に分散するのではなく、シンボルで識別される単一のメモリ位置に定数を配置します。これにより、イミディエートモードの速度の利点は失われますが、メモリ効率には利点があり、デバッガーは実行時にこれらの定数を処理できます。また、CとC ++のヘッダーファイルの競合によってマクロが誤って再定義される可能性がありますが、コンパイル時に競合する定数が検出されます。

言語に応じて、定数は型指定されていない場合と型付けされている場合があります。CおよびC++では、マクロは前者を提供し、後者は後者 を提供します。const

#define PI 3.1415926535

const float pi2 = 3.1415926535 ;    

Adaには、必要に応じて使用できるユニバーサル数値タイプがあります。

円周率  定数 :=  3.1415926535 ;

pi2   定数 float  :=  3.1415926535 ;

型なしのバリアントは、使用するたびに暗黙的に適切な型に変換されます。[2]

動的に評価される定数

上記の静的定数に加えて、AdaやC ++などの多くの手続き型言語は、定数の概念を、初期化時に作成されるグローバル変数、実行時にスタックまたはレジスタに自動的に作成されるローカル変数に拡張し、動的に割り当てられたメモリに拡張します。ポインタ、および関数ヘッダーのパラメータリストからアクセスします。

動的に値が設定される定数は、変数がメモリの特定の領域に存在することを指定したり、コンパイル時に値が設定されたりすることはありません。次のようなC++コードの場合

float func const float ANYTHING {    
    const float XYZ = someGlobalVariable * someOtherFunction ANYTHING );    
    ..。
}

定数が初期化される式自体は定数ではありません。ここでは、プログラムの合法性や意味の正確さのために一定性を使用する必要はありませんが、次の3つの利点があります。

  1. 一度設定すると、オブジェクトがそれ以上変更されないことは読者には明らかです。
  2. オブジェクトの値を変更しようとする試み(プログラムロジックを完全に理解していない後のプログラマーによる)は、コンパイラーによって拒否されます。
  3. コンパイラーは、オブジェクトの値が一度作成されると変更されないことを知って、コードの最適化を実行できる場合があります。[3]

動的に評価される定数は、ALGOL68の言語機能として生まれました。[3] AdaおよびC++コードの研究によると、動的に値付けされた定数は、ローカルの非クラスオブジェクトの約40〜50%が使用されているため、使用頻度が低いことが示されています。一度作成されると実際には不変です。[3] [4]一方、このような「不変変数」は、副作用(再帰など)のないプログラミングスタイルを好むか、MLなどのほとんどの宣言をデフォルトで不変にするため、関数型言語ではデフォルトになる傾向があります。 。純粋に関数型言語でさえ、副作用を完全に禁止します。

定数は、オブジェクトが参照によって渡されたときに、呼び出された関数がそれを変更しないという約束として、関数宣言でよく使用されます。構文に応じて、ポインタまたはポイントされているオブジェクトのいずれかが一定である場合がありますが、通常は後者が望ましいです。特にC++とCでは、プログラム全体で適切なデータ構造が一定であることを保証するための規律は、const-correctnessと呼ばれます。

定数関数パラメーター

C / C ++では、関数またはメソッドのパラメーターを定数として宣言することができます。これは、このパラメーターが最初の割り当て後に(誤って)変更されないことを保証するものです。パラメーターが事前定義された(組み込み)タイプの場合、値によって呼び出され、変更できません。ユーザー定義型の場合、変数はポインターアドレスであり、変更することもできません。ただし、オブジェクトのコンテンツは無制限に変更できます。パラメーターを定数として宣言することは、この値を変更してはならないことを示す方法かもしれませんが、プログラマーは、オブジェクトの変更に関するチェックをコンパイラーが実行できないことに注意する必要があります。

この機能に加えて、C ++では、関数またはメソッドをとして宣言することもできます。これにより、そのような関数やメソッドがローカル変数以外のものを変更するのを防ぎます。 const

C#では、キーワードconstは存在しますが、C / C ++の場合のように、関数パラメーターに対して同じ効果はありません。ただし、少し注意が必要ですが、コンパイラを「かき混ぜて」チェックを行う方法があります。[5]

同じ効果を得るには、最初に2つのインターフェイスを定義 します

パブリック インターフェイス IReadable 
{ 
    IValueInterfaceaValue  { get ; _  } }  


パブリック インターフェイス IWritable   IReadable 
{ 
    IValueInterface  aValue  {  set ;  } 
}

public  class  AnObject   IWritable 
{ 
    private  ConcreteValue  _aValue ;

    public  IValueInterface  aValue 
    { 
        get  {  return  _aValue ;  } 
        set  {  _aValue  =  value  as  ConcreteValue ;  } 
    } 
}

次に、定義されたメソッドは、読み取り専用または読み取り/書き込み機能を備えた適切なインターフェイスを選択します。

public  void  doSomething IReadable  aVariable 
{ 
    // aVariableを変更できません!
}

public  void  doSomethingElse IWritable  aVariable 
{ 
    // aVariableを変更できるので、注意してください!
}

オブジェクト指向定数

一定のデータ構造またはオブジェクトは、オブジェクト指向の用語では「不変」と呼ばれます。不変であるオブジェクトは、プログラム設計にいくつかの利点をもたらします。たとえば、ポインタまたは参照をコピーするだけで「コピー」でき、時間のかかるコピー操作を回避し、メモリを節約できます。

C ++などのオブジェクト指向言語は、定数性をさらに拡張します。構造体またはクラスの個々のメンバーは、クラスがそうでない場合でも、constにすることができます。逆に、このキーワードを使用すると、オブジェクトがとしてインスタンス化された場合でも、クラスメンバーを変更できますmutableconst

関数でさえC++でconstにすることができます。ここでの意味は、constとしてインスタンス化されたオブジェクトに対してconst関数のみを呼び出すことができるということです。const関数は、変更できないデータを変更しません。

C#にはaconstreadonly修飾子の両方があります。そのconstはコンパイル時の定数専用ですが、読み取り専用はコンストラクターやその他のランタイムアプリケーションで使用できます。

Java

finalJavaには、参照の変更を防ぎ、別のオブジェクトを指さないようにするという修飾子があります。これは、参照されるオブジェクト自体への変更を妨げるものではありません。Javaは基本的にC++のポインタfinalと同等です。のその他の機能は提供しませんconst const

Javaでは、修飾子finalは、影響を受けるデータメンバーまたは変数が割り当て可能ではないことを次のように示します。

final  int  i  =  3 ; 
i  =  4 ;  // エラー!「最終的な」オブジェクトを変更することはできません

finalマーカー付きの変数が初期化されるコンパイラーによって決定可能である必要があり、一度だけ実行する必要があります。そうしないと、クラスはコンパイルされません。JavafinalとC++のキーワードは、プリミティブ変数を適用した場合に同じ意味を持ちます。 const

const int i = 3 ; //C++宣言i = 4 ; // エラー!     
   

ポインターを考慮すると、Javaでの参照は、C++でのポインターにfinal似たものを意味します。C ++では、「定数ポインター型」を宣言できます。 const

Foo * const bar = mem_location ; //constポインタタイプ     

ここでは、宣言時に初期化する必要があり、再度変更することはできませんが、ポイントする内容変更可能です。つまり、有効です。別の場所を指すことはできません。Javaの最終参照は、初期化されていない状態で宣言できることを除いて、同じように機能します。 bar*bar = value

最終的 Fooi  ; //Java宣言 

注:Javaはポインターをサポートしていません。[6] これは、ポインター(制限付き)がJavaのオブジェクトにアクセスするデフォルトの方法であり、Javaがそれらを示すために星を使用しないためです。たとえば、最後の例のiはポインタであり、インスタンスにアクセスするために使用できます。

C++で「読み取り専用」データへのポインタを宣言することもできます。

const Foo * bar ;  

ここでbarは、いつでも何でも指すように変更できます。そのポイントされた値だけをポインタ で変更する ことはできません。bar

Javaには同等のメカニズムはありません。したがって、方法もありませんJavaでは定数の正確性を強制することはできませんが、インターフェースを使用し、クラスへの読み取り専用インターフェースを定義してこれを渡すことにより、オブジェクトを変更できない方法でシステムに渡すことができます。 const

Javaコレクションフレームワークは、{ Collectionviaおよび同様のメソッドの不変ラッパーを作成する方法を提供します。 Collections.unmodifiableCollection()

Javaのメソッドは「final」と宣言できます。つまり、サブクラスでオーバーライドすることはできません。

C#

C#では、修飾子はJavaの場合とC++のreadonly場合と同じ効果をデータメンバーに与えます。修飾子には、C ++の場合と同様の効果があります(まだ型指定され、クラススコープされています) 。もう1つの、メソッドとクラスに適用された場合のJavaの継承抑制効果は、キーワードを使用してC#で誘導されます。 finalconstconst#definefinalsealed

C ++とは異なり、C#ではメソッドとパラメーターをとしてマークすることはできませんconstただし、読み取り専用サブクラスを渡すこともできます。.NETFrameworkは、可変コレクションを読み取り専用ラッパーとして渡すことができる不変コレクションに変換するためのサポートを提供します。

パラダイムによって

定数の扱いは、プログラミングパラダイムによって大きく異なります。デフォルトでは、名前のバインディングは通常、名前が示すように変化する可能性のある変数を作成するため、定数の正確さはC ++のような命令型言語の問題です。したがって、バインディングを定数としてマークしたい場合は、追加の指示が必要です。[b]他のプログラミング言語のパラダイムでは、const-correctnessに類似した問題がいくつか見られ、関連する問題が発生します。

関数型プログラミングでは、データは通常、デフォルトでは可変ではなく、デフォルトで一定です。変数(名前と潜在的に変数の値を持つストレージスペース)に値を割り当てる代わりに、Lispletの多くの方言の構造などによって、名前の値へのバインディングを作成します。一部の関数型言語、特にCommon Lispなどのマルチパラダイム言語では、データの変更は一般的ですが、他の関数型言語では回避または例外と見なされます。これは、構文を使用してデータを変更するScheme(別のLisp方言)の場合です。set!これに注目を集める感嘆符。このような言語は、デフォルトでconst-correctnessの目標を達成し、一定性ではなく変更に注意を向けます。

多くのオブジェクト指向言語には、不変オブジェクトの概念があります。これは、文字列などの基本的な型に特に使用されます。注目すべき例としては、Java、JavaScript、Python、C#などがあります。これらの言語は、ユーザー定義型を不変としてマークできるかどうかによって異なり、オブジェクトまたは型の特定のフィールド(属性)を不変としてマークできる場合があります。

オブジェクト指向と機能の両方のスタイルを可能にする一部のマルチパラダイム言語では、これらの機能の両方を組み合わせることができます。たとえば、OCamlオブジェクトフィールドはデフォルトで不変であり、変更可能にするにはキーワードで明示的にマークする必要がありますが、Scalaでは、バインディングは「value」でmutable定義すると明示的に不変であり、「variable」で定義すると明示的に変更可能です。 valvar

命名規則

定数の命名規則はさまざまです。他の変数と同じように、単に名前を付ける人もいます。他の人は、などのシンボリックマクロの従来の使用法と同様の方法で、定数に大文字とアンダースコアを使用しますSOME_CONSTANT[7]ハンガリアン記法では、「k」接頭辞は、定数、マクロ列挙型を示します。

強制される規則の1つは、Rubyでは、大文字で始まる変数はすべて、クラス名を含めて定数と見なされるというものです。

も参照してください

メモ

  1. ^ 場合によっては、自己変更コードを使用したり、値が格納されているメモリの場所を上書きしたり
  2. ^ これは普遍的ではありません。たとえば、Adaの入力パラメーターとループパラメーターは暗黙的に定数です。

参照

  1. ^ IBMシステム情報命令セット-PowerPCのアセンブラ言語リファレンス。
  2. ^ ブーチ、グレイディ(1983)。Adaによるソフトウェアエンジニアリングベンジャミンカミングスpp。116–117  _ ISBN 0-8053-0600-5
  3. ^ a b c Schilling、Jonathan L.(1995年4月)。「動的に評価される定数:十分に活用されていない言語機能」。SIGPLANの通知30(4):13–20。土井10.1145/202176.202177
  4. ^ Perkins、JAプログラミングプラクティス:空軍、陸軍、および海軍向けに開発されたAdaソースの分析議事録TRI-Ada'89。pp。342–354。土井10.1145/74261.74287
  5. ^ ティムウィ(2010-09-09)。「C#の読み取り専用(「const」のような)関数パラメーター」https://stackoverflow.com/:StackOverflow _ 2012年5月6日取得[...]次に、変数の変更を計画しているかどうかに関係なく、パラメータータイプが「tells」であるメソッドを宣言できます。[...]これは、C++の定数に似たコンパイル時のチェックを模倣しています。Eric Lippertが正しく指摘したように、これは不変性と同じではありません。しかし、C ++プログラマーとして、あなたはそれを知っていると思います。 {{cite web}}:(ヘルプ外部リンク|location=
  6. ^ 「Java開発者のためのOracleテクノロジーネットワーク|Oracleテクノロジーネットワーク|Oracle」Java.sun.com。2013-08-14 2013年8月18日取得
  7. ^ Microsoft Office XP開発者:定数名