3
size_t size_int = sizeof(unsigned long int);
size_t size_ptr = sizeof(void*);
printf("sizeof(unsigned long int): %zu\n", size_int);
printf("sizeof(void*): %zu\n", size_ptr);

if(size_int == size_ptr) {
    int a = 0;
    void * ptr_a = &a;
    
    // case 1
    unsigned long int case_1 = *((unsigned long int*)&ptr_a);
    printf("case 1: %lu\n", case_1);

    // case 2
    unsigned long int case_2 = (unsigned long int)ptr_a;
    printf("case 2: %lu\n", case_2);

    // case 3
    unsigned long int case_3 = 0;
    memcpy(&case_3, &ptr_a, sizeof(void*));
    printf("case 3: %lu\n", case_3);
    
    // case 4
    void *ptr_b = NULL;
    memcpy(&ptr_b, &case_3, sizeof(void*));
    int *ptr_c = (int*)ptr_b;
    *ptr_c = 5;
    printf("case 5: %i\n", a);
}

実際、C99 には uintptr_t と intptr_t があることを認識しています。しかし、教育目的のために、いくつか質問したいと思います。始める前に、これは悪い習慣であり、この方法で行うべきではないことを知っています。

Q1. ケース1は未定義の動作を引き起こす可能性がありますか? 安全ですか?そうでない場合、なぜですか?安全であれば、「case_1」変数が unsigned long int とまったく同じアドレスを保持することが保証されていますか?
Q2. ケース 2 についても同様です。
Q3. ケース3も同様。
Q4.ケース 4 については上記と同じです。

4

3 に答える 3

1

C言語リファレンス(またはC11のより正確にはドラフトn1570)は、6.3.2.3変換/ポインター§6で述べています:

任意のポインター型を整数型に変換できます。前に指定された場合を除き、結果は実装定義です。結果が整数型で表現できない場合、動作は未定義です。結果は、任意の整数型の値の範囲内にある必要はありません。

したがってsizeof(void*)==sizeof(unsigned long int)、何らかの理由で結果を表現できなかった場合、未定義の動作である可能性があります。次の原因が考えられます。

  • タイプ内のビットをパディングすると、unsigned long表現される値が少なくなります
  • 表現できない値をもたらす病理学的変換

一般的なアーキテクチャ (実際に私が知っているすべて) では、ポインターを unsigned long に変換すると、まったく同じビットのメモリ アドレスが得られ、整数型にはパディングがないため、未定義の動作は発生しません。しかし、基準は非常に保守的です...

于 2020-07-02T08:37:51.683 に答える