当前位置:首页 > 科技  > 软件

C 语言变长参数及其陷阱

来源: 责编: 时间:2023-12-05 09:25:47 406观看
导读C 工具变长参数列表这部分解释了旧的 C 风格变长参数列表。了解这些内容很重要,因为你可能会在遗留代码中遇到它们。然而,在新代码中,你应该使用变参模板来实现类型安全的变长参数列表。考虑 C 函数 printf(),来自 <cstdi

olI28资讯网——每日最新资讯28at.com

C 工具

变长参数列表

这部分解释了旧的 C 风格变长参数列表。了解这些内容很重要,因为你可能会在遗留代码中遇到它们。然而,在新代码中,你应该使用变参模板来实现类型安全的变长参数列表。olI28资讯网——每日最新资讯28at.com

考虑 C 函数 printf(),来自 <cstdio>。你可以用任意数量的参数调用它:olI28资讯网——每日最新资讯28at.com

printf("int %d/n", 5);printf("String %s and int %d/n", "hello", 5);printf("Many ints: %d, %d, %d, %d, %d/n", 1, 2, 3, 4, 5);

C/C++ 提供了语法和一些实用宏,用于编写你自己的变长参数函数。这些函数通常看起来很像 printf()。尽管你不经常需要这个特性,但偶尔你会遇到它相当有用的情况。例如,假设你想编写一个快速而简单的调试函数,当设置了调试标志时,该函数将字符串打印到 stderr,但如果没有设置调试标志,则不执行任何操作。就像 printf() 一样,这个函数应该能够打印具有任意数量和任意类型参数的字符串。一个简单的实现如下:olI28资讯网——每日最新资讯28at.com

#include <cstdio>#include <cstdarg>bool debug { false };void debugOut(const char* str, ...) {    va_list ap;    if (debug) {        va_start(ap, str);        vfprintf(stderr, str, ap);        va_end(ap);    }}

首先,请注意 debugOut() 的原型包含一个类型化且命名的参数 str,后面跟着 ...(省略号)。它们代表任意数量和类型的参数。要访问这些参数,你必须使用 <cstdarg> 中定义的宏。你声明一个 va_list 类型的变量,并用 va_start 调用进行初始化。va_start() 的第二个参数必须是参数列表中最右边的命名变量。所有具有变长参数列表的函数都至少需要一个命名参数。debugOut() 函数简单地将这个列表传递给 vfprintf()(<cstdio> 中的标准函数)。vfprintf() 调用返回后,debugOut() 调用 va_end() 来终止访问变量参数列表。在调用 va_start() 后,你必须始终调用 va_end(),以确保函数以一致的堆栈状态结束。你可以如下方式使用该函数:olI28资讯网——每日最新资讯28at.com

debug = true;debugOut("int %d/n", 5);debugOut("String %s and int %d/n", "hello", 5);debugOut("Many ints: %d, %d, %d, %d, %d/n", 1, 2, 3, 4, 5);

访问参数

如果你想自己访问实际参数,你可以使用 va_arg() 来做到这一点。它接受 va_list 作为第一个参数,以及要解释的参数的类型。不幸的是,除非你提供明确的方式,否则无法知道参数列表的结尾。例如,你可以使第一个参数是参数数量的计数。或者,在你有一组指针的情况下,你可能需要最后一个指针是 nullptr。有许多方法,但它们都对程序员来说是繁琐的。olI28资讯网——每日最新资讯28at.com

下面的示例演示了调用者在第一个命名参数中指定提供了多少个参数的技术。该函数接受任意数量的 int 并打印出来:olI28资讯网——每日最新资讯28at.com

void printInts(size_t num, ...) {    va_list ap;    va_start(ap, num);    for (size_t i { 0 }; i < num; ++i) {        int temp { va_arg(ap, int) };        cout << temp << " ";    }    va_end(ap);    cout << endl;}

你可以按以下方式调用 printInts()。请注意,第一个参数指定将跟随多少个整数。olI28资讯网——每日最新资讯28at.com

