C “为什么?”;1.(int*)1:((void*)((x)*0l))”;工作正常吗?

C “为什么?”;1.(int*)1:((void*)((x)*0l))”;工作正常吗?,c,gcc,C,Gcc,我一直在跟随C语言黑客使用宏检测整型常量表达式——Martin Uecker的Idea。 当我开始玩弄代码时,我通过交换三元运算符的表达式发现了一种奇怪的行为 参见下面的代码 #include <stdio.h> #include <stdlib.h> // https://lkml.org/lkml/2018/3/20/805 #define ICE_P(x) (sizeof(int) == sizeof(*(1 ? ((void*)((x) * 0l)) : (i

我一直在跟随C语言黑客使用宏检测整型常量表达式——Martin Uecker的Idea。

当我开始玩弄代码时,我通过交换三元运算符的表达式发现了一种奇怪的行为

参见下面的代码

#include <stdio.h>
#include <stdlib.h>

// https://lkml.org/lkml/2018/3/20/805
#define ICE_P(x) (sizeof(int) == sizeof(*(1 ? ((void*)((x) * 0l)) : (int*)1)))
//Why is this working?
#define ICE_P2(x) (sizeof(int) == sizeof(*(1 ? (int*)1 : ((void*)((x) * 0l)))))

int main() {

    int a = 5;
    int b = ICE_P(a);
    int c = ICE_P2(a);

    int d = ICE_P(5);
    int e = ICE_P2(5);   
    return 0;
}
#包括
#包括
// https://lkml.org/lkml/2018/3/20/805
#定义ICE_P(x)(sizeof(int)=sizeof(*(1?((void*)((x)*0l)):(int*)1)))
//为什么会这样?
#定义ICE_P2(x)(sizeof(int)=sizeof(*(1?(int*)1:((void*)((x)*0lщ)()
int main(){
INTA=5;
int b=冰P(a);
int c=冰P2(a);
int d=冰P(5);
int e=ICE_P2(5);
返回0;
}
ICE_p(x)可以完美地工作,如上面的链接所述。然而,当我交换三元运算符的左表达式和右表达式时,程序的行为仍然与我所预料的一样

调用ICE_P2时,
1?(int*)1:((void*)((x)*0l))
将被评估


具有正确行为的场景。
  • ICE_P2(5)-
    (void*)((5)*0l))
    将为
    NULL
根据标准,如果三元运算符的一侧被计算为“NULL”,则无论条件表达式如何,都将返回除
NULL
以外的值

因此,在这种情况下,将始终计算
(sizeof(int)==sizeof(int*)
,结果将为
1

我不理解的行为场景。
  • ICE_P2(a)-
    (void*)((a)*0l))
    将不会为
我的期望:

  • 现在,
    1?(int*)1:(void*)()
    应计算为
    (int*)1
    ,因为1始终为真
  • 整个表达式宏的计算结果为
    (sizeof(int)=sizeof(*(int*))
  • 结果是1
实际值:

  • 现在,
    1?(int*)1:(void*)()
    将值计算为
    (void*)()
  • 整个表达式宏的计算结果为
    (sizeof(int)==sizeof(void*)
  • 结果是0

有关
ICE\u p
的更多了解,请参阅或此。

因此,让我们了解一下标准

  • 值为
    0
    的整型常量表达式
    ,或转换为类型
    void*
    的此类表达式称为空指针常量。[……]
  • 在构造
    (void*)((x)*0l)
    中,对于任何整数值,该值显然始终为0,因此在大多数平台上,如果给定任何类型的整数值,它总是导致空指针,但是对于空指针常量iff,
    x
    是一个整数常量表达式

    现在,问题是为什么
    x?(void*)0:(int*)1的工作原理与
    x相同?(int*)1:(void*)0
    当包装在
    sizeof(*(…)
    中时。为此,我们需要阅读:

  • 如果第二个和第三个操作数都是指针,或者一个是空指针常量,另一个是指针,则结果类型是指向使用两个操作数引用的类型的所有类型限定符限定的类型的指针。此外,如果两个操作数都是指向兼容类型或兼容类型的不同限定版本的指针,则结果类型是指向复合类型的适当限定版本的指针如果一个操作数是空指针常量,则结果具有另一个操作数的类型;否则,一个操作数是指向
    void
    的指针或
    void
    的限定版本,在这种情况下,结果类型是指向
    void
    的适当限定版本的指针
  • 因此,如果第二个或第三个表达式是一个空指针常量,则条件运算符的结果具有其他表达式的类型,即都是
    x?(void*)0:(int*)1
    x?(int*)1:(void*)0具有类型
    int*

    在另一种情况下,即
    x?(void*)非常量:(int*)1
    粗体文本的后半部分表示该表达式的类型必须是
    void*

    在编译阶段,根据第二个和第三个操作数的类型完全决定类型;第一个操作数的在其中不起作用。如果在
    sizeof
    中使用该表达式,则整个表达式将变成另一个整数常量表达式


    然后是可疑的部分:
    sizeof(*…)
    。指针为“解引用”,解引用值的
    sizeof
    用于计算。ISO C不允许对
    sizeof(void)
    进行评估-实际上,它甚至不允许对
    void*
    进行取消引用-但GCC将其定义为
    sizeof(void)

    正如莱纳斯所说:



    顺便说一句,对于许多类似的用途,GCC内置就足够了——但是,尽管它将返回
    1
    用于所有的整型常量表达式,但它将返回
    1
    用于优化器可以在代码中替换常量的任何其他左值;不同之处在于,只有常量整数表达式可以用作非VLA数组维度、静态初始值设定项和位字段宽度。

    因此,让我们了解一下标准

  • 值为
    0
    的整型常量表达式
    ,或转换为类型
    void*
    的此类表达式称为空指针常量。[……]
  • 在构造
    (void*)((x)*0l)
    中,对于任何inte,值显然始终为0