C 中的可变参数函数

c 中的可变参数函数

介绍

C 始终使用可以接受不同数量参数的函数 – variadic++ 函数 – printf() 是主要示例。最初,C 无法让您可移植地实现自己的可变参数函数。 当函数原型从 C++ 向后移植到 C 时,它包含声明可变参数函数的语法,例如:

int sum_n( 无符号 n, ... );

表示函数 sum_n 需要一个无符号参数,后跟零个或多个其他参数。

使用计数的简单示例

这是 sum_n() 的示例实现,其中我们使用必需的参数来指定后面有多少个参数:

#include  /* 对于 va_*() 宏 */int sum_n( 无符号 n, ... ) {  va_list 参数;  va_start(参数,n);  整数总和=0;  而 (n-->0)    sum += va_arg( args, int );  va_end( 参数 );  返回总和;}

然后你可以这样称呼它:

int r = sum_n( 3, 1, 2, 5 );  // r = 8

请注意,我们使用必需的参数来指定遵循我们自己的约定的参数数量。 编译器不会从所需参数推断出任何含义。 正如我们将在后面的示例中看到的,所需参数可以是任何类型。 可变参数函数配方

任何可变参数函数

必须

采用以下形式:
R f( T1 p1, T2 p2, TN pN, … ) { // … va_list 参数; va_start(参数,pN); // … va_arg( args, T ) … va_end( 参数 ); // …}

那就是你

必须

:

声明一个 va_list 类型的局部变量。 (您可以将其命名为任何您想要的名称,但 args 是约定俗成的。)调用 va_start( args, pN ),其中 pN 是最后一个必需参数的名称。 请注意,它可以是任何类型。要迭代可变参数的值,请为每个参数调用 va_arg( args, T ),其中 T 是其假定类型。 (每次调用的类型 T 可能不同。)返回前调用 va_end( args )。 使用哨兵的另一个简单示例

这是 str_is_any() 的示例实现,其中所需参数是要比较的字符串(“needle”),后面的参数是要比较的字符串(“haystack”)。 参数由 NULL 指针终止:

_Bool str_is_any( char const *needle, … ) { va_list 参数; va_start(参数,针); _Bool 发现 = false; 做 { char const *const hay = va_arg( args, char* ); 如果(干草== NULL) 休息; 发现 = strcmp( 针, 干草 ) == 0; } while (!找到); va_end( 参数 ); 返回发现;}

你可以这样称呼它:

if ( str_is_any( type_str, “struct”, “union”, NULL ) )

      注意事项

可变参数有几个严重的警告:

没有办法要求任何参数都属于特定类型,也没有办法要求所有参数都属于同一类型。无法确定任何参数的类型实际上是什么。因为没有类型信息,仅发生默认参数转换(见下文)。无法知道给出了多少参数。 (尝试访问比给定的参数更多的参数会导致未定义的行为;但是,访问更少的参数是可以的。)在 C23 之前,可变参数函数必须至少有一个必需参数。…必须永远是最后一个。当通过 va_arg() 迭代参数时,给定的类型必须与实际类型匹配。 如果没有,结果是未定义的行为。默认参数转换为:

char、signed char、unsigned char、short 和 unsigned Short 根据需要提升为 int 或 unsigned int。float 提升为 double.数组被转换为指向其第零个元素的指针。函数名称被转换为指向该函数的指针。因此,实现可变参数函数时最重要的两个问题是:

知道参数的数量或何时停止迭代它们。了解他们的类型。sum_n() 实现通过使用必需的参数来指定后面有多少个参数来“解决”第一个问题。 但是,如果你这样做:

int r = sum( 3, 1, 2 ); // 说的是 3,但只有 2

即指定后面有 3 个参数,但参数较少,结果将是未定义的行为。

此外,sum_n() 实现只能

假设

提供的参数是 int 类型。 如果你要这样做:
int r = sum( 3, 1, 2.7, 5 ); // 双精度型,非整数型

即提供一个 double 类型(或任何其他类型)的值,其中需要 int ,结果将是未定义的行为。

str_is_any() 函数通过使用哨兵“​​解决”了第一个问题,因此它不关心有多少个参数。 然而,它仍然只能

假设

提供的参数是字符串并且最后一个参数为NULL。 如果其中任何一个为 false,结果将是未定义的行为。标准 printf() 函数通过使用一个必需参数作为打印内容的格式来“解决”这两个问题:格式中的每个 % 都是一个

转换说明符

并与参数一一对应。 例如,给定:
printf( “x=%d, y=%dn”, x, y );

 printf() 实现扫描格式字符串以查找 % 字符。遇到一个时,它会使用 % 后面的字符指定的类型,通过 va_arg() 获取下一个可变参数的值,例如 %d 指定 int (并以十进制打印)。

但是,就像 sum_n() 一样,如果您提供的参数少于说明符,或者说明符的类型及其关联参数不匹配,结果将是 – 您猜对了 – 未定义的行为。

幸运的是,现代编译器具有有关 printf() 的特定知识(请参阅此处的“格式”),因此可以在参数类型的数量与格式字符串不匹配时发出警告。 然而,对于您自己的功能,通常需要您自己来完成。

关于实现可变参数函数的思考

考虑到所有的警告,可变参数函数是一个好主意吗? 并不真地。 它们的使用是源于 C 最初根本不关心函数参数的 hack,因此像 printf() 和 scanf() 这样的函数利用了这一点。 即使引入 stdarg.h(以及之前的 varargs.h)也只是使实现可变参数函数

可移植

做了最小的努力,但不是.您应该实现自己的可变参数函数吗? 一般来说,不会。 然而,有一个用例可以实现您自己的可变参数函数。

可变参数函数调用其他可变参数函数

在打印许多消息的大型程序中,如果您知道哪一行代码打印了给定的消息,这样您就可以确定打印消息时程序的状态。

例如,在 cdecl 这样的程序中,如果你得到:

c++decl> 解释 int &*p ^13:错误:指向引用的指针非法;你的意思 ”*&”?

您可能想知道该消息是从源代码中的何处打印的。  在许多情况下,您可以只 grep 查找消息文本,但前提是消息文本按字面意思出现在代码中 - 但此消息的情况并非如此。

我为 cdecl 实现了一个调试选项,除其他外,它还打印了错误消息出现的源代码位置:

c++decl> 设置调试c++decl> 解释 int &*p ^13:错误:[c_ast_check.c:2170]指向引用的指针非法;你的意思 ”*&”?

实现这一点的方式是有一个 fl_print_error() 可变参数函数,它是 fprintf() 的包装器,它在调用时接受额外的文件和行参数。  这是(稍微简化的)实现:

void fl_print_error( char const *file, int line, 字符常量*格式,…){ fprintf( stderr, “错误:” ); if ( opt_cdecl_debug != CDECL_DEBUG_NO ) fprintf( stderr, “[%s:%d] “, 文件, 行 ); va_list 参数; va_start(参数,格式); vfprintf(标准错误,格式,参数); va_end( 参数 );}

以及隐藏文件和行的传递的宏:

#define print_error(文件,行,格式,…) fl_print_error( __FILE__, __LINE__, (格式), __VA_ARGS__ )

如果您不知道,printf() 和 fprintf() 有 vprintf() 和 vfprintf() 对应项,它们采用 va_list 参数:

int vprintf( const char *format, va_​​list vlist );int vfprintf( FILE *stream, const char *format, va_​​list vlist );

va_list 参数允许一个可变参数函数将其变量参数传递给另一个函数。

关于 C23 的注释

如前所述,从 C23 开始,可变参数函数不再坚持至少一个必需参数;这就是你可以做的:

void f( … ) { // 没有必需参数 va_list 参数; va_start( 参数 ); // 没有第二个参数 // …

此功能也是从 C++ 向后移植的。

结论

C 中的可变参数函数基本上是一种 hack。 鉴于他们的严重警告,您通常不应该实现自己的,除非它是另一个可变参数函数的包装。

C++ 继承了 C 的可变参数函数,有很多缺点。然而,在 C++ 中,有更好的函数重载、初始化列表和可变参数模板替代方案,可用于实现以类型安全方式接受不同数量参数的函数 — 但这是另一个故事了。

以上就是C 中的可变参数函数的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1455333.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月18日 07:40:33
下一篇 2025年12月17日 04:08:44

相关推荐

  • C++框架在游戏开发中的应用

    c++++ 框架在游戏开发中提供了以下优势:高性能:c++ 的高性能语言特性支持高效的游戏逻辑。可扩展性:模块化结构允许开发者轻松扩展和定制游戏。可移植性:跨平台框架支持游戏在不同设备上运行。但以下缺点也需要注意:学习曲线陡峭:c++ 和框架的学习难度较大。冗长性:某些功能可能需要大量代码,延长开发…

    2025年12月18日
    000
  • C++框架在智能驾驶领域的应用

    c++++框架在智能驾驶领域广泛应用,因为它具有高性能、可靠性和可扩展性。案例包括apollo(百度)、autoware.auto(adl)和carla(epic games)。在开发智能驾驶系统时,应选择合适的框架并遵循以下步骤:1. 选择框架;2. 设计系统架构;3. 编写代码;4. 集成框架功…

    2025年12月18日
    000
  • C++框架社区资源及学习渠道

    在 c++++ 开发中,利用框架可提升开发效率。社区资源包括 cppcon、boost、github、stack overflow 和 c++ forums,可提供议题、库、开源框架和讨论。学习渠道包括书籍、课程、文档、博客、视频教程和实战示例,例如使用 qt 开发 gui 或使用 boost.as…

    2025年12月18日
    000
  • C++框架的未来趋势是什么?

    c++++ 框架未来趋势包括:1. 异步性和并发性:强调效率和响应,通过协程和异步 i/o 库实现;2. 元编程:通过代码操作代码,增强可扩展性和可维护性;3. 模型驱动工程:从抽象模型生成代码,简化系统设计;4. 轻量级框架:为小型应用程序提供快速开发和测试工具。 C++ 框架的未来趋势探索 C+…

    2025年12月18日
    000
  • C++ 框架最佳实践的具体准则

    c++++框架最佳实践指南:利用依赖注入(di)管理对象依赖关系,提高灵活性;遵循面向对象设计原则,充分利用封装、继承和多态;遵守单一职责原则,将类分解为职责明确的模块;避免强耦合,通过接口和抽象类实现松散耦合;妥善处理异常,使用try-catch块和自定义异常;编写文档和进行单元测试,确保框架代码…

    2025年12月18日
    000
  • C++ 框架中扩展性和可定制性的实现

    c++++框架的扩展性和可定制性通过以下方式实现:扩展性:插件系统抽象接口模组化设计可定制性:配置选项钩子函数主题和皮肤 C++ 框架中的扩展性和可定制性实现 介绍 可扩展性和可定制性是现代 C++ 框架中至关重要的特性。它们使开发人员能够创建适用于各种场景的框架,而无需从头开始构建。 立即学习“C…

    2025年12月18日
    000
  • C++框架的生态系统和社区发展趋势如何?

    c++++框架生态系统蓬勃发展,提供广泛的选择以满足应用程序需求,包含流行框架如boost、qt和google test。活跃的社区提供支持和协作。未来趋势包括:模块化和可扩展性、跨平台支持、云计算整合、人工智能和机器学习支持,以及开放生态系统。 C++ 框架生态系统和社区的发展趋势 简介 C++ …

    2025年12月18日
    000
  • 在 C++ 框架中如何测量代码性能?

    使用计时器和剖析器来测量 c++++ 框架中代码性能。1. 使用计时器测量代码块执行时间。2. 使用剖析器查看应用程序不同部分花费时间的方式。实战案例:通过使用计时器和剖析器,可以识别和优化大型 c++ 应用程序中的性能瓶颈。 在 C++ 框架中测量代码性能 测量代码性能对于优化应用程序至关重要。在…

    2025年12月18日
    000
  • 在“for”循环初始化子句中声明多个变量

    介绍 如你所知,C 和 C++ 中 for 语句的语法是:     for ( init-clause ; 条件表达式 ; 迭代表达式 ) 地点: init-clause 用于初始化(准备)循环的开始;condition-expr 在每次迭代之前进行评估:如果为零,则循环退出;iteration-e…

    2025年12月18日
    000
  • C++ 框架中面向性能优化的模式和设计原则

    为了提高性能,c++++ 框架利用已知模式和原则,包括:池模式:预创建对象池,减少创建和销毁开销。单例模式:确保特定类只有一个实例,管理共享资源等。避免深拷贝:仅复制指针或引用,而不是复制所有成员。使用值语义:传递值而不是引用,提高效率。优化内存布局:相关数据成员相邻,提高缓存命中率。 C++ 框架…

    2025年12月18日
    000
  • 如何考虑不同C++框架的社区支持和文档质量?

    选择 c++++ 框架时,考虑社区支持和文档质量对于项目成功至关重要:社区支持:评估 stack overflow 活动、论坛参与度和社交媒体关注度。文档质量:衡量全面性、清晰度、时效性和实用性。案例研究:boost 库:活跃的社区和广泛的文档推动了其持续发展。qt 框架:庞大的在线社区和定期更新的…

    2025年12月18日
    000
  • 如何利用 C++ 框架中的特定优化特性提升性能?

    利用 c++++ 框架的优化特性可提升应用程序性能。具体方式包括:1. 内存优化:使用智能指针管理内存;2. 容器优化:使用不拷贝容器避免拷贝;3. 并发优化:使用线程池管理线程;4. 算法优化:使用高效的算法进行排序和查找。实战案例:图像处理应用程序使用这些优化特性后,通过智能指针管理缓冲区、并行…

    2025年12月18日
    000
  • 如何根据特定行业或领域的需求评估C++框架?

    根据特定行业或领域的需求评估 c++++ 框架:确定行业/领域特定需求。研究 c++ 框架的特性和功能。评估框架的性能、可扩展性、易用性、灵活性和支持。检查实战案例,例如零售、金融和医疗保健。根据评估结果,选择最符合行业/领域特定需求的 c++ 框架。 如何根据特定行业或领域的需求评估 C++ 框架…

    2025年12月18日
    000
  • C++ 框架中常见的性能陷阱和优化误区

    优化 c++++ 框架的性能时需注意以下陷阱:过度使用虚函数会导致动态绑定开销;不当使用智能指针会引入额外开销;不必要的拷贝会浪费时间,可通过引用或移动语义传递数据;内存碎片会降低性能,可使用适当的数据结构和内存管理器。 C++ 框架中的性能陷阱和优化误区 在 C++ 框架中,优化代码以获得最佳性能…

    2025年12月18日
    000
  • C++框架的就业市场前景和人才需求如何?

    c++++ 框架的就业市场前景光明,因其在关键行业中被广泛采用,例如金融和游戏开发。由于高性能计算和人工智能等技术趋势的推动,对 c++ 框架专家的需求很高。最受欢迎的框架包括 boost 和 qt,而经验丰富的专业人员可以在软件工程、架构和技术顾问等领域找到广泛的就业机会。 C++ 框架的就业市场…

    2025年12月18日
    000
  • 在 C++ 框架中实施轻量级设计优化性能的技巧

    通过采用轻量级设计原则,可以显著提升 c++++ 框架中应用程序的性能。具体技巧包括:避免不必要的依赖项:仅包括必需依赖项,减少代码复杂性和大小。优先使用轻量级数据结构:选择哈希表、向量和链表等高效存储和检索数据的轻量级数据结构。使用智能指针:采用独特指针和共享指针等智能指针,避免内存泄漏和提高可靠…

    2025年12月18日
    000
  • C++框架的性能优化和可扩展性如何提升?

    优化 c++++ 框架性能和可扩展性的方法包括:内存优化:使用智能指针和引用计数有效管理内存,避免不必要的数据复制。并行化算法:利用 c++11 及更高版本的并行编程特性,如 std::thread 和 std::mutex。模块化设计:将应用程序分解为独立且易于维护的模块。依赖注入:使用依赖注入框…

    2025年12月18日
    000
  • C++框架性能优化技巧

    在 c++++ 框架中,以下技巧有助于性能优化:减少不必要的内存分配,使用对象池或智能指针。优化内存布局,使相关数据成员相邻放置。利用并行性,如多线程或协程,以提高计算密集型任务的性能。定期进行性能分析以识别瓶颈并进行有针对性优化。这些技巧可显著提升 c++ 框架的性能和应用程序的效率。 C++ 框…

    2025年12月18日
    000
  • C++框架在哪些方面不如Java框架?

    c++++ 框架在内存管理、继承和并发方面不如 java 框架有效。c++ 要求手动内存管理,而 java 具有自动垃圾回收。c++ 使用多继承,可能导致混乱,而 java 使用单继承。c++ 使用原生并发原语,而 java 提供更高级别的并发 api,更易于管理。这些不足导致 c++ 框架更易出现…

    2025年12月18日
    000
  • 如何根据不同C++框架的学习曲线和培训需求做出决策?

    选择合适的 c++++ 框架至关重要,需要考虑其学习曲线和培训需求:学习曲线:浅:易于学习,适合初学者。陡:更复杂,需要大量时间和精力才能掌握。培训需求:低:有丰富的文档和示例,可自学。高:可能需要外部分析或培训才能充分利用。决策因素包括项目规模、时间资源、团队技能等。根据具体情况选择最合适的框架,…

    2025年12月18日
    000

发表回复

登录后才能评论
关注微信