【问题标题】:terminfo parameterized string %d encoding behaviorterminfo 参数化字符串 %d 编码行为
【发布时间】:2017-11-19 02:06:16
【问题描述】:

我正在尝试了解在 terminfo 的参数化字符串解析器中 %d 编码的行为。相关的手册页是 here 并指出 -

%[[:]flags][width[.precision]][doxXs]
        as  in  printf, flags are [-+#] and space.  Use a ":" to allow the
        next character to be a "-" flag, avoiding interpreting "%-" as  an
        operator.

但没有说明从哪里打印值以及如何处理边缘情况。它们是来自堆栈还是来自传递给参数化字符串的参数?另外,当传递额外的参数(参数化字符串中不等于%d)或存在额外的%d(参数化字符串不正确?)时会发生什么?那是未定义的行为或实现定义或在某处定义定义?

我试图通过手动编写一些有效和无效的字符串并验证输出来检查某些情况,但到目前为止一切都有些不一致,所以我在这里看不到模式 -

#include <iostream>
#include <curses.h>
#include <term.h>

using namespace std;

int main() {
    // single %d prints single argument
    // => 2
    auto res = tparm("[%d]", 2);
    cout << res << endl;

    // single %d prints single argument and ignores additional
    // => 2
    res = tparm("[%d]", 2, 3, 4);
    cout << res << endl;

    // multiple %d prints 0 for absent additional arguments
    // => 2-0-0-0
    res = tparm("[%d-%d-%d-%d]", 2);
    cout << res << endl;

    // multiple %d prints with equal number of arguments prints
    // first two correctly and rest 0
    // => 2-3-0-0-0
    res = tparm("[%d-%d-%d-%d-%d]", 2,3,4,5,6);
    cout << res << endl;

    // single value pushed to stack prints from stack
    // => 2
    res = tparm("[%p1%d]", 2);
    cout << res << endl;

    // single value pushed to stack prints from stack and ignores extra arguments
    // => 2
    res = tparm("[%p1%d]", 2,3,4);
    cout << res << endl;

    // single value pushed to stack prints from stack and additional prints are 0
    // if no arguments are provided
    // => 2-0-0
    res = tparm("[%p1%d-%d-%d]", 2);
    cout << res << endl;

    // single value pushed to stack prints from stack and additional prints 0
    // even if equal arguments are provided
    // => 2-0-0
    res = tparm("[%p1%d-%d-%d]", 2,3,4);
    cout << res << endl;

    // single value pushed to stack prints from stack after pop()?
    // => 100-<garbage>
    res = tparm("[%p1%d-%c]", 100);
    cout << res << endl;

    // pushed to stack via {} and equal arguments provided, prints all
    // => 2-1-100-200
    res = tparm("[%{1}%{2}%d-%d-%d-%d]", 100, 200);
    cout << res << endl;

    // pushed to stack via {} and %p1 equal arguments provided
    // prints only stack and rest 0
    // => 100-2-1-0
    res = tparm("[%{1}%{2}%p1%d-%d-%d-%d]", 100, 200);
    cout << res << endl;
}

【问题讨论】:

  • 它是C还是C++?
  • “未定义的行为”和“实现定义的行为”有什么区别?
  • @ScottHunter 无关紧要,因为两种语言都可以使用 tparm()。我写了未定义的行为,因为我在某些参数组合上遇到了 ncurses 的分段错误.. 字符串但是当我在其他一些实现(其他语言的另一个库)上尝试相同的事情时,它给出了完全不同的结果(因此它可能是错误的或正在使其实现定义的行为)。

标签: c++ c ncurses curses terminfo


【解决方案1】:

您的示例中指出的一个问题是它们执行未定义的行为。 terminfo 的 defined 行为使用诸如%p1 之类的显式参数标记将推送参数传递到堆栈中,它们可以由诸如%d 之类的运算符弹出。缺少这一点,您将依赖 ncurses 的 termcap 解决方法(它没有参数标记),并且临时生成一个类似

的表达式
    res = tparm("[%d-%d-%d-%d]", 2);

尝试从参数列表中读取多个参数。您的示例给出了 一个,因此您处于 C 语言 未定义行为的领域(即,可变长度参数列表中的参数数量错误)。如果您的调用传递了额外的参数,那 可能 没问题(例如参见 Visual C accepting wrong number of arguments?),但如果使用较少的参数,结果可能是访问参数列表外部的内存.

回复评论: ncurses 允许在没有%p1 的情况下以termcap 样式使用%d。但它会计算参数的数量,在进行实际替换之前列出这些参数。由于它将这些作为可变长度参数列表进行处理,因此它无法确定您的应用程序是否为给定字符串传递了错误数量的参数。

进一步阅读:

/* * 分析字符串,看看我们需要多少可变参数列表中的参数, * 以及它们的类型是什么。我们只会接受字符串参数,如果他们 * 在显式参数引用之后显示为 %l 或 %s 格式(例如, * %p2%s)。所有其他参数都是数字。 * * 'number' 粗略计算我们在字符串中看到的 pop 的数量,并且 * 'popcount' 显示字符串中的最高参数编号。我们想 * 简单地使用后一个计数,但如果我们正在读取 termcap 字符串,则 * 可能是我们看不到显式参数编号的情况。 */

以及诸如 tc_BUMP 之类的功能,这些功能可以适应 termcap 缺少参数标记的情况。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多