C 什么';这两者之间的区别是什么;(类型)变量“;及*((type*)“variable”);,如果有的话?
我想知道以下两者之间是否有区别:C 什么';这两者之间的区别是什么;(类型)变量“;及*((type*)“variable”);,如果有的话?,c,pointers,casting,C,Pointers,Casting,我想知道以下两者之间是否有区别: 将基元变量强制转换为另一个基元类型 将基元变量地址的强制转换解引用到另一个基元类型的指针 我还想知道是否有充分的理由使用(2)而不是(1)。我在遗留代码中看到了(2),这就是为什么我想知道的原因。从上下文来看,我不明白为什么(2)比(1)更受青睐。从我写的以下测试中,我得出结论,至少在这两种情况下,向上投射的行为是相同的: /* compile with gcc -lm */ #include <stdio.h> #include <math.
/* compile with gcc -lm */
#include <stdio.h>
#include <math.h>
int main(void)
{
unsigned max_unsigned = pow(2, 8 * sizeof(unsigned)) - 1;
printf("VALUES:\n");
printf("%u\n", max_unsigned + 1);
printf("%lu\n", (unsigned long)max_unsigned + 1); /* case 1 */
printf("%lu\n", *((unsigned long *)&max_unsigned) + 1); /* case 2 */
printf("SIZES:\n");
printf("%d\n", sizeof(max_unsigned));
printf("%d\n", sizeof((unsigned long)max_unsigned)); /* case 1 */
printf("%d\n", sizeof(*((unsigned long *)&max_unsigned))); /* case 2 */
return 0;
}
从我的角度来看,(1)和(2)之间应该没有区别,但我想咨询SO专家进行健全性检查。第一个(
(type)变量
简单地将变量转换为所需的类型,第二个(*(type*)变量
)在按所需的指针类型强制转换后解除对指针的防护。第一次强制转换是合法的;第二次强制转换可能不合法
第一个cast告诉编译器使用变量类型的知识进行到所需类型的转换;如果在语言标准中定义了正确的转换,编译器会进行转换
第二个强制转换告诉编译器忘记其对变量类型的了解,并将其内部表示形式重新解释为不同类型的表示形式*。这具有有限的适用性:只要二进制表示形式与目标指针所指类型的表示形式相匹配,此转换将起作用。但是,这并不等同于第一次强制转换,因为在这种情况下,值转换永远不会发生
将要转换的变量类型切换为具有不同表示形式的变量,例如float
,很好地说明了这一点:第一次转换生成正确的结果,而第二次转换生成垃圾:
float test = 123456.0f;
printf("VALUES:\n");
printf("%f\n", test + 1);
printf("%lu\n", (unsigned long)test + 1);
printf("%lu\n", *((unsigned long *)&test) + 1); // Undefined behavior
这张照片
123457.000000
123457
1206984705
*仅当其中一种类型为字符类型且指针对齐有效时,此选项才有效,类型转换非常简单(即,没有转换时),当您更改限定符或签名,或者在第一个成员为有效转换源/目标的情况下,在
结构
/联合
中进行强制转换时。否则,这将导致未定义的行为。请参阅C 2011(N1570),6.5 7,用于完整描述。感谢您指出定义第二次转换时的情况。不同之处在于,在第二种情况下,您可能有未定义的行为。原因是无符号
与无符号整数
相同,无符号长
可能大于无符号整数带符号的int
,当转换到您取消引用的指针时,您还可以读取未初始化的无符号long
第一种情况只是将
unsigned int
转换为unsigned long
,并根据需要扩展unsigned int
。在处理结构时,强制转换指针会产生不同:
struct foo {
int a;
};
void foo()
{
int c;
((struct foo)(c)).a = 23; // bad
(*(struct foo *)(&c)).a = 42; // ok
}
让我们看两个简单的例子,在现代硬件上使用
int
和float
(没有有趣的事情)
输出,可能…(您的结果可能不同)
它与整数10653216的表示形式相同。这是:
(type)variable
获取变量的值
并将其转换为类型类型
。此转换不一定只是复制表示的位;它遵循转换的语言规则。根据源类型和目标类型,结果可能与变量
具有相同的数学值,但可能会被重新表示完全不同
这:
执行称为别名的操作,有时非正式地称为。它占用变量
占用的内存块,并将其视为类型
的对象。如果源类型和目标类型具有不同的表示形式,它可能会产生奇怪的结果,甚至使程序崩溃(例如,一个整数和一个浮点类型),或者即使它们的大小不同。例如,如果变量
是一个16位整数(例如,它的类型是short
),并且type
是一个32位整数类型,那么最多只能得到一个包含16位垃圾的32位结果——而简单的值转换将给出一个数学上正确的结果
指针强制转换表单也会给您带来对齐问题。例如,如果变量
是字节对齐的,并且类型
需要2字节或4字节对齐,则您可能会得到未定义的行为,这可能会导致垃圾结果或程序崩溃。或者,更糟糕的是,它可能看起来工作正常(这意味着您有一个隐藏的bug,以后可能会出现,并且很难找到)
通过获取对象的地址并将其转换为无符号字符*
,可以检查对象的表示形式;该语言特别允许将任何对象视为字符类型的数组
但是如果一个简单的值转换完成了任务,那么这就是你应该使用的
如果variable
和type
都是算术类型,则可能不需要强制转换;您可以将任何算术类型的表达式指定给任何算术类型的对象,并且转换将隐式进行
下面是一个示例,其中两种形式具有非常不同的行为:
#include <stdio.h>
int main(void) {
float x = 123.456;
printf("d = %g, sizeof (float) = %zu, sizeof (unsigned int) = %zu\n",
x, sizeof (float), sizeof (unsigned int));
printf("Value conversion: %u\n", (unsigned int)x);
printf("Aliasing : %u\n", *(unsigned int*)&x);
}
“(类型)变量”和“*((类型*)&variable)”之间有什么区别
第二个表达式可能会导致对齐和别名问题
第一种形式是将一个值转换为另一种类型的自然方式。但假设没有违反对齐或别名,在某些情况下,第二种表达式比第一种形式有优势。*((type*)和variable)
将生成左值,而(t
(int) x = 1
*(int *) &x = 1065353216
00111111 100000000 00000000 0000000
(type)variable
*((type *)&variable)
#include <stdio.h>
int main(void) {
float x = 123.456;
printf("d = %g, sizeof (float) = %zu, sizeof (unsigned int) = %zu\n",
x, sizeof (float), sizeof (unsigned int));
printf("Value conversion: %u\n", (unsigned int)x);
printf("Aliasing : %u\n", *(unsigned int*)&x);
}
d = 123.456, sizeof (float) = 4, sizeof (unsigned int) = 4
Value conversion: 123
Aliasing : 1123477881
(*((type *)& expr)))++
(type)expr ---becomes---> *(type *)&expr
cond ? expr1 : expr2 ---becomes---> *(cond ? &expr1 : &expr2)