为什么我的可变参数函数可以同时使用 int 和 long long ?

2023-12-19

根据这个答案 https://stackoverflow.com/a/40323646/1197719传递给可变参数函数的数字常量始终被视为int如果它们合二为一的话。这让我想知道为什么下面的代码适用于两者,int and long long。考虑以下函数调用:

testfunc(4, 1000, 1001, 1002, 1003);

testfunc看起来像这样:

void testfunc(int n, ...)
{
    int k;
    va_list marker;

    va_start(marker, n);
    for(k = 0; k < n; k++) {
        int x = va_arg(marker, int);
        printf("%d\n", x);
    }
    va_end(marker); 
}

这很好用。它打印 1000, 1001, 1002, 1003。但令我惊讶的是,以下代码也有效:

void testfunc(int n, ...)
{
    int k;
    va_list marker;

    va_start(marker, n);
    for(k = 0; k < n; k++) {
        long long x = va_arg(marker, long long);
        printf("%lld\n", x);
    }
    va_end(marker); 
}

这是为什么?为什么它可以与long long也?我认为数字整数常量被传递为int他们是否合二为一? (参见上面的链接)那么它怎么可能与long long too?

哎呀,它甚至在交替时也能工作int and long long。这让我很困惑:

void testfunc(int n, ...)
{
    int k;
    va_list marker;

    va_start(marker, n);
    for(k = 0; k < n; k++) {

        if(k & 1) {
            long long x = va_arg(marker, long long);
            printf("B: %lld\n", x);
        } else {
            int x = va_arg(marker, int);
            printf("A: %d\n", x);
        }
    }
    va_end(marker); 
}

怎么会这样?我以为我所有的参数都传递为int……为什么我可以任意来回切换int and long long没有任何麻烦吗?我现在真的很困惑...

感谢您对此的任何启发!


这与 C 无关。只是您使用的系统 (x86-64) 在 64 位寄存器中传递前几个参数,即使对于可变参数也是如此。

本质上,在您使用的体系结构上,编译器生成的代码对每个参数(包括可变参数)使用完整的 64 位寄存器。这是架构上约定的ABI,与C本身无关;所有程序,无论如何生成,都应该遵循其应运行的架构上的 ABI。

如果您使用 Windows,则 x86-64 使用rcx, rdx, r8, and r9按顺序存储前四个(整数或指针)参数,其余的则堆栈。在 Linux、BSD、Mac OS X 和 Solaris 中,x86-64 使用rdi, rsi, rdx, rcx, r8, and r9按顺序存储前六个(整数或指针)参数,其余参数则堆栈。

您可以使用一个简单的示例程序来验证这一点:

extern void func(int n, ...);

void test_int(void)
{
    func(0, 1, 2);
}

void test_long_long(void)
{
    func(0, 1LL, 2LL);
}

如果将以上内容编译为 x86-64 程序集(例如gcc -Wall -O2 -march=x86-64 -mtune=generic -S)在 Linux、BSD、Solaris 或 Mac OS(X 或更高版本)中,您大约会得到(AT&T 语法,源、目标操作数顺序)

test_int:
        movl    $2, %edx
        movl    $1, %esi
        xorl    %edi, %edi
        xorl    %eax, %eax
        jmp     func

test_long_long:
        movl    $2, %edx
        movl    $1, %esi
        xorl    %edi, %edi
        xorl    %eax, %eax
        jmp     func

即功能是相同的,并且不要将参数压入堆栈。注意jmp func相当于call func; ret,就更简单了。

但是,如果您针对 x86 进行编译(-m32 -march=i686 -mtune=generic),你大约得到

test_int:
        subl    $16, %esp
        pushl   $2
        pushl   $1
        pushl   $0
        call    func
        addl    $28, %esp
        ret

test_long_long:
        subl    $24, %esp
        pushl   $0
        pushl   $2
        pushl   $0
        pushl   $1
        pushl   $0
        call    func
        addl    $44, %esp
        ret

这表明 Linux/BSDs/etc 中的 x86 调用约定。涉及在堆栈上传递可变参数,并且int变体将 32 位常量推入堆栈(pushl $x压入 32 位常量x到堆栈),以及long long变体将 64 位常量压入堆栈。

