C 类型升级/转换

C 类型升级/转换,c,64-bit,C,64 Bit,sizeof运算符的结果似乎是size_t类型,在Windows 64位上定义为无符号long long 考虑以下一段(伪)代码: 当按如下方式调用func时,在类型提升/转换方面实际会发生什么 func((herp + derp) * durr * sizeof wut); // 1st example herp、derp和durr的值是否首先提升为无符号长长度,然后在计算结果后,将结果转换回无符号长长度 func((herp + derp) * durr * (unsigned long)

sizeof运算符的结果似乎是size_t类型,在Windows 64位上定义为无符号long long

考虑以下一段(伪)代码:

当按如下方式调用func时,在类型提升/转换方面实际会发生什么

func((herp + derp) * durr * sizeof wut); // 1st example
herp、derp和durr的值是否首先提升为无符号长长度,然后在计算结果后,将结果转换回无符号长长度

func((herp + derp) * durr * (unsigned long)sizeof wut); // 2nd example
相反,当按如下方式调用func时,是否唯一将类型转换为unsigned long的转换

func((herp + derp) * durr * (unsigned long)sizeof wut); // 2nd example

考虑到类型转换/升级,调用func最合适/正确的方法是什么?

当遇到参数大小不同的算术运算符时,编译器应用,这基本上使两个操作数的类型相同

这在整个表达式中执行,一次一个运算符。根据C的语法将表达式分析为单独的操作;编译器不允许修改解析,除非它能证明结果没有差别(在这种情况下,修改或多或少是不相关的)

请考虑你的表达:

func((herp + derp) * durr * sizeof wut);
这在语法上等同于以下一系列操作,其中省略了类型和转换(目前):

前四个临时变量将自动键入每个运算符的结果类型(这是该运算符的常用算术转换生成的类型)

  • temp1=herp+derp

    herp
    derp
    都是
    unsigned long
    ;不需要转换;结果类型为
    无符号长

  • temp2=temp1*durr

    temp1
    durr
    都是
    unsigned long
    ;不需要转换;结果类型为
    无符号长

  • temp3=wut的大小

    sizeof
    始终返回
    size\t
    ,因此这是结果的类型

  • temp4=temp2*temp3

    temp2
    unsigned long
    temp3
    size\u t
    ,在示例平台上是
    unsigned long
    。这需要将
    temp2
    转换为
    unsigned long
    ,之后的结果类型为
    unsigned long

  • 因此,我们可以在上面的示例代码中插入类型和转换:

    unsigned long temp1 = herp + derp;
    unsigned long temp2 = temp1 * durr;
    unsigned long long temp3 = sizeof wut
    unsigned long long temp4 = (unsigned long long)temp2 * temp3;
    temp5 = (unsigned long)temp4;
    func(temp5)
    
    这里需要说明的关键是,结果将转换为其他类型这一事实对其计算没有影响。编译器不能决定不应用通常的算术转换,或应用不寻常的算术转换(可能缩小一个参数而不是扩大另一个参数),除非它能证明最终结果在所有情况下都与标准规定的结果相同。(这里,“在所有情况下”实际上是指“在所有不表现出未定义行为的情况下”,但由于无符号算术是定义良好的,除除以/模0外,此细节在本例中不相关。)

    如果表达式包含除法运算符,则溢出实际上很重要。考虑

    (a + b) * c * (sizeof x) / (sizeof y)
    
    其中
    a
    b
    c
    都是相同的类型,比
    尺寸

    与上面的逻辑一样,
    (a+b)*c在
    a
    b
    c
    的常见类型中进行评估。然后将该结果提升为
    size\u t
    ,以便它可以乘以
    x
    的大小。但肯定有可能
    (a+b)*c
    在转换之前溢出,导致最终结果无效。坚持使用
    size\u t
    操作数执行整个计算会更安全。这可以通过添加单个显式转换来实现:

    ((size_t)a + b) * c * (sizeof x) / (sizeof y)
    

    当遇到参数大小不同的算术运算符时,编译器将应用,这基本上使两个操作数的类型相同

    这在整个表达式中执行,一次一个运算符。根据C的语法将表达式分析为单独的操作;编译器不允许修改解析,除非它能证明结果没有差别(在这种情况下,修改或多或少是不相关的)

    请考虑你的表达:

    func((herp + derp) * durr * sizeof wut);
    
    这在语法上等同于以下一系列操作,其中省略了类型和转换(目前):

    前四个临时变量将自动键入每个运算符的结果类型(这是该运算符的常用算术转换生成的类型)

  • temp1=herp+derp

    herp
    derp
    都是
    unsigned long
    ;不需要转换;结果类型为
    无符号长

  • temp2=temp1*durr

    temp1
    durr
    都是
    unsigned long
    ;不需要转换;结果类型为
    无符号长

  • temp3=wut的大小

    sizeof
    始终返回
    size\t
    ,因此这是结果的类型

  • temp4=temp2*temp3

    temp2
    unsigned long
    temp3
    size\u t
    ,在示例平台上是
    unsigned long
    。这需要将
    temp2
    转换为
    unsigned long
    ,之后的结果类型为
    unsigned long

  • 因此,我们可以在上面的示例代码中插入类型和转换:

    unsigned long temp1 = herp + derp;
    unsigned long temp2 = temp1 * durr;
    unsigned long long temp3 = sizeof wut
    unsigned long long temp4 = (unsigned long long)temp2 * temp3;
    temp5 = (unsigned long)temp4;
    func(temp5)
    
    这里需要说明的关键是,结果将被转换为其他类型这一事实没有任何影响