【问题标题】:how to verify the type of a value passed to a function as void pointer?如何验证作为空指针传递给函数的值的类型?
【发布时间】:2013-10-24 18:56:55
【问题描述】:

有以下代码:


文件:types.h

typedef struct Struct_A_T
{
   int   A;
   char  B;
   float C;
}Struct_A;

文件:code.c

#include "types.h"

void Function(const void *const ptr)
{
   Struct_A localStruct = *((Struct_A *)ptr);

   localStruct.A = 1000;
   localStruct.B = 250;
   localStruct.C = 128.485;
}

文件:main.c

#include "types.h"

void Function(const void *const ptr);

int main(void)
{
   Struct_A MyStruct1 = {2, 5, 2.8};
   float local = 24.785;

   /* Correct call */
   Function(&MyStruct1);

   /* Incorrect call!!! */
   Function(&local);
}

并且知道指向 void 的指针可以用作“通用”指针。如何在 "Function" 内部检测到在 void 指针中传递的类型是否正确,以避免文件 main.c 中的最后一次调用引发的运行时错误?

【问题讨论】:

  • 空指针和类型安全是相互排斥的概念。你不能同时拥有两者,至少在 C 语言中是这样。

标签: c pointers runtime-error void-pointers


【解决方案1】:

使用语言功能无法做到这一点。它只能手动完成。

首先,我在代码的调试版本中使用以下技术

typedef struct Struct_A_T
{
   int   A;
   char  B;
   float C;
#ifdef DEBUG
   unsigned signature; 
#endif /* DEBUG */
}Struct_A;

即在调试配置中,我在结构中引入了一个附加字段。该结构类型的每个对象都必须使用特定于该类型的一些预先确定的“不可预测”签名值初始化该字段,例如

#define STRUCT_A_SIGNATURE 0x12345678

如果所有结构都以某种集中方式创建(例如动态分配或由专用函数初始化),这很容易做到。如果没有这样的集中位置,这可能会更麻烦。但我们有时不得不为安全付出代价。例如,在您的示例中,

Struct_A MyStruct1 = {2, 5, 2.8, 0x12345678 };

顺便说一句,指定的初始化器可能会使此类初始化更稳定且更易于阅读。

然后,为了将指针从 void * 转换为特定类型,我使用以下强制转换宏

#ifdef DEBUG
  #define TO_STRUCT_A(p)\
    (assert((p) == NULL || ((Struct_A *)(p))->signature == STRUCT_A_SIGNATURE),\
      (Struct_A *)(p))
#else /* DEBUG */
  #define TO_STRUCT_A(p) ((Struct_A *)(p))
#endif /* DEBUG */

意味着在你的Function 中你会这样做

Struct_A localStruct = *TO_STRUCT_A(ptr);

如果将指向错误类型的指针传递给Function,很有可能会触发断言失败。

当然,这一切都可以(并且应该)使用一组更通用的宏来实现。

显然,这仅适用于结构类型,您可以在其中注入额外的签名字段。这种方法的另一个潜在问题是,通过在调试构建的结构中引入一个额外的字段,可能会导致调试构建和发布构建的行为发生分歧。

【讨论】:

    【解决方案2】:

    您不能,这是void* 的主要缺点,您无法确定所指向的内容。你只是必须知道

    【讨论】:

      【解决方案3】:

      您可以使用sizeof 将结构的大小与void* 指向的大小进行比较。将指向您的结构类型的指针传递给Function,这不是充分但必要的条件。顺便说一句,可以检查变量的元数据(包括类型)的语言属性被称为 reflection。它在许多现代语言中都可用,最近 C++11 中包含了一些位,但 C 仍然缺少它。

      【讨论】:

      • 嗯?你如何找到void* 指向的sizeof 东西? AFAIK,指针无法知道它指向 at... 的大小
      • sizeof 不能应用于 void 类型,这意味着它不能应用于取消引用的 void * 指针。在任何情况下,在 VLA 上下文之外 sizeof 是一个纯粹的编译时运算符,因此不可能用于验证任何运行时条件。
      猜你喜欢
      • 1970-01-01
      • 2019-11-01
      • 2021-07-18
      • 1970-01-01
      • 2012-06-17
      • 2016-07-17
      • 2018-02-06
      • 2019-08-17
      • 1970-01-01
      相关资源
      最近更新 更多