因此,由于您使用的操作系统和架构的底层 ABI,您的可变参数函数显示了您观察到的“异常”。要仅看到您期望从 C 标准获得的行为,您需要解决底层 ABI 怪癖 - 例如,通过使用至少六个参数启动可变参数函数,以占用 x86-64 架构上的寄存器,以便休息,你真正的可变参数,在堆栈上传递。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么我的可变参数函数可以同时使用 int 和 long long ? 的相关文章

  • 向进度条添加百分比文本 C#

    我有一个方法可以显示进程栏何时正在执行以及何时成功完成 我工作得很好 但我想添加一个百分比 如果完成 则显示 100 如果卡在某个地方 则显示更少 我在网上做了一些研究 但我无法适应我正在寻找的解决方案 这是我的代码 private voi
  • 在 C++ 中使用 matlab 结构(matlab 函数调用的返回值)(由 matlab 编译器生成的库)

    你好 我有一个相当简单的 matlab 函数 例如 function MYSTRUCT myfunc MYSTRUCT prop1 test MYSTRUCT prop2 foo MYSTRUCT prop3 42 end 我用 matla
  • 注销租约抛出 InvalidOperationException

    我有一个使用插件的应用程序 我在另一个应用程序域中加载插件 我使用 RemoteHandle 类http www pocketsilicon com post Things That Make My Life Hell Part 1 App
  • 确保 StreamReader 不会挂起等待数据

    下面的代码读取从 tcp 客户端流读取的所有内容 并且在下一次迭代中它将仅位于 Read 上 我假设正在等待数据 我如何确保它不会在没有任何内容可供读取时返回 我是否必须设置低超时 并在失败时响应异常 或者有更好的办法吗 TcpClient
  • 在 DataView 的 RowFilter 中选择 DISTINCT

    我试图根据与另一个表的关系缩小 DataView 中的行范围 我使用的 RowFilter 如下 dv new DataView myDS myTable id IN SELECT DISTINCT parentID FROM myOthe
  • 错误:表达式不产生值

    我尝试将以下 C 代码转换为 VB NET 但在编译代码时出现 表达式不产生值 错误 C Code return Fluently Configure Mappings m gt m FluentMappings AddFromAssemb
  • 如何创建包含 IPv4 地址的文本框? [复制]

    这个问题在这里已经有答案了 如何制作一个这样的文本框 我想所有的用户都见过这个并且知道它的功能 您可以使用带有 Mask 的 MaskedTestBox000 000 000 000 欲了解更多信息 请参阅文档 http msdn micr
  • java.io.Serialized 在 C/C++ 中的等价物是什么?

    C C 的等价物是什么java io Serialized https docs oracle com javase 7 docs api java io Serializable html 有对序列化库的引用 用 C 序列化数据结构 ht
  • 为什么 Google 测试会出现段错误?

    我是 Google Test 的新手 正在尝试提供的示例 我的问题是 当我引入失败并设置GTEST BREAK ON FAILURE 1 或使用命令行选项 GTest 将出现段错误 我正在考虑这个例子 https code google c
  • 使用接口有什么好处?

    使用接口有什么用 我听说它用来代替多重继承 并且还可以用它来完成数据隐藏 还有其他优点吗 哪些地方使用了接口 程序员如何识别需要该接口 有什么区别explicit interface implementation and implicit
  • 由 IHttpClientFactory 注入时模拟 HttpClient 处理程序

    我创建了一个自定义库 它会自动为依赖于特定服务的 Polly 策略设置HttpClient 这是使用以下方法完成的IServiceCollection扩展方法和类型化客户端方法 一个简化的例子 public static IHttpClie
  • 将 Word 文档另存为图像

    我正在使用下面的代码将 Word 文档转换为图像文件 但是图片显得太大 内容不适合 有没有办法渲染图片或将图片保存到合适的尺寸 private void btnConvert Click object sender EventArgs e
  • 具有交替类型的可变参数模板参数包

    我想知道是否可以使用参数包捕获交替参数模式 例如 template
  • 我可以使用 moq Mock 来模拟类而不是接口吗?

    正在经历https github com Moq moq4 wiki Quickstart https github com Moq moq4 wiki Quickstart 我看到它 Mock 一个接口 我的遗留代码中有一个没有接口的类
  • Qt - ubuntu中的串口名称

    我在 Ubuntu 上查找串行端口名称时遇到问题 如您所知 为了在 Windows 上读取串口 我们可以使用以下代码 serial gt setPortName com3 但是当我在 Ubuntu 上编译这段代码时 我无法使用这段代码 se
  • 如何禁用 fread() 中的缓冲?

    我正在使用 fread 和 fwrite 读取和写入套接字 我相信这些函数用于缓冲输入和输出 有什么方法可以在仍然使用这些功能的同时禁用缓冲吗 Edit 我正在构建一个远程桌面应用程序 远程客户端似乎 落后于服务器 我不知道可能是什么原因
  • 外键与独立关系 - Entity Framework 5 有改进吗?

    我读过了several http www ladislavmrnka com 2011 05 foreign key vs independent associations in ef 4 文章和问题 https stackoverflow
  • CMake 无法确定目标的链接器语言

    首先 我查看了this https stackoverflow com questions 11801186 cmake unable to determine linker language with c发帖并找不到解决我的问题的方法 我
  • AES 128 CBC 蒙特卡罗测试

    我正在 AES 128 CBC 上执行 MCT 如中所述http csrc nist gov groups STM cavp documents aes AESAVS pdf http csrc nist gov groups STM ca
  • 不同类型指针之间的减法[重复]

    这个问题在这里已经有答案了 我试图找到两个变量之间的内存距离 具体来说 我需要找到 char 数组和 int 之间的距离 char data 5 int a 0 printf p n p n data 5 a long int distan

随机推荐