1

私はそのようなCコードを持っています。64ビットLinuxシステムでは、結果は-32ではなく4294967264になります。clangとgccはどちらもバイナリを生成し、同じ誤った結果になります。行の問題:

* v = va_arg(args、long);
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

void setter(long *v, ...)
{
        va_list args;
        va_start(args, v);
        *v = va_arg(args, long);
        va_end(args);
}

int main()
{
        long v = 0;
        setter((long *) &v, -32);
        printf("%ld\n", v);
        return 0;
}
4

4 に答える 4

4

実際にはlong、関数にを渡す必要があります。を渡しますint

setter(&v, -32L);
于 2013-03-18T15:51:20.013 に答える
3

x86_64アーキテクチャでは、のサイズlongは64ビットです。-32に渡す場合setter()、そのタイプはint32ビットです。long渡されたい場合は、明示的にキャストしてください。例えば:

setter((long *) &v, (long)-32);
于 2013-03-18T15:52:00.757 に答える
1

少し説明します。
前述のように、64ビットアーキテクチャでは、alongは64ビットです。ただし、C / C ++は自動変換を行うため、これですべてではありません。ここで、setter()関数は1つの指定された引数と0個以上の指定されていない引数を受け入れます。-32引数は、これらの指定されていない引数の1つであるため、コンパイラーは、実際にalongが意図されており、int(32ビット)を保持していることを認識していません。また、64ビットのアライメントを維持するために、余分なゼロがスタックにプッシュされます。これにより、上記のように印刷結果が生成されます。longしたがって、ここ(-32Lまたは)が必要であることを明示的に指定する必要があります(long) -32。しかし、関数が実際に宣言されていた場合void setter (long *v, long arg)、コンパイラは2番目の引数がlong、およびint引数を自動的に変換しました。

于 2015-04-30T10:21:41.103 に答える
0

さらに奇妙ですが、実際のコンパイラのバグではありません。-32値を7番目以降の可変個引数として渡すと、64ビットに拡張される可能性があります。

#include <stdio.h>
#include <stdarg.h>

/* long long int is a 64bit datatype on both Unix and Windows */
void setter(long long int arg, ...)
{
    va_list args;
    va_start(args, arg);
    while(arg != 0) {
        printf("0x%016llx  %lld\n", arg, arg);
        arg = va_arg(args, long long int);
    }
    va_end(args);
}

int main()
{
    setter(-32, -32, -32, -32, -32, -32, -32, -32, 0);
    return 0;
}

また、x64Linuxでの出力は次のとおりです。

0xffffffffffffffe0  -32
0x00000000ffffffe0  4294967264
0x00000000ffffffe0  4294967264
0x00000000ffffffe0  4294967264
0x00000000ffffffe0  4294967264
0x00000000ffffffe0  4294967264
0xffffffffffffffe0  -32
0xffffffffffffffe0  -32

ただし、x64Windowsでの出力は次のようになります。

0xffffffffffffffe0  -32
0x00000000ffffffe0  4294967264
0x00000000ffffffe0  4294967264
0x00000000ffffffe0  4294967264
0x00040800ffffffe0  1134700294832096
0x178bfbffffffffe0  1696726761565323232
0x00007ff6ffffffe0  140698833649632
0x00007ff6ffffffe0  140698833649632

ここでの理由は、64ビットx86の呼び出し規約では、いくつかの先行引数(System V AMD64 ABIでは6 、Microsoft x64では4)がCPUレジスタを介して渡され、後続の引数のみがスタックを介して渡されるためです。

64ビットレジスタは下位32ビットへの個別のアクセスを提供するため、コンパイラは下位32ビットのみを設定するため、64ビットへの値の拡張はありません。

以降の引数については、コンパイラによって異なります。

  • gccは拡張された64ビット値をスタックにプッシュします
  • MSVC cl.exeは、下位32ビットをスタックに書き込み、上位32ビットを初期化しないままにします。
  • 他のコンパイラについてはよくわかりません

いずれの場合も、スタックの各引数用に64ビットが予約されています。

于 2019-03-05T18:04:56.727 に答える