【问题标题】:In C, is there a way to cast a void pointer to a defined size type?在 C 中,有没有办法将 void 指针转换为定义的大小类型?
【发布时间】:2021-11-18 03:34:51
【问题描述】:

我需要一个“通用”scanf 函数来验证输入,同时为指令和错误打印一条消息。Scan 是一个接受 void 指针的函数,它是将存储输入值、类型说明符和消息的变量。代码如下所示:

void scan(char specifier, void *dest, string instruction, string error) {
bool success = true;
do {
    if(!success) {
        while (getchar()!='\n');
        perror(error);
    }
    puts(instruction);
    switch (specifier)
    {
    case 'd':
        success = scanf(" %d", (int *)dest);
        break;
        
    case 's’:
        // In this particular program I need the strings to be 30 bytes with spaces.
        success = scanf(“ %29[^\n]s", (char *)dest);
        break;

    case 'h':
        success = scanf(" %hd", (short *)dest);
        break;

    case 'f':
        success = scanf(" %f", (float *)dest);
        break;
    }
} while (!success ); }

但是是否可以只将 void 指针转换为给定大小类型的指针而不是 switch?像这样:

double myVar;
size_t varSize = sizeof(double); void * dest = &myVar;
// specifierForDest is a string
scanf(specifierForDest, (varSize *)dest);

这样dest 就变成了一个指向大小为varSize 的变量的指针。

或者任何其他已知方法比为 C 中的每种类型编写 case 更好?

【问题讨论】:

  • 你问错问题了。您正在尝试创建一些通用的扫描函数,并决定这样做的方法是将指针传递给scanf,并将这些指针转换为“指向特定大小类型的指针”。但这不是一个正确的解决方案,因为scanf 被指定为采用指向某些类型的指针,与转换规范相匹配,而不仅仅是指向特定大小的类型的指针。您的文字问题的答案是肯定的,我们可以使用(char (*)[n]) pointer 轻松地将指针转换为指向 n 字节对象的指针。但这不是实现目标的正确方法。
  • 我建议你放弃scanf() 并且:(1) 使用fgets() 来读取用户输入和(2) 使用sscanf() 来解析它。
  • 这是 X-Y 问题。 \
  • 不,@0__________,CS50 是用 C 编译器编译的。但它提供了一个库和头文件,该类的分配可以/应该使用,头文件定义的其中一件事是 typedef string 别名 char *。这是 CS50 不喜欢的地方之一。
  • " %30[^\n]s"char string[30] 一起使用是错误的。应该是" %29[^\n]"

标签: c void-pointers


【解决方案1】:

C 标准没有提供将指针动态转换为scanf 所需的指针类型的方法。这意味着,一旦您的函数接收到 void * 参数,它转换为指定接收 scanf 的类型的唯一方法是将它们作为个别情况处理。

另一种方法是不以void * 形式接收参数,而是在可变参数列表中接收它并将其转发给专为此类操作设计的vscanf。为此,您必须将目标参数移动到参数列表的末尾。下面是示例代码。变量参数列表处理在最后几行。

#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>


typedef char *string;


void scan(char specifier, string instruction, string error,...)
{
    bool success = true;
    do
    {
        if (!success)
        {
            int c;
            do
                c = getchar();
            while (c != EOF && c != '\n');
            perror(error);
        }
        puts(instruction);
        char *specifierForDest;
        switch (specifier)
        {
            case 'd': specifierForDest = " %d";       break;
            case 's': specifierForDest = " %30[^\n]"; break;
            case 'h': specifierForDest = " %hd";      break;
            case 'f': specifierForDest = " %f";       break;
            default:
                ;   // Do something here; do not neglect error cases.
        }
        va_list ap;           // Create argument list pointer.
        va_start(ap, error);  // Initialize argument list handling.
        success = 1 == vscanf(specifierForDest, ap);
                              // Forward argument to vscanf.
        va_end(ap);           // Clean up argument list handling.
    } while (!success);
}

【讨论】:

  • 为 OP 的 typedef char string[30]; 建议 " %29[^\n]";
【解决方案2】:

谢谢。根据您的所有建议,我想出了这个:

#include <stdarg.h>
#include <stdlib.h>

typedef char string[30];
void scan(string specifier, string instruction, string error, ...) {
bool success = true;
va_list ap; va_start(ap, error);
do {
    if(!success) {
        int c;
        do
            c = getchar();
        while (c != EOF && c != '\n');
        puts(error);
    }
    puts(instruction);
    success = vscanf(specifier, ap);
} while (!success );
va_end(ap); }

效果很好。

【讨论】:

  • bool success = vscanf(...);vscanf() 返回EOF 时导致无限循环。 while (getchar()!='\n'); 也是如此。
  • string specifier, string instruction, string error 都不需要使用stringconst char * 更好。
  • 嗯,在vscanf(specifier, ap); 中重复使用ap 可能是个问题......?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-20
  • 2014-08-30
  • 1970-01-01
  • 2014-06-16
  • 2017-10-07
  • 1970-01-01
相关资源
最近更新 更多