如何检测通过省略号传递的参数的大小?

2023-12-25

以下代码按预期工作GCC https://godbolt.org/z/bbcdfj and Clang https://godbolt.org/z/WPc79z,但是,我相信它包含未定义的行为。它起作用的可能原因是省略号中的参数在堆栈中以 64 位大小的块传递,即使提取了 32 位大小的值也是如此。

#include <stdarg.h>                                                                           
#include <stdio.h>                                                                            
#include <stdint.h>                                                                           
#include <inttypes.h>                                                                         
                                                                                              
void foo(int count, ...) {                                                                    
        va_list args;                                                                         
        va_start(args, count);                                                                
        for (int i = 0; i < count; i++) {                                                     
                uint32_t x = va_arg(args, uint32_t);                                          
                printf("%"PRIu32" ", x);                                                      
        }                                                                                     
        printf("\n");                                                                         
        va_end(args);                                                                         
}                                                                                             
                                                                                              
void bar(int count, ...) {                                                                    
        va_list args;                                                                         
        va_start(args, count);                                                                
        for (int i = 0; i < count; i++) {                                                     
                uint64_t x = va_arg(args, uint64_t);                                          
                printf("%"PRIu64" ", x);                                                      
        }                                                                                     
        printf("\n");                                                                         
        va_end(args);                                                                         
}                                                                                             
                                                                                              
int main() {                                                                                  
        uint32_t a = 1, b = (uint32_t)10000000000, c = (uint32_t)-1;                          
        uint64_t x = 1, y = 10000000000, z = (uint64_t)-1;                                    
        foo(3, a, b, c);                                                                      
        foo(3, x, y, z); // UB                                                                     
        bar(3, a, b, c); // UB                                                                
        bar(3, x, y, z);                                                                                                                     
}

Output:

1 1410065408 4294967295 // ok                                                            
1 1410065408 4294967295 // ok                                                           
1 1410065408 4294967295 // ok                                                          
1 10000000000 18446744073709551615 // ok 

如果某些架构使用 32 位偏移量来实现函数中的堆栈foo,那么当传递 64 位值时,其结果将不正确。在这种情况下,输出可能如下所示:

1 1410065408 4294967295 // ok
1 0 1410065408 // incorrect
6056184812580896769 12884901887 18446744073709551615 // incorrect
1 10000000000 18446744073709551615 // ok

如何正确实现单个函数而不是两个函数foo and bar并能够使用 32 位和 64 位参数调用它?


是的,OP的代码有UB。

解决更高层次的问题:

如何检测通过省略号传递的参数的大小?

如何正确实现单个函数而不是两个函数 foo 和 bar 并能够使用 32 位和 64 位参数调用它?

Use _Generic自动区分类型参数并为其添加前缀。

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

#define GP(X) _Generic((X), \
      uint32_t: 32, \
      uint64_t: 64 \
      ),(X)

void foobar(int count, ...) {
  va_list args;
  va_start(args, count);
  for (int i = 0; i < count; i++) {
    int kind = va_arg(args, int);
    switch (kind) {
      case 32: {
        uint32_t x = va_arg(args, uint32_t);
        printf("%"PRIu32" ", x);
        break;
      }
      case 64: {
        uint64_t x = va_arg(args, uint64_t);
        printf("%"PRIu64" ", x);
        break;
      }
    }
  }
  printf("\n");
  va_end(args);
}

int main(void) {
  uint32_t a = 1, b = (uint32_t) 10000000000, c = (uint32_t) -1;
  uint64_t x = 1, y = 10000000000, z = (uint64_t) -1;
  foobar(3, GP(a), GP(b), GP(c));
  foobar(3, GP(x), GP(y), GP(z));
  foobar(3, GP(a), GP(b), GP(c));
  foobar(3, GP(x), GP(y), GP(z));
}

Output

1 1410065408 4294967295 
1 10000000000 18446744073709551615 
1 1410065408 4294967295 
1 10000000000 18446744073709551615 

这进一步探讨在
格式化打印,无需使用 _Generic 指定类型匹配说明符 https://codereview.stackexchange.com/q/115143/29485.

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

