c语言 - 在自己的 fscanf 中使用可变参数函数



所以这里的上下文是我正在开发自己的vfscanf实现。我目前有以下两个函数签名:

int k_fscanf_l(uint32 HA, FILE *stream, int max_length, char *format_tok, int *bytes_moved, void *va_list_item)
int k_vfscanf(uint32 HA, FILE *stream, const char *format, va_list args)

K表示内核,V表示可变长度,L只是我的字符串长度会计变体。

k_fscanf_l是一个辅助功能,可以处理一堆繁重的工作k_vfscanf.就每个函数的具体作用而言:

k_vfscanf采用格式字符串并将其分解为单数标记并单独传递给k_fscanf_l,或者在字符的情况下传递给单独的k_fread函数(不要...问)。在里面k_fscanf_l我打电话给sscanf(lstrPtr, mod_tok, va_list_item, &offset)为我做大部分繁重的工作,除此之外,我自己处理文件的东西。

我可以说的还有很多话来过度描述这一点,但我相信更容易找到问题的症结,这与每个函数中的最后一个参数有关。我的程序目前正在断言从索引到va_list args,所以我想问:

问题:

  1. 像这样索引到va_list args中是严格不正确的:(void *)args[va_list_index]并将其传递给k_fscanf_l
  2. 应该使用预定义的宏而不是 void 指针来实现我想要的吗?
  3. 甚至有可能实现我想要的吗?也就是说,我可以将va_list args划分为多个void *va_list_item指针,由帮助程序函数内部的sscanf填充吗?我必须更改函数签名吗?
  4. 如果我调用va_arg宏,我是否指定了类型,或者是否有(void *)式解决方案?
  5. 如果上面没有回答,在这种情况下你会推荐什么?

我目前对格式字符串进行标记化并在同一循环中调用帮助程序函数,但我可以将它们分成 2 次传递,以严格知道在使用va_list之前我将进行多少次迭代,我只是不知道我是否可以传递单个项目进行填充即使我这样做。

编辑 2:更新代码段以使其更具包容性

else if (tok_len > 1)
{
// Character(s) - Deterministic length and compatibility with byte characters
if (ft_low == 'c')
{
int num_chars;
ret_val = sscanf(formatTok, "%d", &num_chars);
if (ret_val < 1)
num_chars = 1;
k_print_message(HA, NOTE, 0, "vfscanf", "tGot %d characters for specifiern", num_chars);

ret_val = k_fread(HA, va_arg(args, void *), num_chars, 1, stream);
}
// %N case - Does not need to actually call scanning function
else if (ft_low == 'n')
{
int *va_list_count = va_arg(args, int *);
*va_list_count = byte_offset;
}
// Strings - Hard to predict strings and scansets, support max length available for buffer
else if (ft_low == 's' || ft_low == ']')
ret_val = k_fscanf_l(HA, stream, MAX_LSTR_IN, formatTok, &temp_offset, va_arg(args, char *));

// Non-Floating Numbers - Common bases shouldn't reach 16 digits, ever
else if (ft_low == 'i' || ft_low == 'd' || ft_low == 'u' || ft_low == 'o' || ft_low == 'x' || ft_low == 'p')
ret_val = k_fscanf_l(HA, stream, 64, formatTok, &temp_offset, va_arg(args, int *));
// Floating Point Numbers - resolutions should never reach 64 digits, ever
else if (ft_low == 'f' || ft_low == 'e' || ft_low == 'g' || ft_low == 'a')            
ret_val = k_fscanf_l(HA, stream, 64, formatTok, &temp_offset, va_arg(args, float *));
// Updates value
byte_offset += temp_offset;
if (ret_val > 0)
stored_items++;
else if (ret_val < 0)
break;
}
k_print_message(HA, NOTE, 0, "vfscanf", "tGot ft_low: %cn", ft_low);
k_print_message(HA, NOTE, 0, "vfscanf", "tGot ret_val: %dn", ret_val);
// Resets flag
null_flag = 0;
// Deallocates memory
kernel_free(HA, formatTok);
}
// Closes va_list from iteration
va_end(args);
  1. 像这样索引到va_list args是严格不正确的:(void *)args[va_list_index]并将其传递给k_fscanf_l

这样做严格超出了 C 语言规范定义的语义范围。 因此,就C本身而言,这样做的行为是不确定的。 如果您的 C 实现碰巧记录了此类用法的语义,并且您不关心可移植性到其他 C 实现,那么这可能不是您的问题。 否则,这种做法充其量是不明智的。

  1. 应该使用预定义的宏而不是空指针来实现我想要的吗?

va_startva_argva_endva_copy宏是C语言规范记录的处理va_list的唯一方法。 如果您的实现为此目的记录了其他功能,那么考虑使用它们是合理的(从而产生可移植性成本)。 如果不是,那么尝试使用宏以外的任何东西是不明智的。

  1. 甚至有可能实现我想要的吗?也就是说,我可以将va_list args划分为多个void *va_list_item指针吗 由sscanf在辅助函数中填充?我必须更改 函数签名?

与可变参数处理的细节无关,如果您的函数要支持 POSIX 样式的%n$输入指令,则无法执行此类拆分,因为这些指令的显示顺序与相应指针的出现顺序脱节。 但这可能不是你关心的问题。

我还认为,尝试使用sscanf来支持从文件扫描数据会带来一些与参数解释完全分开的重大挑战。 但我想你已经整理好了那部分。

就从va_list获取目标指针而言,您确实具有以下优势:对于对此特定函数的有效调用,所有变量参数都必须是对象指针。 如果您可以依赖所有指向对象的指针类型来具有相同的表示形式,那么您可能可以通过一系列评估(例如......

void *va_list_item = va_arg(args, void *);

这可能足以满足您的目的。但是,当实际参数既不是指向void的指针也不是指向字符类型的指针时,C 明确没有定义该方法的行为,这充其量是一种不舒服的情况。

如果要严格遵循 C,则必须至少解析格式字符串以确定每个参数的正确(指针)类型,并使用该类型从va_list中提取参数。 这样做后,您可以安全地将结果转换为类型void *以传递给您的帮助程序函数,并且此类转换甚至是自动的。

  1. 如果我调用va_arg宏,我是否指定了类型,或者是否有(void *)式解决方案?

见上文。

  1. 如果上面没有回答,在这种情况下你会推荐什么?

我是严格一致性的粉丝,所以一般来说,我建议使用它们的正确类型提取变量参数。 这提供了最不愉快的意外的可能性,尽管这确实意味着您将不得不重复sscanf已经必须完成的一些工作。

另一方面,如果项目有某种通用开发策略,可以通过将对象指针类型视为可互换(而不仅仅是可互换)来打破严格的别名,那么通过假装变量参数都是类型void *来依赖该策略几乎没有额外的危害。

相关内容

  • 没有找到相关文章

最新更新