我最近在我的测试脚本中实现了类似的东西,当我摆弄标准C时,我对它产生了好奇。这不是家庭作业,只是我自己的一些爱好。
#include <stdio.h>
typedef struct Struct0 {
int x;
int y;
} Struct0;
typedef struct Struct1 {
char x;
double y;
char z;
} Struct1;
typedef struct Struct2 {
double x;
char y;
int z;
char w;
} Struct2;
void my_test_callback(void *pack) {
// Type cast back to void**
void **params = (void **) pack;
// Unpack data
Struct0* s0 = (Struct0 *) params[0];
Struct1* s1 = (Struct1 *) params[1];
Struct2* s2 = (Struct2 *) params[2];
// Use the data
printf("s0: %i %i\n", s0->x, s0->y);
printf("s1: %c %.2f %c\n", s1->x, s1->y, s1->z);
printf("s2: %.2f %c %i %c\n", s2->x, s2->y, s2->z, s2->w);
}
int main(int argc, char **argv) {
// Imagine we have two or more variables that need to be passed
// to a single function call, as a single parameter.
Struct0 a;
Struct1 b;
Struct2 c;
// Initializing Struct 0
a.x = 1;
a.y = 2;
// Initializing Struct 1
b.x = 'a';
b.y = 3.0;
b.z = 'b';
// Initializing Struct 2
c.x = 4.0;
c.y = 'c';
c.z = 5;
c.w = 'd';
// So, instead of doing something like this (which is the common way):
struct packed {
Struct0 _a;
Struct1 _b;
Struct2 _c;
} pack_data;
pack_data._a = a;
pack_data._b = b;
pack_data._c = c;
// I would like to do something much more generic, such as:
void* generic_pack[3];
generic_pack[0] = (void *) &a; // This way, I don't really need to
generic_pack[1] = (void *) &b; // know the variables types, and I
generic_pack[2] = (void *) &c; // can simply type cast them to void*
// Is it legal to do this? Will it always work?
my_test_callback((void *) generic_pack);
// Output from my console:
// s0: 1 2
// s1: a 3.00 b
// s2: 4.00 c 5 d
return 0;
}
代码编译没有问题,* 在windows 10,gcc.exe(Rev6,由MSYS2项目构建)12.2.0* 上创建和测试,并且它工作时没有任何分段错误。
将gen_pack
(void **
指针)转换为void *
类型是否保证每次运行脚本时都能正常工作?或者我们只是幸运地没有发生分段错误?
2条答案
按热度按时间mcvgt66p1#
您所做的是有效的。* 任何 * 对象指针类型都可以自由地与
void *
转换,其中包括void **
。你从一个
void *
数组开始,每一个数组都被赋以不同的结构体指针值,这对上面的转换规则是有效的,然后你把你的void *
数组,它衰减为void **
,并把它传递给一个期望void *
的函数,这也是有效的。在函数内部,将
void *
参数转换为void **
,这对于上述规则是有效的,并且返回了开始时的类型,然后迭代void *
数组,并将每个数组赋给不同的struct类型,这对于上述规则也是有效的,并且返回了开始时的指针类型。pb3skfrl2#
转换本身对于C来说是可以的。C允许几乎任何形式的野生和疯狂的指针转换--特别是在
void*
的情况下,它们可以在没有强制转换的情况下执行。只有当你通过错误的类型解除引用指针时,问题才开始出现。generic_pack[0] = (void *) &a;
这是确定的,你甚至不需要演员阵容。my_test_callback((void *) generic_pack);
这是可疑的,但是强制类型转换本身是有效的。这里所做的是将一个“decayed”数组指针(从void* [3]
到void**
)转换为void*
。同样,转换为void*
时不需要强制类型转换。void **params = (void **) pack;
由于调用者代码有数组衰减,这是可以的。但是我们不需要强制类型转换。如果调用者代码是
(void *) &generic_pack
,那么它将是错误的和有问题的。类似地,如果您将参数声明为
void* pack[3]
,那么该参数将“衰减”为void**
,也不会出现任何问题。void**
并在其上执行指针运算。它指向一个void*
数组,所以这是可以的。但是同样,不需要强制转换。除此之外,可以将
void* [3]
数组的成员强制转换为结构指针,因为这是您最初存储在那里的内容。请注意,如果您决定使用
void**
指向struct
数组,则不将void*
数组声明为中间步骤会产生更多问题。因为 * 只有 *void*
是特殊的泛型指针类型,所以它不适用于void**
。总而言之,
void*
是不安全和危险的,所以明智的做法是避免它们。实现代码的正确方法应该是创建第四个struct
,其中包含另外三个:或者,该结构体可能包含了指向其他位置的结构体的指针,到目前为止,最好的选择当然是重写函数以接受3个参数。