如何检测通过省略号传递的参数的大小? 的相关文章

  • EF Core Group By 翻译支持条件总和

    听说 EF Core 2 1 将支持翻译小组 我感到非常兴奋 我下载了预览版并开始测试它 但发现我在很多地方仍然没有得到翻译分组 在下面的代码片段中 对 TotalFlagCases 的查询将阻止翻译分组工作 无论如何 我可以重写这个以便我
  • 为什么 C# Array.BinarySearch 这么快?

    我已经实施了一个很简单用于在整数数组中查找整数的 C 中的 binarySearch 实现 二分查找 static int binarySearch int arr int i int low 0 high arr Length 1 mid
  • 在哪里可以找到列出 SSE 内在函数操作的官方参考资料?

    是否有官方参考列出了 GCC 的 SSE 内部函数的操作 即 头文件中的函数 除了 Intel 的 vol 2 PDF 手册外 还有一个在线内在指南 https www intel com content www us en docs in
  • ASP.NET MVC:这个业务逻辑应该放在哪里?

    我正在开发我的第一个真正的 MVC 应用程序 并尝试遵循一般的 OOP 最佳实践 我正在将控制器中的一些简单业务逻辑重构到我的域模型中 我最近一直在阅读一些内容 很明显我应该将逻辑放在域模型实体类中的某个位置 以避免出现 贫血域模型 反模式
  • Asp.NET WebApi 中类似文件名称的路由

    是否可以在 ASP NET Web API 路由配置中添加一条路由 以允许处理看起来有点像文件名的 URL 我尝试添加以下条目WebApiConfig Register 但这不起作用 使用 URIapi foo 0de7ebfa 3a55
  • 类模板参数推导 - clang 和 gcc 不同

    下面的代码使用 gcc 编译 但不使用 clang 编译 https godbolt org z ttqGuL template
  • 从Web API同步调用外部api

    我需要从我的 Web API 2 控制器调用外部 api 类似于此处的要求 使用 HttpClient 从 Web API 操作调用外部 HTTP 服务 https stackoverflow com questions 13222998
  • HTTPWebResponse 响应字符串被截断

    应用程序正在与 REST 服务通信 Fiddler 显示作为 Apps 响应传入的完整良好 XML 响应 该应用程序位于法属波利尼西亚 在新西兰也有一个相同的副本 因此主要嫌疑人似乎在编码 但我们已经检查过 但空手而归 查看流读取器的输出字
  • 在 ASP.NET 5 中使用 DI 调用构造函数时解决依赖关系

    Web 上似乎充斥着如何在 ASP NET 5 中使用 DI 的示例 但没有一个示例显示如何调用构造函数并解决依赖关系 以下只是众多案例之一 http social technet microsoft com wiki contents a
  • C++ OpenSSL 导出私钥

    到目前为止 我成功地使用了 SSL 但遇到了令人困惑的障碍 我生成了 RSA 密钥对 之前使用 PEM write bio RSAPrivateKey 来导出它们 然而 手册页声称该格式已经过时 实际上它看起来与通常的 PEM 格式不同 相
  • 创建链表而不将节点声明为指针

    我已经在谷歌和一些教科书上搜索了很长一段时间 我似乎无法理解为什么在构建链表时 节点需要是指针 例如 如果我有一个节点定义为 typedef struct Node int value struct Node next Node 为什么为了
  • 控件的命名约定[重复]

    这个问题在这里已经有答案了 Microsoft 在其网站上提供了命名指南 here http msdn microsoft com en us library xzf533w0 VS 71 aspx 我还有 框架设计指南 一书 我找不到有关
  • 垃圾收集器是否在单独的进程中运行?

    垃圾收集器是否在单独的进程中启动 例如 如果我们尝试测量某段代码所花费的进程时间 并且在此期间垃圾收集器开始收集 它会在新进程上启动还是在同一进程中启动 它的工作原理如下吗 Code Process 1 gt Garbage Collect
  • 向现有 TCP 和 UDP 代码添加 SSL 支持?

    这是我的问题 现在我有一个 Linux 服务器应用程序 使用 C gcc 编写 它与 Windows C 客户端应用程序 Visual Studio 9 Qt 4 5 进行通信 是什么very在不完全破坏现有协议的情况下向双方添加 SSL
  • 为什么编译时浮点计算可能不会得到与运行时计算相同的结果?

    In the speaker mentioned Compile time floating point calculations might not have the same results as runtime calculation
  • 如何在Xamarin中删除ViewTreeObserver?

    假设我需要获取并设置视图的高度 在 Android 中 众所周知 只有在绘制视图之后才能获取视图高度 如果您使用 Java 有很多答案 最著名的方法之一如下 取自这个答案 https stackoverflow com a 24035591
  • 基于 OpenCV 边缘的物体检测 C++

    我有一个应用程序 我必须检测场景中某些项目的存在 这些项目可以旋转并稍微缩放 更大或更小 我尝试过使用关键点检测器 但它们不够快且不够准确 因此 我决定首先使用 Canny 或更快的边缘检测算法 检测模板和搜索区域中的边缘 然后匹配边缘以查
  • 测试用例执行完成后,无论是否通过,如何将测试用例结果保存在变量中?

    我正在使用 NUNIT 在 Visual Studio 中使用 Selenium WebDriver 测试用例的代码是 我想在执行测试用例后立即在变量中记录测试用例通过或失败的情况 我怎样才能实现这一点 NUnit 假设您使用 NUnit
  • 哪种 C 数据类型可以表示 40 位二进制数?

    我需要表示一个40位的二进制数 应该使用哪种 C 数据类型来处理这个问题 如果您使用的是 C99 或 C11 兼容编译器 则使用int least64 t以获得最大的兼容性 或者 如果您想要无符号类型 uint least64 t 这些都定
  • C++ 中类级 new 删除运算符的线程安全

    我在我的一门课程中重新实现了新 删除运算符 现在我正在使我的代码成为多线程 并想了解这些运算符是否也需要线程安全 我在某处读到 Visual Studio 中默认的 new delete 运算符是线程安全的 但这对于我的类的自定义 new

随机推荐