【问题标题】:How do I tell if a C integer variable is signed?如何判断 C 整数变量是否有符号?
【发布时间】:2009-09-11 17:15:24
【问题描述】:

作为练习,我想编写一个宏来告诉我一个整数变量是否有符号。这是我到目前为止所拥有的,如果我在使用 gcc -fsigned-char 或 -funsigned-char 的 char 变量上尝试这个,我会得到我期望的结果。

#define ISVARSIGNED(V) (V = -1, (V < 0) ? 1 : 0) 

这是便携式的吗?有没有办法在不破坏变量值的情况下做到这一点?

【问题讨论】:

  • 这是一个奇怪的问题,但我对您对这些信息的预期用途更感兴趣。有机会分享吗?
  • @jeffamaphone:实际上,这就是模板在 C++ 中大放异彩的地方。
  • @stephentyrone - 我认为这只是好奇心,基于 OP 问题的前三个词。我无法完全想象这种信息的实际应用。
  • @Chris @stephentyrone 这是来自 Peter Van Der Linden 的 Expert C Programming 一书中的练习

标签: c gcc integer c-preprocessor


【解决方案1】:
#define ISVARSIGNED(V)  ((V)<0 || (-V)<0 || (V-1)<0)

不改变 V 的值。第三个测试处理 V == 0 的情况。

在我的编译器 (gcc/cygwin) 上,这适用于 int 和 long,但不适用于 char 或 short。

#define ISVARSIGNED(V) ((V)-1<0 || -(V)-1<0)

也在两个测试中完成这项工作。

【讨论】:

  • 我见过的最好的。便携,符合标准,据我所知准确。
  • 不区分有符号/无符号短和字符。在评估 表达式时,这些类型被提升为 int。
  • 如果你想让它适用于short 和char,你可以在使用它之前将变量转换为char。那应该可以处理大多数溢出问题。我认为...
【解决方案2】:

如果您使用的是 GCC,则可以使用 typeof 关键字来不覆盖该值:

#define ISVARSIGNED(V) ({ typeof (V) _V = -1; _V < 0 ? 1 : 0 })

这会创建一个临时变量_V,它的类型与V 相同。

至于便携性,我不知道。它可以在二进制补码机器上工作(也就是你的代码很可能会在上面运行的所有东西),我相信它也可以在一个补码机器和符号和大小机器上工作。附带说明一下,如果您使用 typeof,您可能需要将 -1 转换为 typeof (V) 以使其更安全(即不太可能触发警告)。

【讨论】:

  • 在 C++ 中,无论整数表示如何(对于 n 位,值是 2^n - 1),它都保证符合标准。我手头没有 C 标准(其中任何一个)。
  • 我也不是,但我记得在 Wikipedia(所有事物的来源:P)上读到 C 标准允许我列出的三种表示形式。并不是说有人再使用它们了……
【解决方案3】:
#define ISVARSIGNED(V) ((-(V) < 0) != ((V) < 0))

不破坏变量的值。但不适用于 0 值。

怎么样:

#define ISVARSIGNED(V) (((V)-(V)-1) < 0)

【讨论】:

  • #define ISVARSIGNED(V) ((-V
  • 是的,我不应该 C&Pd 那些多余的东西 ;-)
  • @plinth,你忘记了“V”周围的“额外”括号,它可以避免疯狂的宏扩展
  • V == 0 时失败。此外,如果 V 具有最大可能的负值,它很可能会失败,因为没有正等值。
  • #define ISVARSIGNED(V) ((-(V)
【解决方案4】:

所有“否定”答案的不同方法:

#define ISVARSIGNED(V) (~(V^V)<0)

这样就不需要对不同的 V 值有特殊情况,因为 ∀ V ∈ ℤ, V^V = 0。

【讨论】:

    【解决方案5】:

    这个简单的解决方案没有副作用,包括只引用一次 v 的好处(这在宏中很重要)。我们使用 gcc 扩展“typeof”来获取 v 的类型,然后将 -1 强制转换为该类型:

    #define IS_SIGNED_TYPE(v)   ((typeof(v))-1 <= 0)
    

    它是

    【讨论】:

      【解决方案6】:

      有符号/无符号数学的一个显着特征是,当您右移一个有符号数时,最高有效位会被复制。移位无符号数时,新位为 0。

      #define HIGH_BIT(n) ((n) & (1 << sizeof(n) * CHAR_BITS - 1))
      #define IS_SIGNED(n) (HIGH_BIT(n) ? HIGH_BIT(n >> 1) != 0 : HIGH_BIT(~n >> 1) != 0
      

      所以基本上,这个宏使用条件表达式来确定是否设置了数字的高位。如果不是,宏通过按位否定数字来设置它。我们不能进行算术否定,因为 -0 == 0。然后我们右移 1 位并测试是否发生符号扩展。

      这假设了 2 的补码算术,但这通常是一个安全的假设。

      【讨论】:

      • 你有关于位移行为的这些假设的来源吗?
      • C99 标准(第 6.5.7 节)表示有符号负值的右移是实现定义的。我的解释是在 2 的补码机器上会有一个符号扩展。由于 C 不是特定于 2 的补码架构,所以他们不会出来说这个。
      • 使用CHAR_BITS 而不是魔法8,你会更PC。 (是的,我知道,只有极少数人不在一个字节为 8 位的机器上工作。仍然。)
      • @sbi 编辑为使用 CHAR_BITS。不过我很好奇:您正在使用什么机器,每字节没有 8 位?它有多少位?我知道这样的系统很久以前就存在了,但我不知道任何现代系统都没有 8 位字节。
      • @sbi:有各种 16、24(!) 或 32 位字符的 DSP。
      【解决方案7】:

      到底为什么需要它作为宏?模板非常适合:

      template <typename T>
      bool is_signed(T) {
          static_assert(std::numeric_limits<T>::is_specialized, "Specialize std::numeric_limits<T>");
          return std::numeric_limits<T>::is_signed;
      }
      

      这对于所有基本的整数类型都是开箱即用的。它也会在编译时在指针上失败,而仅使用减法和比较的版本可能不会。

      编辑:哎呀,这个问题需要 C。不过,模板是不错的方法:P

      【讨论】:

      • 一个 C 整数,而不是 C++ 整数。
      猜你喜欢
      • 1970-01-01
      • 2011-11-13
      • 1970-01-01
      • 1970-01-01
      • 2012-05-09
      • 1970-01-01
      • 1970-01-01
      • 2013-11-05
      • 1970-01-01
      相关资源
      最近更新 更多