我可以计算出scanf的参数数量吗?

问题描述

我们知道,最好检查scanf这样的错误:

if(scanf("%d %d %d",&x,&y,&z) != 3) {
    /* Handle error */
}

但是我想知道是否有任何方法可以自动检测它应该是3。

我考虑过的一种方法是分别声明格式字符串,然后解析它。像这样:

const char format[] = "%d %d %d";
size_t n = count(format);
if(scanf(format,&z) != n) {

但是我不知道如何正确实现count。我可以做一些诸如计算%的数量的操作,但这很容易出错。如果没有为此提供的库功能,我怀疑将其正确设置将非常困难。

我考虑过的另一种方法是做这样的事情:

void wrapper(const char *format,...) {
    va_list arg;
    va_start(arg,format);
    size_t n = count(arg);
    int done = __vfscanf_internal (stdin,format,arg,0);
    if(done != n) {
    

但是据我所知,这里无法编写函数countva_arg的规范说:“如果ap中没有更多参数时调用va_arg,则行为未定义。” 因此,我无法循环播放直到NULL或类似的东西。 / p>

第三种方法是查看scanf是否支持将此数字写入这样的变量:

int n = scanf("%<somespecifier>%d %d %d",%c,%x,%y,%z);
if(n != c) {
    /* Handle error */
}

但这似乎不是一个选择。

所以我在这里茫然。有什么方法可以做我想要的吗?

我的最终目标是写一个“安全”版本的scanf,该版本会在失败时退出。像

void safe_scanf(const char *format,...) {
    /* Code */
    if(<wrong number of assignments>) {
        perror("Wrong number of assignments");
        exit(EXIT_FAILURE);

解决方法

您是否考虑过宏?它们支持可变参数的方式有时使它们比常规函数更有用。

下面的代码未经测试,但也许可以帮助您入门。

#define safe_sscanf(fmt,...) {\
const char *p = fmt-1; int n=0;\
while (p=strstr(p+1,"%")) ++n;\
p = fmt-2;\
while (p=strstr(p+2,"%%")) n-=2;\
if (sscanf(fmt,__VA_ARGS__) != std::max(0,n)) exit(1);\
}
,

您可以使用此variadic macro trick来计算传递给函数的参数数量:

#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__,5,4,3,2,1)
#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,N,...) N

此实现最多支持5个参数,但可以轻松扩展。因此,您可以将scanf包装到类似如下的包装器中:

#define scanf_checked(...) scanf(__VA_ARGS__) - VA_NUM_ARGS(__VA_ARGS__) + 1

后跟+ 1,需要从计数中删除格式非可变参数。

假设您传递了正确数量的参数(大多数编译器对此有适当的警告),则必须检查返回值是否为零。此示例使用sscanf_checked+ 2,因为还有一个非可变参数:

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__,...) N

#define scanf_checked(...) scanf(__VA_ARGS__) - VA_NUM_ARGS(__VA_ARGS__) + 1
#define sscanf_checked(...) sscanf(__VA_ARGS__) - VA_NUM_ARGS(__VA_ARGS__) + 2

int main() {

    unsigned u1,u2;

    int i;

    i = sscanf_checked("1 2","%u %u",&u1,&u2);
    assert(i == 0);

    i = sscanf_checked("1",&u2);
    assert(i != 0);

    i = sscanf_checked("1",&u1); // note: UB
    assert(i == 0); // assert may succeed,but compiler warns for too few arguments

    return EXIT_SUCCESS;

}

工作示例here

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...