printInts(5, 5, 4, 3, 2, 1);

为什么不应使用 C 风格的变长参数列表

访问风险

使用 C 风格的变长参数列表访问参数并不安全。这种方法存在几个风险,从 printInts() 函数可以看出:olI28资讯网——每日最新资讯28at.com

  • 不知道参数的数量:在 printInts() 的情况下,你必须信任调用者作为第一个参数传递正确数量的参数。在 debugOut() 的情况下,你必须信任调用者在字符数组后传递的参数数量与字符数组中的格式化代码数量相同。
  • 不知道参数的类型:va_arg() 接受一个类型,用它来解释其当前位置的值。然而,你可以告诉 va_arg() 将值解释为任何类型。它无法验证正确的类型。

警告:避免使用 C 风格的变长参数列表。建议传递一个 std::array 或 vector 的值、使用初始化列表,或者使用类型安全的变参模板来实现变长参数列表。olI28资讯网——每日最新资讯28at.com

olI28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-38135-0.htmlC 语言变长参数及其陷阱

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 一次性讲清楚「连接池获取连接慢」的所有原因|

下一篇: 三分钟搞懂CUDA和GPU编程

标签:
  • 热门焦点
  • 卢伟冰长文解析K60至尊版 对Redmi有着里程碑式的意义

    在今天的Redmi后性能时代战略发布会结束之后,Redmi总经理卢伟冰又带来了一篇长文,详解了为什么 Redmi 要开启后性能时代?为什么选择和 MediaTek、Pixelworks 深度合作?以及后性
  • 6月安卓手机好评榜:魅族20 Pro蝉联冠军

    性能榜和性价比榜之后,我们来看最后的安卓手机好评榜,数据来源安兔兔评测,收集时间2023年6月1日至6月30日,仅限国内市场。第一名:魅族20 Pro好评率:95%5月份的时候魅族20 Pro就是
  • 6月安卓手机性能榜:vivo/iQOO霸占旗舰排行榜前三

    2023年上半年已经正式过去了,我们也迎来了安兔兔V10版本,在新的骁龙8Gen3和天玑9300发布之前,性能榜的榜单大体会以骁龙8Gen2和天玑9200+为主,至于那颗3.36GHz的骁龙8Gen2领先
  • 容量越大越不坏?24万块硬盘故障率报告公布 这些产品零故障

    8月5日消息,云存储服务商Backblaze发布了最新的硬盘故障率报告,年故障率有所上升。Backblaze发布的硬盘季度统计数据,其中包括故障率等重要方面。这些结
  • 十个可以手动编写的 JavaScript 数组 API

    JavaScript 中有很多API,使用得当,会很方便,省力不少。 你知道它的原理吗? 今天这篇文章,我们将对它们进行一次小总结。现在开始吧。1.forEach()forEach()用于遍历数组接收一参
  • 梁柱接棒两年,腾讯音乐闯出新路子

    文丨田静 出品丨牛刀财经(niudaocaijing)7月5日,企鹅FM发布官方公告称由于业务调整,将于9月6日正式停止运营,这意味着腾讯音乐长音频业务走向消亡。腾讯在长音频领域还在摸索。为
  • 当家的盒马,加速谋生

    来源 | 价值星球Planet作者 | 归去来自己&ldquo;当家&rdquo;的盒马,开始加速谋生了。据盒马官微消息,盒马计划今年开放生鲜供应链,将其生鲜商品送往食堂。目前,盒马在上海已经与
  • 8月见!小米MIX Fold 3获得3C认证:支持67W快充

    这段时间以来,包括三星、一加、荣耀等等有不少品牌旗下的最新折叠屏旗舰都得到了不少爆料,而小米新一代折叠屏旗舰——小米MIX Fold 3此前也屡屡被传
  • 北京:科技教育体验基地开始登记

      北京“科技馆之城”科技教育体验基地登记和认证工作日前启动。首批北京科技教育体验基地拟于2023年全国科普日期间挂牌,后续还将开展常态化登记。  北京科技教育体验基
Top