编译时,C 丢弃 [1] 大部分类型信息,只留下偏移量。所以,你的函数会编译成这样的东西,在伪代码中:
changeCount:
assign *(*(stack_ptr + offset_element) + offset_Count) + 1
to *(stack_ptr + offset_element) + offset_Count;
assign 1 to return_value;
pop
stack_ptr 是调用 changeCount 时创建的堆栈帧的位置,offset_element 是元素参数的位置,相对于 stack_ptr,但是 offset_Count 是什么?请记住,编译器对您的代码的所有了解就是您在示例中显示的内容;element 是一个通用指针,实际上并不是指向任何东西的指针。您必须通过强制转换或将其分配给变量 [2] 来告诉编译器指向的元素:
typedef struct { int Count; } counted_t;
int changeCount(void* element)
{
counted_t* counted = element;
counted.Count++;
return 1;
}
这个函数将生成与上面基本相同的(伪)代码,但编译器现在知道 Count 的偏移量应该是多少。
您提到元素指向的类型有三种可能性。有几种处理方法:一个显着的联合或一个“继承”的结构。对于一个显着的联合使用,比如说,一个结构,一个元素是一个枚举,标识三种可能性中的哪一种,另一个元素是三个可能结构的联合;这大致就是 ML 语言(OCaml、Haskell 等)所称的代数数据类型或 Pascal 中的联合。对于“继承”,您可以使用类型定义:
typedef struct { counted_t counted; int i; } counted_int_t;
typedef struct { counted_t counted; double d; } counted_double_t;
typedef struct { counted_t counted; char* s; } counted_charptr_t;
在这种情况下,您可以使用上面的 changeCount 函数并传入指向 counted_int_t、counted_double_t 或 counted_charptr_t 的指针。
发生的情况是,只要 counted_t 元素是 first,编译器就会将三个结构与“后代”结构中的 Count 元素布置在同一位置。(至少,在我用过的每一个编译器和我见过的每一段代码中。我认为这在某些时候使它成为了 C 标准,但这是一个非常正常的习惯用法。)
[1] 调试信息除外,如果你告诉编译器发出它。但是,您的程序将无法访问该信息,因此在这种情况下它无济于事。
[2] x++ (postincrement) 操作递增它所应用的变量(嗯,左值);原始代码中的赋值是不必要的。