【问题标题】:Is there any way to pass a structure type to a c function有什么方法可以将结构类型传递给 c 函数
【发布时间】:2010-09-09 05:28:57
【问题描述】:

我有一些具有多个功能非常相似的代码,用于根据结构中一个字段的内容在列表中查找项目。函数之间的唯一区别是发生查找的结构的类型。如果我可以传入类型,我可以删除所有代码重复。

我还注意到这些函数中也发生了一些互斥锁,所以我想我可以不理会它们......

【问题讨论】:

    标签: c


    【解决方案1】:

    如果您确保该字段被放置在每个此类结构中的相同位置,您可以简单地转换一个指针来获取该字段。这种技术用于许多低级系统库,例如BSD 套接字。

    struct person {
      int index;
    };
    
    struct clown {
      int index;
      char *hat;
    };
    
    /* we're not going to define a firetruck here */
    struct firetruck;
    
    
    struct fireman {
      int index;
      struct firetruck *truck;
    };
    
    int getindexof(struct person *who)
    {
      return who->index;
    }
    
    int main(int argc, char *argv[])
    {
      struct fireman sam;
      /* somehow sam gets initialised */
      sam.index = 5;
    
      int index = getindexof((struct person *) &sam);
      printf("Sam's index is %d\n", index);
    
      return 0;
    }
    

    这样做会失去类型安全性,但这是一种很有价值的技术。

    [我现在已经实际测试了上面的代码并修复了各种小错误。有了编译器就容易多了。 ]

    【讨论】:

    • 确实需要注意没有结构填充。作为结构的第一个字段的“char”类型可能不会存储在不同结​​构中的相同偏移处
    • 结构的布局(除非您明确要求)由平台 ABI 决定。我想拥有一个执行你所说的重新排序和打包的 ABI 实际上并不是非法的(就像 ISO C 标准所禁止的那样),但它会非常疯狂。我认为很多现有的软件根本无法在这样的平台上运行。
    • @itj:对于结构的第一个字段,char 项目不会被放置在偏移量 0 以外的任何位置。如果该字段出现在其他任何位置,则可能会出现问题.
    【解决方案2】:

    由于结构只不过是预定义的内存块,因此您可以这样做。您可以将 void * 传递给结构,并传递一个整数或其他东西来定义类型。

    从那里开始,最安全的做法是在访问数据之前将 void * 重铸为适当类型的指针。

    您需要非常非常小心,因为当您转换为 void * 时会失去类型安全性,并且在执行此类操作时可能会遇到难以调试的运行时错误。

    【讨论】:

      【解决方案3】:

      我认为您应该查看 C 标准函数 qsort() 和 bsearch() 以获得灵感。这些是对数组进行排序和在预排序数组中搜索数据的通用代码。它们适用于任何类型的数据结构 - 但您将指针传递给进行比较的辅助函数。辅助函数知道结构的细节,因此可以正确进行比较。

      事实上,由于您想要进行搜索,可能只需要 bsearch(),但如果您正在动态构建数据结构,您可能会决定您需要与排序列表不同的结构. (您可以使用排序列表——与堆相比,它只会减慢速度。但是,您需要一个通用的 heap_search() 函数和一个 heap_insert() 函数才能正确完成这项工作,并且此类函数在 C 中未标准化。搜索网络显示此类函数存在 - 不是那个名称;只是不要尝试“c heap search”,因为它假定您的意思是“廉价搜索”并且您会得到大量垃圾!)

      【讨论】:

        【解决方案4】:

        如果您测试的 ID 字段是所有结构共享的公共初始字段序列的一部分,则使用联合可以保证访问将起作用:

        #include <stdio.h>
        
        typedef struct
        {
            int id;
            int junk1;
        } Foo;
        
        typedef struct
        {
            int id;
            long junk2;
        } Bar;
        
        typedef union
        {
            struct
            {
                int id;
            } common;
        
            Foo foo;
            Bar bar;
        } U;
        
        int matches(const U *candidate, int wanted)
        {
            return candidate->common.id == wanted;
        }
        
        int main(void)
        {
            Foo f = { 23, 0 };
            Bar b = { 42, 0 };
        
            U fu;
            U bu;
        
            fu.foo = f;
            bu.bar = b;
        
            puts(matches(&fu, 23) ? "true" : "false");
            puts(matches(&bu, 42) ? "true" : "false");
        
            return 0;
        }
        

        如果您不走运,并且该字段出现在各种结构中的不同偏移量处,您可以在函数中添加偏移量参数。然后,offsetof 和一个包装宏模拟 OP 的要求——在调用站点传递结构的类型:

        #include <stddef.h>
        #include <stdio.h>
        
        typedef struct
        {
            int id;
            int junk1;
        } Foo;
        
        typedef struct
        {
            int junk2;
            int id;
        } Bar;
        
        int matches(const void* candidate, size_t idOffset, int wanted)
        {
            return *(int*)((const unsigned char*)candidate + idOffset) == wanted;
        }
        
        #define MATCHES(type, candidate, wanted) matches(candidate, offsetof(type, id), wanted)
        
        int main(void)
        {
            Foo f = { 23, 0 };
            Bar b = { 0, 42 };
            puts(MATCHES(Foo, &f, 23) ? "true" : "false");
            puts(MATCHES(Bar, &b, 42) ? "true" : "false");
        
            return 0;
        }
        

        【讨论】:

          【解决方案5】:

          这样做的一种方法是将类型字段作为结构的第一个字节。您的接收函数查看此字节,然后根据它发现的内容将指针转换为正确的类型。另一种方法是将类型信息作为单独的参数传递给需要它的每个函数。

          【讨论】:

            【解决方案6】:

            您可以使用参数化宏来执行此操作,但大多数编码策略会对此表示不满。

            
            #include 
            #define getfield(s, name) ((s).name)
            
            typedef struct{
              int x;
            }Bob;
            
            typedef struct{
              int y;
            }Fred;
            
            int main(int argc, char**argv){
                Bob b;
                b.x=6;
            
                Fred f;
                f.y=7;
            
                printf("%d, %d\n", getfield(b, x), getfield(f, y));
            }
            

            【讨论】:

              【解决方案7】:

              简短的回答:不。但是,您可以为此创建自己的方法,即提供有关如何创建此类结构的规范。但是,这通常不是必需的,也不值得付出努力;只是通过引用。 (callFuncWithInputThenOutput(input, &amp;struct.output);)

              【讨论】:

                【解决方案8】:

                我对 c 有点生疏,但尝试使用 void* 指针作为函数参数中的变量类型。然后将结构的地址传递给函数,然后按照您的方式使用它。

                void foo(void* obj);
                
                void main()
                {
                  struct bla obj;
                  ...
                  foo(&obj);
                  ...
                }
                
                void foo(void* obj)
                {
                  printf(obj -> x, "%s")
                }
                

                【讨论】:

                • 这不可能编译。你不能像那样取消引用指向 void 的指针。
                • 好吧,公平地说,我确实说过我对 C 语言生疏了。;)
                猜你喜欢
                • 1970-01-01
                • 2010-09-22
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2020-12-02
                • 2018-04-10
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多