【问题标题】:Unexpected macro evaluation意外的宏观评估
【发布时间】:2016-10-29 07:41:49
【问题描述】:

为什么会打印ffffffff

#define MYMACRO(n) (((uint16_t)0 - 1) >> (16 - (n)))
std::cout << std::hex << MYMACRO(1);

虽然这会打印 1?

#define MYMACRO(n) (((uint32_t)0 - 1) >> (32 - (n)))
std::cout << std::hex << MYMACRO(1);

我用 GCC 和 cpp.sh 尝试过。

【问题讨论】:

  • 通常的算术转换。据推测,uint16_t 被提升为int(恰好是 32 位)。
  • 这是因为 1 的类型为 int
  • 至关重要的是,在您的平台上,int 是比 uint16_t 更大/更宽的整数类型。

标签: c++ macros


【解决方案1】:

解释在type-promotion-in-c

(§6.3.1.1 布尔值、字符和整数):

如果一个int可以表示原始类型的所有值,则将该值转换为一个int; ...这些被称为整数促销。 整数提升不会改变所有其他类型。

因此,对于您的第一种情况,((uint16_t)0 - 1) 被转换为 int 以满足减法的需要。那么右移操作是算术右移而不是逻辑右移。

【讨论】:

  • 有没有办法在 uint16_t 中进行此计算,同时获得预期的 1?
  • 是的:也将1 转换为uint16_t
  • @user268396:不会有什么不同。
  • @Silicomancer:无法在uint16_t 中进行计算。无论您做什么,计算本身都将在int 中完成。您可以做的是将结果强制返回到 uint16_t 减法之后。即使在那之后它会重新回到int,但至少它不会落入负面区域。例如。使用((uint16_t) (0 - 1) &gt;&gt; (16 - (n))),与((uint16_t) -1 &gt;&gt; (16 - (n))) 相同。或者,更好的是((uint16_t) ((uint16_t) -1 &gt;&gt; (16 - (n))))
  • @AnT 可以强制转换为 unsigned int 而不是让编译器提升为 int 以获得所需的行为。
【解决方案2】:

Integral Promotions 发生在 C++ 中。尽管如此,实际测试这些表达式对于了解幕后发生的事情并不是一个坏主意......

#include <typeinfo>
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;

int main(){
#define MYMACRO(n) (((uint16_t)0 - 1) >> (16 - (n)))
    std::cout << std::hex << MYMACRO(1);

    std::cout << "\n-----\n";
#undef MYMACRO
#define MYMACRO(n) (((uint32_t)0 - 1) >> (32 - (n)))
    std::cout << std::hex << MYMACRO(1);

    std::cout << "\n--++++--\n";
    std::cout << boost::typeindex::type_id<decltype((uint16_t)0)>().pretty_name() << std::endl;
    std::cout << boost::typeindex::type_id<decltype((uint16_t)0 - 1)>().pretty_name() << std::endl;    
    std::cout << boost::typeindex::type_id<decltype((uint32_t)0 - 1)>().pretty_name() << std::endl;
}

移位运算符之后的表达式的 integer 类型在我们的例子中是不相关的。从上述程序的输出可以看出Live On Coliru

ffffffff
-----
1
--++++--
unsigned short
int
unsigned int

总结如下:

  • 子表达式:(uint16_t)0 在大多数平台上通常会产生unsigned short

  • 表达式:(uint16_t)0 - 1 产生int 的类型;因为整数提升规则。 1int 类型的整数常量

  • 表达式:(uint32_t)0 - 1) 产生unsigned int 的类型;还是因为usual arithmetic conversionsunsigned int 被认为大于 int

【讨论】:

  • 有没有办法在 uint16_t 中进行此计算,同时获得预期的 1?
  • @Silicomancer,是的,你可以......将整个表达式转换为uint16_t 类型......通过像这样定义你的宏:#define MYMACRO(n) ( (uint16_t)((uint16_t)((uint16_t)0 - 1) &gt;&gt; (16 - (n))))
  • @M.M 通过说“C++ 表达式的结果类型可以不同于一个 子表达式 到它的封闭 表达式” - 我接受了从单独处理子表达式的角度来看。例如,tenary ?: operator 也有一些有趣的规则......单独处理子表达式是错误的吗?尽管如此,我还是删除了该声明。
  • @MM,谢谢。更正了最后的要点usual arithmetic conversions...我也在学习。 :-)。你能在之前的评论中回答我的问题吗?谢谢
  • @WhiZTiM C 和 C++ 中表达式的优点之一是它们独立于上下文 - 表达式的类型不会因使用位置而改变
猜你喜欢
  • 1970-01-01
  • 2012-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-19
  • 1970-01-01
  • 1970-01-01
  • 2012-11-19
相关资源
最近更新 更多