web-dev-qa-db-ja.com

Cでの静的、定義、および定数

関数が呼び出されるたびに変数値を変更/初期化したくない場合は、静的変数が関数内で使用されることを読みました。しかし、「メイン」の前にメインプログラムで静的変数を定義するのはどうですか?.

#include <stdio.h>

static double m = 30000;

int main(void)
{
value = m * 2 + 3;
}

ここで、変数mの定数値は、後でメインプログラムで変更されることはありません。同じ考え方で、静的な定義を使用する代わりに、これらを使用するとどのような違いが生じますか。

const double m = 30000;

または

#define m 30000  //m or M  

次に、mを正しいデータ型に変換するために、メインコードでdouble操作を使用するようにします。

19
yCalleecharan
static double m = 30000; 

double foo(double x, double y) {
    return x/m + y;
}

これはあなたに何も勝ちません。計算を行うには、mのコピーを作成する必要があります。また、あなたがそうするならば:

double bar( double x, double y) {
     m += x + y;
     return  m;
}

その後、barへのすべての呼び出しはmを変更します。関数(またはクラス)の外部の静的変数は、実際にはファイルスコープを持つグローバル変数です。他のファイルはexternでそれらを取得できません

関数内の静的変数は、同じファイル内の他の関数でさえ直接見ることができないことを除いて、依然としてグローバル変数に似ています。

const double m = 30000;

これはより良く、多くの場合最良です。コンパイラがこのグローバル定数を確認してからmへの参照を確認すると、コードを生成するのではなく、値をどこからでも(最初にリテラルアドレスをレジスタにロードする必要がある)レジスタまたはスタック位置にロードすることがわかります。計算を行うには、レジスタを30000にするか、30000がエンコードされた命令を生成することができます。

これの欠点は、コンパイラが他のソースファイルがmを読み取りたいと想定し、実際にコピーを変数(ただし定数変数)としてオブジェクトファイルに格納する必要があることです。

標準かどうかはわかりませんが、extern const double m = 30000;を実行できる場合があり、コンパイラは30000を使用して最適化し、別のファイルに実行可能ファイルに格納されるmのコピーが実際にあると想定します。 static const double m = 30000;を実行することもできます。コンパイラは、このソースファイルから生成されたオブジェクトコードにmのコピーが格納されていることを他の誰も期待しないと想定できます。

やってる

#define m 30000

より危険です。以前に変数、定数、または関数として宣言された別のmがあった場合、警告やエラーは発生しません。また、このようなプリプロセッサマクロの場合、混乱しやすいです。例えば:

#define BASE_ADDRESS 48
#define MY_OFFSET  9
#define MY_ADDRESS  BASE_ADDRESS+MY_OFFSET
...
  return MY_ADDRESS*4;

はい、これはばかげた例ですが、プリプロセッサがそれを使い終えた後、これはどのように見えるかです

...
  return 48+9*4;

これは

 return 48+(9*4);

そして、それはおそらくあなたが望んでいたことではありません。

マクロが悪いもう1つの場所は、文字列などの大きな定数がある場合です。文字列は、ポインタでアドレス指定可能である必要があり、整数や浮動小数点リテラルまたは定数よりも最適化が困難です。次のようなものがたくさんある場合は、非常に大きなプログラムを簡単に作成できます。

#define JIM "Jim"
#define JOHN "John"

次に、プログラム全体でJIMとJOHNを使用しました。これは、プログラムで「Jom」と「John」の文字列が本当に必要であることがコンパイラーに認識されない可能性があるためです。

そうは言っても、定数がそのように宣言されているのを見るのは珍しいことではなく、多くの場合、定数は自分が何をしているのかを知っている人々によってそのように適切に行われています。

16
nategoose

staticは、変数が静的な保存期間とローカルの可視性を持つことを意味します。この場合、それはその「ローカル可視性」部分に使用されています。つまり、mはこの変換ユニット内でのみ表示されます(基本的に、このファイルは処理された後)。

7
Jerry Coffin

関数の外部で宣言されたオブジェクトのstaticは、オブジェクトを変換ユニットに対してローカルにするだけです(つまり、他の.cファイルからアクセスすることはできません)。それはそれを一定にしません。 constだったのは。それらは直交しているので、どちらか一方または両方を持つことができます。

例えば.

static const double m = 5;

#defineは、(この場合)定数値として使用できるマクロを宣言します。オブジェクトがないため、変更するオブジェクトがないため、constは適用されません。結果として、マクロのアドレスを取得することもできません。

5
CB Bailey

あなたが書くときconst double m=3000;他のファイルからアクセスできるオブジェクトファイルにシンボルmを作成するようにコンパイラに指示しています。コンパイラーは、mの値を、それが定義されているファイルにインライン化する場合がありますが、シンボルは、個別のコンパイルの目的で割り当てられるhasです。

あなたが書くとき#define m 3000ソースファイルのいくつかの場所に同じ定数を書き込むために、構文上の便宜を使用しているだけです。

5
Pascal Cuoq

mの値を永久に同じままにする必要がある場合は、もちろん、次のいずれかを使用できます。

static const double m = 30000; 

または

#define m 30000

Cでは、constオブジェクトにはデフォルトで外部リンケージがあるため、同等のconst宣言を取得するには、constだけでなくstatic constを使用する必要があることに注意してください。

また、C言語ではconstオブジェクトは定数ではなく、「定数変数」であることに注意してください。 true 定数(つまり、定数式を形成するエンティティ)が必要な場合は、#defineまたは列挙型定数のいずれかを使用する必要があります。

後者は通常、積分定数のみの問題です。 doubleの場合、[static] constを使用したアプローチが最適な場合があります。

3
AnT

...関数が呼び出されるたびに変更/初期化

「変更」と「初期化」という言葉を同じように使用しますが、そうではありません

void f(void) {
  static int a = 0;
  a++; // changed!
  printf("%d\n", a);
}

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

/* 
  # 1
  # 2
*/

ファイルスコープ(関数外)の場合、staticは「静的値」のように「const」を意味しませんが、識別子はその変換単位でのみ参照できることを意味します。

したがって、mなしの最初のconstは引き続き変更できます。 constのみが変更を防ぎます。ただし、staticを省略した場合、ファイルスコープで同じ非静的識別子を持つライブラリまたは別のオブジェクトファイルにリンクすると、リンク時に競合が発生します。

#defineはプリプロセッサ操作であり、コンパイルフェーズが発生する前に、すべてのm30000に置き換えられます。他の2つの例は、善意の変数です。 static変数は、それが宣言されている変換ユニットに存在し、変更可能です。 const変数は読み取り専用です。

2
fbrereto

トップレベルスコープでstaticは、変数(または関数)がこのソースファイルの外部からアクセスできないことを意味します。リンカーで使用できるようにならず、リンクされたときに名前の競合が発生しません。変数が定数であるかどうかには影響しません。実際、このような変数は、初期化をキャッシュできるように、特に非定数であることがよくあります。

const#defineの使用の違いは、前者ではコンパイラーが定数の使用法を型チェックできることです。

1
rampion

主な違いは、#defineを使用すると型システムを離れることです。プリプロセッサには、型安全性、スコープなどの概念はありません。後で次のようなループを作成しようとした場合

for(int m = 0; m <size; m ++){...}

あなたは厄介な驚きに直面しています...

また、#definesを使用する場合、コードのデバッグ時に30000の値のみが表示され、名前mは表示されません。この場合、これは大きな違いにはならないようですが、意味のある定数名と変数名を使用すると、実際に違いがあります。

1
Péter Török