2017-06-02 4 views
7

は、このテストプログラムを見るなぜできるのallocaの範囲へのI後藤:D変数ではなく、可変長配列?

#include <stdio.h> 
#include <string.h> 

int main(int argc, char *argv[]) 
{ 
    if (argc < 2) 
    goto end; 

    char s[strlen(argv[1]) + 1]; 
    strcpy(s, argv[1]); 
    printf("s=%s\n", s); 

end: 
    return 0; 
} 

は、エラー(other question参照)「可変変性タイプと識別子の範囲にジャンプ」でコンパイルすることができません。

私はこれにsの宣言を変更(およびalloca.hを含む)にあれば、それは罰金コンパイルしかし:

char *s = alloca(strlen(argv[1]) + 1); 

なぜC標準はallocaで作成されたオブジェクトの範囲に飛び込むことができないではなく、可変長配列?私は彼らが同等だったと思った。

+0

VLAの範囲を分岐するとスタックが混乱しますが、 'sizeof(char *)'は固定されているため、手を振って答えます。まともな答えを得るチャンスを増やすために弁護士のタグを追加しました。 – Bathsheba

+0

ありがとう、言語弁護士は間違いなく適用されます。もし私が推測しなければならないのは、 'alloca'を飛び越えて' s'を使うのは 's'が初期化されていないポインタなので既にUBだが、VLA宣言を飛び越して' s'を使うのは、ジャンプ自体は禁止されていませんでした。 –

+1

'alloca'は標準ではないので、コンパイラは他の関数と同じように扱うことができます(ただし、しばしばそうしません)(http://man7.org/linux/man-pages/man3/alloca.3 .html#NOTES))、その場合は標準ですべて問題ありません。もちろん、同様の未定義の動作につながる可能性があります。 – Kninnug

答えて

2

コンパイラ、ランタイム初期化しなければならないためのVLAとの範囲の枠をそれはあります。言い換えれば、あなたは:ENDに対処するためにジャンプすることを教えていますが、そのスコープのフレームの初期化コードの上にジャンプすることをお願いします。

VLAためのスペースを初期化するためのコードは、単にVLAの長さを計算する式の前にあります。あなたには、いくつかのgotoを行うことができますコードを、スキップした場合、すべてのプログラムがセグメンテーションフォールトます。あなたはVLA aのミューテータへジャンプけどとしてのコードは単にセグメンテーションフォルト、初期化されていなかっただろう。この場合

if (cond) goto end; 
... 
char a[expr]; 
end: 
a[i] = 20; 

のようなものを想像してみてください。 VLAを初期化するためのコードは、定義の場所に挿入する必要があります。

今についてalloca。コンパイラは同じことを行いますが、segfaultを検出することはできません。

これはsegfaultです。コンパイラの警告/エラーはありません。

ロジックはVLAと同じです。 ISO 9899で

int main(int argc, char *argv[]) 
{ 
    goto end; 
    char *s = alloca(100); 
end: 
    s[1] = 2; 
    return 0; 
} 

彼らは声明を挿入理由です:

6.8.6.1 goto文 - goto文で制約

1識別子があるラベルに名前を付けなければなら 囲む機能のどこかにある。 goto文は、 を、その識別子の範囲内に可変的に変更された識別子の範囲外からにジャンプさせてはならない。

実際にはなので、コンパイラはこの問題の正解を静的解析中に検出できません。

+0

これは、単にC標準がVLAを静的解析に従属させようとするという技術的理由( 'alloca'は' malloc'でもあったかもしれません)です。単に静的に宣言されているからですダイナミックなサイズを考えれば、とにかくそれに完全に従順ではありません)。私にはVLAの代わりに 'alloca'を標準化するほうがはるかに優れた決断だったようです。 – PSkocik

+1

@PSkocik私の意見では、言語の定義によって課せられたこの制限は、 'halting-problem'を避けるための' brute-force'な方法です。 – alinsoar

2

VLAの宣言後にジャンプした場合のVLAの割り当て解除に加えて、sizeofの問題もあります。コンパイル時に計算され、すべてが順調であることができallocaバージョン、sizeof s == sizeof(char*)について

end: 
    printf("size of str: %zu\n", sizeof s); 
    return 0; 
} 

は、あなたのプログラムはこれで拡張された想像してみてください。しかし、VLAバージョンの場合、sの長さは不明であり、sizeof sと計算できません。

+0

あなたのポイントは良いです、私は 'sizeof'の値は宣言の代わりに' sizeof'呼び出しの場所ではなく実際に計算され、sizeofの値はstack/some registerなので、sizeofの直前でジャンプし、定義の場所で初期化コードをジャンプすると、sizeof printが凶悪になります。 – alinsoar

+0

@alinsoar:VLAのサイズについてのあなたの主張保存されています? –

+0

int a [m]; m ++; sizeof(a);サイズは定義時に計算され、どこかに格納されます。 – alinsoar

関連する問題