输入促销/转化

时间:2017-05-01 17:52:46

标签: c 64-bit

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

考虑以下(伪)代码:

int func(unsigned long arg)
{
    /* ... */
}

/* ... */

unsigned long herp = 3,
              derp = 5,
              durr = 7;

wchar_t wut;

func(/* ... */);

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

func((herp + derp) * durr * sizeof wut); // 1st example

herp,derp和durr的值是否首先提升为无符号long long,然后在计算结果后,结果将转换回unsigned long?

相反,当按如下方式调用func时,唯一的转换是将类型强制转换为unsigned long吗?

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

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

1 个答案:

答案 0 :(得分:2)

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

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

所以,请考虑你的表达:

func((herp + derp) * durr * sizeof wut);

这在语法上等同于以下一系列操作,其中省略了类型和转换(暂时):

temp1 = herp + derp;
temp2 = temp1 * durr;
temp3 = sizeof wut
temp4 = temp2 * temp3;
temp5 = (unsigned long) temp4;
func(temp5)

前四个临时值会自动输入到每个运算符的结果类型(这是该运算符的常用算术转换产生的类型)。

  1. temp1 = herp + derp;

    herpderp都是unsigned long;不需要转换;结果类型为unsigned long

  2. temp2 = temp1 * durr;

    temp1durr都是unsigned long;不需要转换;结果类型为unsigned long

  3. temp3 = sizeof wut;

    sizeof始终返回size_t,因此这是结果的类型。

  4. temp4 = temp2 * temp3

    temp2unsigned longtemp3size_t,在示例平台上为unsigned long long。这需要将temp2转换为unsigned long long,之后结果类型为unsigned long long

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

    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)
    

    其中abc都是相同类型,比size_t窄。

    与上述逻辑一样,(a + b) * c的评估方式为abc。然后,该结果会提升为size_t,以便可以乘以x的大小。但是,(a + b) * c肯定有可能在转换之前溢出,使最终结果无效。坚持用size_t操作数执行整个计算会更安全。这可以通过添加单个显式转换来实现:

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