如何编写一些代码来判断变量是否已签名

How to write some code to tell if a variable is signed or not

我想编写一个函数(或宏)来判断变量是否已签名。也就是说,如何判断给定变量的 type 是否有符号,而不是它的 value.

您可以创建一个 _Generic 宏:

#define is_signed(X) _Generic((X), \
                short : true, \
                int : true, \
                long : true, \
                long long : true, \
                unsigned short : false, \
                unsigned int : false, \
                unsigned long : false, \
                unsigned long long : false, \
                float : true, \
                double : true, \
                long double : true \
               )

Demo


如果你想做一些更复杂的事情,你也可以委托给函数。我做了一个简单的例子,其中 is_signed returns 一个 signs 对象包括所提供变量的 typevalue 的符号。我已经排除了不需要的参数名称,这在 C2x 标准中是允许的。如果需要,您可以添加虚拟名称。

typedef struct { bool type; bool value; } signs;

signs Short(short x) { signs r={true, x < 0}; return r; }
signs Int(int x) { signs r={true, x < 0}; return r; }
signs Long(long x) { signs r={true, x < 0}; return r; }
signs Longlong(long long x) { signs r={true, x < 0}; return r; }

signs UShort(unsigned short) { signs r={false, false}; return r; }
signs UInt(unsigned int) { signs r={false, false}; return r; }
signs ULong(unsigned long) { signs r={false, false}; return r; }
signs ULonglong(unsigned long long) { signs r={false, false}; return r; }

signs Float(float x) { signs r={true, x < 0.f}; return r; }
signs Double(double x) { signs r={true, x < 0.}; return r; }
signs LongDouble(long double x) { signs r={true, x < 0.L}; return r; }

#define is_signed(X) _Generic((X), \
                short : Short, \
                int : Int, \
                long : Long, \
                long long : Longlong, \
                unsigned short : UShort, \
                unsigned int : UInt, \
                unsigned long : ULong, \
                unsigned long long : ULonglong, \
                float : Float, \
                double : Double, \
                long double : LongDouble \
               )(X)

Demo

您可以使用 _Generic:

对明确列出的类型执行此操作
#include <limits.h>
#include <stdio.h>

int main(void)
{
    enum Signedness { Signed, Unsigned, Unknown };
    char x;
    enum Signedness s = _Generic(x,
            char:               CHAR_MIN < 0 ? Signed : Unsigned,
            signed char:        Signed,
            unsigned char:      Unsigned,
            short:              Signed,
            unsigned short:     Unsigned,
            int:                Signed,
            unsigned:           Unsigned,
            long:               Signed,
            unsigned long:      Unsigned,
            long long:          Signed,
            unsigned long long: Unsigned,
            default:            Unknown
            );
    switch (s)
    {
        case Signed:
            printf("The type is signed.\n");
            break;
        case Unsigned:
            printf("The type is unsigned.\n");
            break;
        case Unknown:
            printf("It is not known whether the type is signed or unsigned.\n");
            break;
    }
}

这里有一个不完美的方法:

#define isSigned(x) ((x) < 0 ? 1 : (-(x) < 0 ? 1 : 0))

如果数字比较小于0,则其类型显然是有符号的。否则,它是正数,但如果它的负数小于 0,它又是有符号的。

但是,哎呀,没那么简单,因为也有可能值为0! (谢谢,@JohnBollinger。)如果它恰好为 0,那么如果 x-1 为负,则它已签名。

#define isSigned(x) ((x) < 0 ? 1 : (x) > 0 ? (-(x) < 0 ? 1 : 0) : ((x) - 1 < 0 ? 1 : 0))

(不幸的是,处理 x == 0 案例的额外子句将其从“勉强可读”变为“几乎完全混淆”。)

无论如何,这假设变量确实有一个值(即已经初始化)。

不幸的是,它对小于 int 的类型根本不起作用,因为在那种情况下,即使 x 是无符号的,-x 也是负数。

示范:

int main()
{
    char c = 1;
    unsigned char uc = 1;
    signed char sc = 1;
    int i = -1;
    unsigned u = 1;
    float f = 1;
    double d = -1;

    printf("char: %ssigned\n",          isSigned(c) ? "" : "un");
    printf("signed char: %ssigned\n",   isSigned(sc) ? "" : "un");
    printf("unsigned char: %ssigned\n", isSigned(uc) ? "" : "un");
    printf("int: %ssigned\n",           isSigned(i) ? "" : "un");
    printf("unsigned: %ssigned\n",      isSigned(u) ? "" : "un");
    printf("float: %ssigned\n",         isSigned(f) ? "" : "un");
    printf("double: %ssigned\n",        isSigned(d) ? "" : "un");
}

我提供了两个适用于所有整数类型的解决方案,不需要您列出所有可能的整数类型。

使用 C 扩展的解决方案

这在 GCC 中工作正常。它使用 typeof 扩展名。

#define SIGNFIND(x) ((typeof(x))-1 < 1)

普通 C 解决方案

这在不使用 C 扩展的情况下工作。但是,这似乎很危险,因为它会(暂时)修改变量的一个字节。此外,它假定您的系统使用小端字节顺序,但这很容易修复(只需扩展它以将变量的所有位设置为 1)。此外,如果您 运行 执行此操作时变量尚未初始化,则您可能正在调用未定义的行为。

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

uint8_t * signfind_ptr;
uint8_t signfind_save;

bool signfind_start(uint8_t * v)
{
  signfind_ptr = v;
  signfind_save = *signfind_ptr;
  *signfind_ptr = 0xFF;
  return false;
}

bool signfind_end(bool s)
{
  *signfind_ptr = signfind_save;
  return s;
}

#define SIGNFIND(x) (signfind_start((uint8_t *)&(x) + sizeof(x) - 1), signfind_end((x) < 1))

int main()
{
  char v_char = 0;
  unsigned char v_uchar = 0;
  int v_int = 0;
  uint64_t v_u64 = 0;

  printf("char: %d\n", SIGNFIND(v_char));
  printf("unsigned char: %d\n", SIGNFIND(v_uchar));
  printf("int: %d\n", SIGNFIND(v_int));
  printf("uint64_t: %d\n", SIGNFIND(v_u64));
}

首先,这只能通过宏来实现,因为函数的参数具有预定义的类型。

如果你传递一个变量:#define ISUNSIGNED(a) (a >= 0 && ~a >=0)
如果你传递的是一个类型:#define ISUNSIGNED(type) ((type)0 - 1 > 0)

首先:无符号值的基本特征是不能为负。有符号变量的特点是对它的最高有效位进行补码会改变符号(在 2 的补码下)。因为其他位无关紧要,只需使用 ~ 将它们全部更改即可。 (它有效。只需获取一个样本编号,无论是否为负数,然后在记事本上计算出来。)

第二个只是使用类型转换。