【问题标题】:typecheck for return value类型检查返回值
【发布时间】:2009-11-22 18:17:12
【问题描述】:

我有一个列表,我希望能够在其中放置不同的类型。我有一个函数可以返回索引处的当前值:

void *list_index(const List * list, int index) {
    assert(index < list->size);
    return list->data[index];
}

数组中有多种类型,例如:

typedef struct structA { List *x; char *y; List *z; } structA;
typedef struct structB { List *u; char *w; } structB;

现在为了从数组中获取数据:

structA *A;
structB *B;
for(j=0... ) {
 A = list_index(list, j);
 B = list_index(list, j);
}

但是现在我如何找出返回值的类型呢? typeof 可以做到这一点吗(我正在使用 GCC btw)?

这甚至可能吗,还是我必须做出某种不同的构造?

【问题讨论】:

    标签: c types typeof


    【解决方案1】:

    您必须使用here 所示的联合。

    【讨论】:

      【解决方案2】:

      解决这个问题的最佳方法是使用联合。

      另一种方法是将列表项 memcpy() 转换为适当类型的实际结构(即,不是指针)。这将具有使每个列表项尽可能小的优点。

      第三种方法是像type punning 那样转换指针类型。只要对象以其正确的类型或字符被取消引用,C 就允许这样做。

      无论哪种方式,您都需要在每个结构中放置一个标识对象类型的代码。编译器无法为您找出指针指向的内容。即使你可以使用typeof,你也不应该使用。不是 C99。

      从技术上讲,如果您不使用联合,则在对类型代码进行合法的 C99 访问时会遇到问题,因为您需要对类型做出临时假设,这将违反对象必须被取消引用为它们的实际类型,通过联合,或通过 char *。然而,由于类型代码必须在每种类型中处于相同位置(为了有用),这种常见的技术违规实际上不会导致别名优化错误。

      实际上,如果您将类型代码设为char,将其设为结构中的第一件事,并通过char * 访问它,我认为您最终会得到一些读起来有点混乱的代码,但是完全符合 C99。

      这里是一个例子,这通过gcc -Wall -Wextra

      #include <stdio.h>
      #include <stdlib.h>
      
      struct A {
          char typeCode;
          int something;
      };
      struct B {
          char typeCode;
          double somethingElse;
      };
      
      void *getMysteryList();
      
      int main()
      {
          void **list = getMysteryList();
          int i;
          for (i = 0; i < 2; ++i)
              switch (*(char *) list[i]) {
              case 'A':
                  printf("%d\n", ((struct A *) list[i])->something);
                  break;
              case 'B':
                  printf("%7.3f\n", ((struct B *) list[i])->somethingElse);
                  break;
              }
          return 0;
      }
      
      void *getMysteryList()
      {
          void **v = malloc(sizeof(void *) * 2);
          struct A *a = malloc(sizeof(struct A));
          struct B *b = malloc(sizeof(struct B));
      
          a->typeCode = 'A';
          a->something = 789;
          b->typeCode = 'B';
          b->somethingElse = 123.456;
      
          v[0] = a;
          v[1] = b;
          return v;
      }
      

      【讨论】:

      • 只要您始终按类型访问类型代码字段,就不会违反标准。您确实需要确保类型代码字段在所有结构中具有相同的类型和位置。
      【解决方案3】:

      C 完全在编译时处理类型和类型(没有动态类型),所以一旦你将指针转换为“void *”,它就会丢失有关原始类型的任何信息。您可以将其转换回原始类型,但您需要通过其他方法知道那是什么。

      执行此操作的常用方法是在可能存储在列表类型中的所有对象的开头使用某种类型标记或描述符。例如:

      typedef struct structA { int tag; List *x; char *y; List *z; } structA;
      typedef struct structB { int tag; List *u; char *w; } structB;
      enum tags { structAtype, structBtype };
      

      您需要确保每次创建 structA 或 structB 时,都正确设置了 tag 字段。然后,您可以将从 list_index 返回的 void * 转换为 int * 并使用它来读取标签。

      void *elem = list_index(list, index)
      switch (*(int *)elem) {
      case structAtype:
          /* elem is a structA */
                    :
      case structBtype:
          /* elem is a structB */
      

      【讨论】:

        【解决方案4】:

        使您想要放入列表的元素都继承自一个公共基类。然后你可以让你的基类包含识别实际类型的成员。

        class base {
        public:
          typedef enum {
            type1,
            type2,
            type3
          } realtype;
        
          virtual realtype whatAmI()=0;
        };
        
        class type_one : public base {
          public:
            virtual base::realtype whatAmI() { return base::type1; };
        };
        
        class type_two : public base {
          public:
            virtual base::realtype whatAmI() { return base::type2; };
        };
        

        之后,您将声明您的列表类型:

        std::list<base *> mylist;
        

        并且您可以将指向任何派生类型的指针填充到列表中。然后,当您将它们取出时,您只需调用 'whatAmI()' 即可找出将其投射到的内容。

        请注意:尝试在 C++ 中执行此操作意味着您正在以一种不适合 C++ 的方式进行操作。任何时候你像这样故意避开 C++ 类型系统,就意味着你放弃了 C++ 的大部分用处(静态类型检查),通常意味着你以后要为自己创造大量的工作,而不仅仅是因为您调试此应用的第一次迭代,尤其是在维护时。

        【讨论】:

        • 问题标记为 C,而不是 C++。
        • 原来如此。我看到了他的“列表”类型,只是假设了一个对象容器。所以我的回答只有在你愿意去 C++ 的情况下才适用。
        【解决方案5】:

        你有一些选择。请记住,C 基本上不是动态类型语言。

        您为结构体创建一个公共基础,并在其中放置一个您自己的简单类型指示符。

        struct base {
          int type_indication:
        };
        

        然后

        struct structA {
           struct base base;
           ...
        };
        

        然后您可以将指针转换为(struct base *)。

        【讨论】:

        • 问题是关于 C,而不是 C++。
        猜你喜欢
        • 2013-12-16
        • 2016-08-03
        • 2023-03-29
        • 2020-02-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-11
        • 2020-01-08
        相关资源
        最近更新 更多