14

私は次のCコードを持っています:

#include <stdint.h>
#include <stdio.h>

int i;
uint64_t a[] = { (uint64_t)&i, (uint64_t)&i + 0x8000000000000000 };

int main() {
    printf("%p %llx %llx\n", &i, a[0], a[1]);
}

これを (C または C++ として) Microsoft Visual Studio Community 2015 でコンパイルして実行すると、出力は次のようになります。

013E9154 13e9154 13e9154

+ 0x8000000000000000の上位ビットを設定することを期待していたコードは、a[1]黙って無視されたようです。

ただし、a内部の初期化を移動するmainと、出力は期待どおりになります。

00179154 179154 8000000000179154

グローバルでaは、追加が黙って無視されるのはなぜですか? 試行された追加は、実際に上位ビットを設定するa[1]必要がありますか、それともコンパイラ エラーを引き起こす必要がありますか?

興味深いことに、+ 0x8000000000000000上記のコードを に置き換えると| 0x8000000000000000、「エラー C2099: 初期化子が定数ではありません」というメッセージが表示されます。

編集:キャストがなくても同様の問題が発生する可能性があります。x64 用にコンパイルされた次のコードは、同じ値 (例: 000000013FB8D180) を 3 回出力します。

#include <stdio.h>

int i;
int * a[] = { &i, &i + 0x100000000 };

int main() {
    printf("%p %p %p\n", &i, a[0], a[1]);
}
4

2 に答える 2

1

で使用される初期化子はありません

uint64_t a[] = { (uint64_t)&i, (uint64_t)&i + 0x8000000000000000 };

適格な定数式です。C の定数式のペダンティックな定義では、ポインター値がアドレス定数の要件を満たしている場合でも、ポインター値を整数型にキャストすることはできません。つまり(uint64_t)&i、この文脈では形式的にはすでに違法です。

ただし、このコンパイラは(uint64_t)&i、このコンテキストで拡張機能として受け入れているようです。

+あと、演算子に置き換えると文句を言う|のは言語仕様に直接根ざしているのだろう

6.6 定数式

7初期化子の定数式には、より多くの自由度が認められています。このような定数式は、次のいずれかになるか、評価されます。

— 算術定数式、

— null ポインター定数、

— アドレス定数、または

— オブジェクト型のアドレス定数プラスまたはマイナスの整数定数式。

繰り返しますが、上記の文言ではアドレス定数のみに固定オフセットを追加できるため、これは完全に一致しません(uint64_t)&iが、このコンテキストで定数式として受け入れるコンパイラの場合、「プラスまたはマイナス」を適用し続けることは珍しくありません。 " 制限。C のアドレス定数に何かを追加する (またはアドレス定数から何かを減算する) 機能は、ロード時にアドレス再配置を実行するローダーの機能によって定義されます。ローダーは加算または減算できますが、アドレスに対してビット演算を実行することはできません。

そして最後に、実行時に効果がないという事実は、起動時に C スタイルのスタティックの初期化を実装する役割を担うローダーの制限によるものと思われます。

于 2016-10-07T16:30:02.273 に答える