注:我看过this和this。他们讨论类似的概念,但不回答这些问题。
我的环境是Windows 10,为了进行测试,我使用了两个编译器CLANG和GCC。
我正在通过void *
函数参数传递变量,并且需要正确转换它们。我想获得有关不同类型的方法之间不一致的反馈。
以下是使用void *
参数和枚举值参数来指示要传入的类型的测试函数的简化描述,该测试函数可容纳多种输入类型:
void func(void *a, int type)
{
switch(type) {
case 0://char
char cVar1 = (char)a; //compiles with no warnings/errors, seems to work
char cVar2 = *(char *)a; //compiles with no warnings/errors, seems to work
break;
case 1://int
int iVar1 = (int)a; //compiles with no warnings/errors, seems to work
int iVar2 = *(int *)a; //compiles with no warnings/errors, seems to work
break;
case 2://float
float fVar1 = (float)a; //compile error: (a1)(b1)
float fVar2 = *(float *)a; //requires this method
case 2://double
double dVar1 = (double)a; //compile error: (a1)(b1)(b2)
double dVar2 = *(double *)a;//this appears to be correct approach
break;
};
}
调用方法:
int main(void)
{
char c = 'P';
int d = 1024;
float e = 14.5;
double f = 0.0000012341;
double g = 0.0001234567;
void *pG = &g;
func4(&c, CHAR);//CHAR defined in enumeration, typical
func4(&d, INT);
func4(&e, FLT);
func4(&f, DBL);
func4(pG, DBL);
return 0;
}
与以上注释中的标志有关的确切错误文本如下:
CLANG-3.3版本
gcc-(tdm-1)5.1.0
供以下讨论参考
type var = (type)val;
type var = *(type *)val;
我的结果表明,转换float
和double
需要方法2。但是对于char
和int
,方法2似乎是可选的,即方法1可以很好地编译,并且似乎可以连续工作。
问题:
似乎是从void *
函数中恢复值参数应始终要求使用方法2,为什么要使用方法1(似乎来使用char
和int
类型?这是未定义的行为吗?
如果方法1适用于char
和int
,为什么至少在float
类型上它也不能使用?不是因为它们的大小不同,即:sizeof(float) == sizeof(int) == sizeof(int *) == sizeof(float *)
。是否因为严格的别名冲突?
C standard明确允许在指针和整数类型之间进行转换。有关指针转换的详细说明,请参见第6.3.2.3节:
5整数可以转换为任何指针类型。除非先前指定,否则结果是实现定义的,可能不是正确对齐,可能未指向所引用的实体类型,并且可能是陷阱表示。
6任何指针类型都可以转换为整数类型。除非先前指定,否则结果是实现定义的。如果结果不能以整数类型表示,其行为是未定义。结果不必在任何值的范围内整数类型。
假设您在将整数类型传递给函数时将其转换为void *
,然后将其转换为适当的整数类型,则可以这样做[[提供实现允许。尤其是在GCC允许这种情况下,假设所讨论的整数类型至少与void *
一样大。
char
和int
情况的原因,但是您需要传递值(广播到void *
)而不是地址。 6.3.2.3 not
没说过关于在指针和浮点类型之间进行转换的任何事情,这就是为什么不允许这样做的原因。当然,通过void *
传递值的最安全的方法是将值存储在适当类型的变量中,传递其地址,然后将函数中的void *
强制转换回正确的指针类型。这样可以保证工作。