C 语言变长参数及其陷阱

使用 C 风格的变长参数列表访问参数并不安全。这种方法存在几个风险,从 printInts() 函数可以看出。

使用 C 风格的变长参数列表访问参数并不安全。这种方法存在几个风险,从 printInts() 函数可以看出。

C 语言变长参数及其陷阱

C 工具

变长参数列表

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

考虑 C 函数printf(),来自<cstdio>。你可以用任意数量的参数调用它:

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()一样,这个函数应该能够打印具有任意数量和任意类型参数的字符串。一个简单的实现如下:

#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(),以确保函数以一致的堆栈状态结束。你可以如下方式使用该函数:

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。有许多方法,但它们都对程序员来说是繁琐的。

下面的示例演示了调用者在第一个命名参数中指定提供了多少个参数的技术。该函数接受任意数量的int并打印出来:

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()。请注意,第一个参数指定将跟随多少个整数。

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

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

访问风险

使用 C 风格的变长参数列表访问参数并不安全。这种方法存在几个风险,从printInts()函数可以看出:

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

警告:避免使用 C 风格的变长参数列表。建议传递一个std::array或vector的值、使用初始化列表,或者使用类型安全的变参模板来实现变长参数列表。

©本文为清一色官方代发,观点仅代表作者本人,与清一色无关。清一色对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。本文不作为投资理财建议,请读者仅作参考,并请自行承担全部责任。文中部分文字/图片/视频/音频等来源于网络,如侵犯到著作权人的权利,请与我们联系(微信/QQ:1074760229)。转载请注明出处:清一色财经

(0)
打赏 微信扫码打赏 微信扫码打赏 支付宝扫码打赏 支付宝扫码打赏
清一色的头像清一色管理团队
上一篇 2023年12月5日 17:07
下一篇 2023年12月5日 17:07

相关推荐

发表评论

登录后才能评论

联系我们

在线咨询:1643011589-QQbutton

手机:13798586780

QQ/微信:1074760229

QQ群:551893940

工作时间:工作日9:00-18:00,节假日休息

关注微信