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

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(使用前将#替换为@)

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

  • 具有子列表属性映射问题的自动映射器

    我有以下型号 Models public class Dish Required public Int64 ID get set Required public string Name get set Required public str
  • 进程何时获得 SIGABRT(信号 6)?

    C 中进程获得 SIGABRT 的场景有哪些 该信号是否始终来自进程内部 或者该信号可以从一个进程发送到另一个进程吗 有没有办法识别哪个进程正在发送该信号 abort 向调用进程发送SIGABRT信号 就是这样abort 基本上有效 abo
  • OpenCv读/写视频色差

    我试图简单地使用 openCV 打开视频 处理帧并将处理后的帧写入新的视频文件 我的问题是 即使我根本不处理帧 只是打开视频 使用 VideoCapture 读取帧并使用 VideoWriter 将它们写入新文件 输出文件看起来比输入更 绿
  • 在搜索 List 时,为什么 Enumerable.Any(Func predicate) 比带有 if 语句的 foreach 慢

    最近有件事引起了我的好奇心 Why is the Enumerable Any Func
  • 迭代变量并查找特定类型实例的技术

    我想迭代进程中内存中的变量 通过插件动态加载 并查找特定类型的实例 以前我可以找到特定类型 或内存中的所有类型 我可以创建类型的实例 我可以获取作为不同类型的字段包含的实例 但我无论如何都不知道只是 搜索 特定类型的实例 一种方法是使用 W
  • 为什么我不能用 `= delete;` 声明纯虚函数?

    Intro 纯虚函数使用通用语法声明 virtual f 0 然而 自 c 11 以来 有一种方法可以显式地传达non existence 特殊 成员函数的 Mystruct delete eg default constructor Q
  • 为什么大多数 C 开发人员使用 Define 而不是 const? [复制]

    这个问题在这里已经有答案了 在许多程序中 define与常量具有相同的用途 例如 define FIELD WIDTH 10 const int fieldWidth 10 我通常认为第一种形式优于另一种形式 它依赖于预处理器来处理基本上是
  • 32 位应用程序的特征最大矩阵大小

    所以 我正在寻找Eigen http eigen tuxfamily org index php title Main Page当我尝试声明大于 10000x10000 的矩阵时 包崩溃 我需要声明一个像这样的矩阵 可靠地大约有 13000
  • 为什么要序列化对象需要 Serialized 属性

    根据我的理解 SerializedAttribute 不提供编译时检查 因为它都是在运行时完成的 如果是这样 那么为什么需要将类标记为可序列化呢 难道序列化器不能尝试序列化一个对象然后失败吗 这不就是它现在所做的吗 当某些东西被标记时 它会
  • 如何使用recv()检测客户端是否仍然连接(并且没有挂起)?

    我写了一个多客户端服务器程序C on SuSE Linux 企业服务器 12 3 x86 64 我为每个客户端使用一个线程来接收数据 我的问题是 我使用一个终端来运行服务器 并使用其他几个终端来运行服务器telnet到我的服务器 作为客户端
  • 访问者和模板化虚拟方法

    在一个典型的实现中Visitor模式 该类必须考虑基类的所有变体 后代 在许多情况下 访问者中的相同方法内容应用于不同的方法 在这种情况下 模板化的虚拟方法是理想的选择 但目前这是不允许的 那么 模板化方法可以用来解析父类的虚方法吗 鉴于
  • 如何将“外部模板”与由同一类中的模板化成员使用的嵌套类一起使用?

    首先 一些背景信息 我尝试以 Herb Sutter 在他的解决方案中介绍的方式使用 Pimpl 习语 得到了 101 http herbsutter com gotw 101 这在头文件中看起来像这样 include pimpl h h
  • 无法将类型“System.IO.Stream”隐式转换为“Java.IO.InputStream”

    我提到了一些类似的问题 但没有一个涉及IO 当我使用时 我在java中使用了相同的代码Eclipse 那次就成功了 但现在我尝试在中使用这段代码Mono for Android C 它不起作用 我正在尝试运行此代码来创建一个InputStr
  • 生产代码中的 LRU 实现

    我有一些 C 代码 需要使用 LRU 技术实现缓存替换 目前我知道两种实现LRU缓存替换的方法 每次访问缓存数据时使用时间戳 最后比较替换时的时间戳 使用缓存项的堆栈 如果最近访问过它们 则将它们移动到顶部 因此最后底部将包含 LRU 候选
  • 为什么我不应该对不是由 malloc() 分配的变量调用 free() ?

    我在某处读到 使用它是灾难性的free删除不是通过调用创建的对象malloc 这是真的 为什么 这是未定义的行为 永远不要尝试它 让我们看看当您尝试时会发生什么free 自动变量 堆管理器必须推断出如何获取内存块的所有权 为此 它要么必须使
  • System.Runtime.InteropServices.COMException(0x80040154):[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我在 C 项目中遇到异常 System Runtime InteropServices COMException 0x80040154 检
  • strcmp 给出分段错误[重复]

    这个问题在这里已经有答案了 这是我的代码给出分段错误 include
  • 我在在线程序挑战编译器中遇到演示错误

    include
  • WinRT 定时注销

    我正在开发一个 WinRT 应用程序 要求之一是应用程序应具有 定时注销 功能 这意味着在任何屏幕上 如果应用程序空闲了 10 分钟 应用程序应该注销并导航回主屏幕 显然 执行此操作的强力方法是在每个页面的每个网格上连接指针按下事件 并在触
  • Googletest:如何异步运行测试?

    考虑到一个包含数千个测试的大型项目 其中一些测试需要几分钟才能完成 如果按顺序执行 整套测试需要一个多小时才能完成 通过并行执行测试可以减少测试时间 据我所知 没有办法直接从 googletest mock 做到这一点 就像 async选项

随机推荐