如何编写一些代码来判断变量是否已签名
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 \
)
如果你想做一些更复杂的事情,你也可以委托给函数。我做了一个简单的例子,其中 is_signed
returns 一个 signs
对象包括所提供变量的 type
和 value
的符号。我已经排除了不需要的参数名称,这在 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)
您可以使用 _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 的补码下)。因为其他位无关紧要,只需使用 ~
将它们全部更改即可。 (它有效。只需获取一个样本编号,无论是否为负数,然后在记事本上计算出来。)
第二个只是使用类型转换。
我想编写一个函数(或宏)来判断变量是否已签名。也就是说,如何判断给定变量的 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 \
)
如果你想做一些更复杂的事情,你也可以委托给函数。我做了一个简单的例子,其中 is_signed
returns 一个 signs
对象包括所提供变量的 type
和 value
的符号。我已经排除了不需要的参数名称,这在 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)
您可以使用 _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 的补码下)。因为其他位无关紧要,只需使用 ~
将它们全部更改即可。 (它有效。只需获取一个样本编号,无论是否为负数,然后在记事本上计算出来。)
第二个只是使用类型转换。