C - struct* 在运行时强制转换?

C - struct* cast at runtime?

假设我有以下代码:

struct str1
{
    int common1;
    char common2;
    char *common3;
    long int aaaaaaaa;
}

struct str2
{
    char bbbb;
    char *common3;
    int common1;
    char common2;
}

struct str3
{
    char ccccccccc[200];
    int common1;
    char common2;
    int dddddddd;
    int eeeeeeee;
    char *common3;
}

void somefunc1(struct str1 var)
{
    printf("%d %c %s", var.common1, var.common2, var.common3);
}

void somefunc2(struct str2 var)
{
    printf("%d %c %s", var.common1, var.common2, var.common3);
}

void somefunc3(struct str3 var)
{
    printf("%d %c %s", var.common1, var.common2, var.common3);
}

我能否以某种方式避免代码重复并使用单个通用函数?函数调用将在运行时决定,因此宏是无关紧要的。函数之间的所有区别在于结构的名称,而不是它们的成员。

由于"common"结构成员的位置在结构之间不一致,答案是否定的。因此,结构之间确实没有实际的共性。

使用这些结构很难(如果不是不可能的话)编写一个函数来处理所有这些结构。但是,如果您更改这些结构以共享来自另一个结构的那些公共元素,那么这将是可能的。

struct base
{
    int common1;
    char common2;
    char *common3;
};

struct str1
{
    struct base b;
    long int aaaaaaaa;
};

struct str2
{
    struct base b;
    char bbbb;
};

struct str3
{
    struct base b;
    char ccccccccc[200];
    int dddddddd;
    int eeeeeeee;
};

注意:struct base结构体的变量应该是每个struct结构体的第一个成员,否则这个技巧将不起作用。

现在声明一个函数,该函数接受指向 struct base 的指针。

void somefunc(struct base* var)
{
    printf("%d %c %s\n", var->common1, var->common2, var->common3);
}

用法:

struct str1 s1 = { 1, 'a', "sfad"};
struct str2 s2 = { 2, 'b', "sdfazx"};
struct str3 s3 = { 3, 'c', "oiurotu"};

somefunc((struct base*) &s1);
somefunc((struct base*) &s2);
somefunc((struct base*) &s3);

Can I somehow avoid code duplication and use a single generic function?

没有。这听起来类似于 C++ 模板,当然 C 没有也没有类似的模板,除非您包含预处理器。 C11 有 _Generic,但这会产生一种函数重载:每种类型一个函数,预处理器支持通过单个名称调用它们。

Function calls are to be decided at runtime

在 C 和 C++ 中,函数调用——控制路径——是在编译时确定的。没有运行时 "decision"。

All the difference between the functions is the structs' names, not their members.

实际上,关键区别在于每个结构的布局。名称位于不同的位置。 C 编译器将名称转换为位置。编译后,没有名称也没有类型,没有任何迹象表明位置被命名为 common1 或者是结构的一部分。只有对内存位置的引用。正确使用语言取决于您,以确保引用指向您想要的位置。

therefore a macro is irrelevant.

预处理器允许您在编译器将名称转换为数字之前对其进行操作。如果您想在 C "by name" 中做任何事情,宏是唯一的选择。

在 C89 中,如果两个或多个结构以匹配顺序匹配类型的成员开始(它们共享一个公共初始序列)并且都是同一联合的一部分,则可以使用检查公共初始序列的任何部分共享 CIS 的任何类型的适当命名成员。您的示例不使用匹配类型,因此它不符合条件,但如果它按匹配顺序使用匹配类型,它就会。据我所知,1990 年代的 C89 编译器一致将相同的原则应用于结构指针(因此,如果结构类型 S1 和 S2 具有 CIS,则任一类型的指针都可用于访问 CIS 的成员)。虽然该标准没有明确指定这种处理方式,但到目前为止,编译器确保该规则适用于所有涉及联合的情况的最简单方法是使其适用于所有使用指针的情况,并且许多人(可能包括标准的作者)期望编译器自然会这样做,无论是否明确要求。

C99 要求如果代码要使用一种结构类型的指针来 访问另一个 CIS 的成员时,联合类型的完整定义必须可见,以使编译器知道类型之间的潜在别名。不幸的是,虽然这条规则有一个明确而明显的目的(允许程序员利用 CIS 规则,同时允许编译器假设访问完全不相关的结构不会产生别名)一些编译器作者会假设不会使用结构类型的指针访问任何其他类型,即使包含两种类型的完整联合类型声明可见,甚至在结构实际上是同一联合对象的成员的情况下也是如此。

如果您想利用公共初始序列规则,可能需要在使用带有 -fno-strict-aliasing 标志的编译器时使用标志(即使不利用 CIS,使用该标志也可以防止编译器错误)。利用别名的代码应该努力让它对编译器显而易见(例如通过确保合适的联合类型可见)但是除非或直到编译器编写者开始注意这些事情,-fno-strict-aliasing 将是必要的以适应他们的失败这样做。

看来我对预处理器的限制使用是错误的:

#include <stdio.h>

struct str1
{
    int common1;
    char common2;
    char *common3;
    long int aaaaaaaa;
};

struct str2
{
    char bbbb;
    char *common3;
    int common1;
    char common2;
};

struct str3
{
    char ccccccccc[200];
    int common1;
    char common2;
    int dddddddd;
    int eeeeeeee;
    char *common3;
};

#define somefunc(var) \
    printf("%d %c %s\n", var.common1, var.common2, var.common3);

int main()
{
    struct str1 var1 = {1,'a',NULL, 4};
    struct str2 var2 = {'b',NULL,2,'b'};
    struct str3 var3;
    var3.common1 = 3;
    var3.common2 = 'c';
    var3.common3 = NULL;


    somefunc(var1);
    somefunc(var2);
    somefunc(var3);

}

输出:

1 a (null)
2 b (null)
3 c (null)