当前位置:  开发笔记 > 编程语言 > 正文

在C API中使用varargs来设置键值对是一个好主意吗?

如何解决《在CAPI中使用varargs来设置键值对是一个好主意吗?》经验,为你挑选了3个好方法。

我正在编写一个API来更新结构中的许多不同字段.

我可以通过使更新函数variadic来帮助添加将来的字段:

update(FIELD_NAME1, 10, FIELD_NAME2, 20);

然后添加FIELD_NAME3更改任何现有的调用:

update(FIELD_NAME1, 10, FIELD_NAME2, 20, FIELD_NAME3, 30);

请问智慧的话语?



1> Michael..:

一般来说,没有.

Varargs抛出了很多类型安全 - 你可以传递指针,浮点数等,而不是整数,它将编译没有问题.滥用varargs(例如省略参数)可能会因堆栈损坏或读取无效指针而引入奇怪的崩溃.

例如,以下调用将编译并导致崩溃或其他奇怪的行为:

UpdateField(6, "Field1", 7, "Field2", "Foo");

最初的6是预期的参数数量.它会将字符串指针"Foo"转换为一个int以放入Field2,它将尝试读取和解释其他两个不存在的参数,这可能会导致崩溃,从而解除引用堆栈噪声.

我相信在C语言中实现varargs是一个错误(考虑到今天的环境 - 它可能在1972年完全合理.)实现是你在栈上传递一堆值然后被调用者将在栈中拾取参数,基于关于它对一些初始控制参数的解释.这种类型的实现基本上会让您在可能非常难以诊断的方式中犯错.C#的实现,传递一个带有方法属性的对象数组,只是必须更加理智,尽管不能直接映射到C语言.


然后传递一个已知格式的大字符串.字符串更加系列化,非常便携.你只需要定义一个格式来传递它们 - XMl是一个(虽然重量级),或者只是一个简单的csv会做.

2> paxdiablo..:

我倾向于避免使用varargs,除非在一个非常有用的特定情况下.除了单个函数调用可以完成的任务之外,变量参数并没有真正提供所有好处,尤其是在您的情况下.

在可读性方面(通常我比原始速度更喜欢除了非常特殊的情况),以下两个选项之间没有真正的区别(我已经为varargs版本添加了一个计数,因为你需要一个计数或者哨兵来检测数据的结尾):

update(2, FIELD_NAME1, 10, FIELD_NAME2, 20);
update(3, FIELD_NAME3, 10, FIELD_NAME4, 20, FIELD_NAME5, 30);
/* ========== */
update(FIELD_NAME1, 10);
update(FIELD_NAME2, 20);
update(FIELD_NAME3, 10);
update(FIELD_NAME4, 20);
update(FIELD_NAME5, 30);

事实上,随着varargs版本变得越来越长,无论如何你都需要将其拆分,以便进行格式化:

update(5,
    FIELD_NAME1, 10,
    FIELD_NAME2, 20,
    FIELD_NAME3, 10,
    FIELD_NAME4, 20,
    FIELD_NAME5, 30);

这样做"每个字段名称一次调用"的方式导致函数本身的代码更简单,并且不会降低调用的可读性.此外,它允许编译器正确检测它不能对varargs执行的某些错误,例如不正确的类型或用户提供的计数与实际计数之间的不匹配.

如果你真的必须能够调用一个函数来执行它,我会选择:

void update (char *k1, int v1) {
    ...
}
void update2 (char *k1, int v1, char *k2, int v2) {
    update (k1, v1);
    update (k2, v2);
}
void update3 (char *k1, int v1, char *k2, int v2, char *k3, int v3) {
    update (k1, v1); /* or these two could be a single */
    update (k2, v2); /*   update2 (k1, v1, k2, v2);    */
    update (k3, v3);
}
/* and so on. */

如果您愿意,您甚至可以将更高级别的功能用作宏,而不会丢失类型安全性.

我倾向于使用varargs函数的唯一地方是提供相同的功能printf()- 例如,我偶尔必须编写具有诸如logPrintf()提供相同功能的功能的日志库.在我需要使用它的时候,我想不出任何其他时间在我的长期(我的意思是,长期:-)时间.

顺便说一句,如果你决定使用varargs,我倾向于选择哨兵而不是计数,因为这可以防止在添加字段时出现不匹配.您可能很容易忘记调整计数并最终得到:

update (2, k1, v1, k2, v2, k3, v3);

添加时,这是阴险的,因为它默默地跳过k3/v3,或者:

update (3, k1, v1, k2, v2);

删除时,这对于程序的成功运行几乎肯定是致命的.

有哨兵可以防止这种情况(当然,只要你不忘记哨兵):

update (k1, v1, k2, v2, k3, v3, NULL);



3> Chris Lutz..:

C语言中的varargs的一个问题是你不知道传递了多少个参数,所以你需要将它作为另一个参数:

update(2, FIELD_NAME1, 10, FIELD_NAME2, 20);

update(3, FIELD_NAME1, 10, FIELD_NAME2, 20, FIELD_NAME3, 30);

推荐阅读
oDavid_仔o_880
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有