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.

我想知道以下两者之间是否有区别:

  • 将基元变量强制转换为另一个基元类型
  • 将基元变量地址的强制转换解引用到另一个基元类型的指针
  • 我还想知道是否有充分的理由使用(2)而不是(1)。我在遗留代码中看到了(2),这就是为什么我想知道的原因。从上下文来看,我不明白为什么(2)比(1)更受青睐。从我写的以下测试中,我得出结论,至少在这两种情况下,向上投射的行为是相同的:

    /* 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)