2016-09-12 19 views
3

Cコンパイラとコンパイラフラグのバージョンに依存して、関数内の任意の場所で変数を初期化することができます(私の知る限り)。Cの関数と変数のメモリ割り当て

私はすべての変数を関数の先頭に置いていましたが、関数内の他の場所に変数が定義されていれば、変数のメモリ使用についての議論が始まりました。

以下に私は2つの短い例を書いています。誰かが私にそのメモリがどのように割り当てられているか説明することができるかどうか疑問に思いました。

例1:変数yは可能なreturn文の後に定義されていますが、この変数がその理由で使用されない可能性があります。これは問題ではないと認識しています。 )は、変数が関数の先頭に置かれていれば同じになります。これは正しいです?

例2:変数xはループ内で初期化されます。つまり、この変数のスコープはこのループ内にのみありますが、この変数のメモリ使用はどうですか?関数の上に置かれていれば、それは違うでしょうか?または、関数呼び出し時にスタック上で初期化されましたか? 変数の範囲を縮小したり、最初の使用の場所を変更したりすることは、メモリの使用に影響しますか?

コード例1

static void Function(void){ 
uint8_t x = 0; 

//code changing x 
if(x == 2) 
{ 
    return; 
} 

uint8_t y = 0;  
//more code changing y 
} 

のコード例では、2

static void LoopFunction(void){ 
uint8_t i = 0; 

for(i =0; i < 100; i ++) 
{ 
    uint8_t x = i; 
    // do some calculations 
    uartTxLine("%d", x); 
} 

//more code 
} 

答えて

5

私は、関数の先頭にそれを

これは、使用されているすべての変数を置くために使用しています古いバージョンのCでは必須とされていましたが、現代のコンパイラはその要件を下しました。最初の使用時点で変数の型を知っている限り、コンパイラには必要なすべての情報があります。

メモリがどのように割り振られるのか誰でも説明できますか?

コンパイラは、自動ストレージ領域にメモリを割り当てる方法を決定します。実装は、各変数に別々の場所を宣言するというアプローチに限定されません。範囲外になる変数の場所や、特定の時点以降に使用されなくなった変数の場所を再利用することができます。

yの使用の最初のポイントはxの使用の最後の点の後にあるので、あなたの最初の例では、変数yは、以前は変数xの占有スペースを使用することを許可されています。

2番目の例では、ループ内のxに使用される領域は、// more code領域で宣言できる他の変数に再利用できます。

+0

さて、これは、明確ではないかもしれないところ、それが外にあるとき、上部にあるすべての変数を置くのではなく、それをこのようにコーディングし、実際に、より効率的であることを意味しますスコープ、またはいつ使用する予定ですか?それともこれは問題ではないのですか? – koldewb

+6

@koldewb変数のアクティブな使用領域を把握し、それに応じてメモリを割り当てることができるので、コンパイラには関係ありません。しかし、あなたのコードを読んでいる人にとっては大変重要な意味を持つので、変数を使用する場所に近づけて配置することは、すべてを上にするよりも優れています。 – dasblinkenlight

1

基本的に、この話はこのようになります。生のアセンブラで関数を呼び出すときには、関数が使用するすべてのものをスタックに格納して、関数を呼び出すときにそれをクリーンアップするのが習慣です。特定のCPUおよびABIは、パラメータの自動積み重ねを伴う呼び出し規約を有する場合がある。

これのために、Cおよび多くの他の古い言語では、関数の上端(またはスコープの上端)にすべての変数を宣言して、{ }がスタックにプッシュ/ポップを反映する必要があるという要件がありました。

80年代と90年代のどこかで、コンパイラは、最初に使用された時点でローカル変数の領域を確保するだけで、そのようなコードを効率的に最適化し始めました。それのための。その変数がどこで宣言されているかに関わらず、コンパイラの最適化には関係ありませんでした。

Cと同じ時間に、C++はCが持つ変数宣言の制限を解除し、どこでも変数を宣言することができました。しかしCは1999年以前に更新されたC99標準でこれを修正していませんでした。現代Cでは、どこにでも変数を宣言できます。

非常に古いコンパイラを使用していない限り、2つの例の間にはパフォーマンスの違いはありません。しかし、可読性を犠牲にして行うべきではないが、可能な限り変数の範囲を狭めることは、プログラミングの良い習慣と考えられる。それはスタイルの問題だけですが、私は個人的にこのようなあなたの関数を書くことを好むだろう

(あなたがuint8_tの間違ったprintf書式指定子を使用していることに注意してください)

#include <inttypes.h> 

static void LoopFunction (void) 
{ 
    for(uint8_t i=0; i < 100; i++) 
    { 
    uint8_t x = i; 
    // do some calculations 
    uartTxLine("%" PRIu8, x); 
    } 

//more code 
} 
0

古いですCはブロックの先頭にある変数を宣言(および初期化)するだけで済みました。もしあなたが、それらを使用してコードの隣に変数を宣言する可能性を持っていたので、どこにでもブロック内の新しいブロック({}文字のペアを)initに許可:に許可されている場合、あなたが

... /* inside a block */ 
{ int x = 3; 
    /* use x */ 
} /* x is not adressabel past this point */ 

そして、さて、あなたはステートメントが許可されている任意の場所に変数を宣言することが許可されている

を(あなたが新しいブロックを初期化することができますどこどこでも)switch文、if文とwhiledo文でこれを行うと、その変数のスコープは行きます宣言の点から内側の端まであなたがそれを宣言したtedブロック。

コンパイラは、ローカル変数にストレージを割り当てるタイミングを決定します。スタックフレームを作成するときに、すべての変数を割り当てることができます(ローカル変数を1回だけ割り当てるgccの方法です)。ランタイム時にスペースを割り当てることは、実行時にスタックポインタを前進させる必要があるため、スタックフレームごとに1回だけ行う場合は、CPUサイクルを節約します(しかし、メモリの場所を浪費します)。ここで重要なことは、スコープ定義の外で可変の場所を参照することが許可されていないことです。したがって、実行しようとすると、は未定義の動作になります。私はインターネット経由で長時間実行されていた古いバグを発見しました.GCCでコンパイルする代わりに、Microsoft-Cコンパイラ(コアダンプには失敗しました)を使用してそのプログラムをコンパイルする時間がないからです。コードは、内部スコープ(ifステートメントのthen部分)で定義されたローカル変数を使用していました(すべてがmain関数上にあったため、スタックフレームは常に存在していました)。 ifステートメントの終了時にスペースを再割り当てしましたが、GCCはmainが終了するまで待機しました。static修飾子を変数宣言(グローバルにする)に追加するだけで解決され、それ以上のリファクタリングは不要でした。

int main() 
{ 
    struct bla_bla *pointer_to_x; 
    ... 
    if (something) { 
     struct bla_bla x; 
     ... 
     pointer_to_x = &x; 
    } 
    /* x does not exist (but it did in gcc) */ 
    do_something_to_bla_bla(pointer_to_x); /* wrong, x doesn't exist */ 
} /* main */ 

に変化したとき:

int main() 
{ 
    struct bla_bla *pointer_to_x; 
    ... 
    if (something) { 
     static struct bla_bla x; /* now global ---even if scoped */ 
     ... 
     pointer_to_x = &x; 
    } 
    /* x is not visible, but exists, so pointer_to_x continues to be valid */ 
    do_something_to_bla_bla(pointer_to_x); /* correct now */ 
} /* main */ 
関連する問題