我制作了这个程序来测试任意整数文字要评估的数据类型。该程序的灵感来自阅读有关StackOverflow的其他一些问题。
How do I define a constant equal to -2147483648?
Why do we define INT_MIN as -INT_MAX - 1?
(-2147483648> 0) returns true in C++?
在这些问题中,我们遇到一个问题:程序员想将INT_MIN编写为-
2 ^ 31 ,但是 2 ^ 31 实际上是一个文字-
是一元否定运算符。由于INT_MAX通常为 2 ^ 31-1 具有32位int,因此文字 2 ^ 31 不能表示为int
,因此得到提升为更大的数据类型。第三个问题的第二个答案具有一个图表,根据该图表可以确定整数文字的数据类型。编译器从顶部开始向下浏览,直到找到适合字面量的数据类型。
Suffix Decimal constants none int long int long long int
================================================ ========================
在我的小程序中,我定义了一个宏,它将以C字符串形式返回变量,文字或表达式的“名称”。基本上,它返回在宏内部传递的文本,与在代码编辑器中看到的完全相同。我用它来打印文字表达式。
我想确定表达式的数据类型以及它的计算结果。我必须对如何做到这一点有点聪明。我们如何确定C中变量或表达式的数据类型?我得出的结论是,仅需要两个“位”的信息:数据类型的宽度(以字节为单位)和数据类型的有符号性。
我使用sizeof()
运算符来确定数据类型的宽度(以字节为单位)。我还使用另一个宏来确定数据类型是否已签名。 typeof()
是GNU编译器扩展,它返回变量或表达式的数据类型。但是我无法读取数据类型。我将-1
转换为该数据类型。如果它是带符号的数据类型,它将仍然是-1
,如果是无符号的数据类型,它将成为该数据类型的UINT_MAX
。
#include <stdio.h> /* C standard input/output - for printf() */
#include <stdlib.h> /* C standard library - for EXIT_SUCCESS */
/**
* Returns the name of the variable or expression passed in as a string.
*/
#define NAME(x) #x
/**
* Returns 1 if the passed in expression is a signed type.
* -1 is cast to the type of the expression.
* If it is signed, -1 < 0 == 1 (TRUE)
* If it is unsigned, UMax < 0 == 0 (FALSE)
*/
#define IS_SIGNED_TYPE(x) ((typeof(x))-1 < 0)
int main(void)
{
/* What data type is the literal -9223372036854775808? */
printf("The literal is %s\n", NAME(-9223372036854775808));
printf("The literal takes up %u bytes\n", sizeof(-9223372036854775808));
if (IS_SIGNED_TYPE(-9223372036854775808))
printf("The literal is of a signed type.\n");
else
printf("The literal is of an unsigned type.\n");
return EXIT_SUCCESS;
}
如您所见,我正在测试-
2 ^ 63 以查看它是什么数据类型。问题在于,如果我们可以相信图表,则在ISO C90中,整数文字的“最大”数据类型似乎为long long int
。众所周知,在现代的64位系统上,long long int
的数值范围从 -2 ^ 63 到 2 ^ 63-1 。但是,上面的-
是一元否定运算符,实际上不是整数文字的一部分。我正在尝试确定 2 ^ 63 的数据类型,对于long long int
来说太大了。我试图在C的类型系统中引起错误。这是有意的,并且仅用于教育目的。
我正在编译并运行该程序。我使用-std=gnu99
而不是-std=c99
,因为我使用的是typeof()
,它是GNU编译器扩展,实际上不是ISO C99标准的一部分。我得到以下输出:
$ gcc -m64 -std=gnu99 -pedantic experiment.c
$
$ ./a.out
The literal is -9223372036854775808
The literal takes up 16 bytes
The literal is of a signed type.
我看到等效于 2 ^ 63 的整数立即数的值为16个字节的带符号整数类型!据我所知,C编程语言中没有这种数据类型。我也不知道任何具有16字节寄存器来存储此类右值的Intel x86_64处理器。如果我错了,请纠正我。解释这是怎么回事?为什么没有溢出?另外,是否可以在C中定义16字节数据类型?你会怎么做?
答案 0 :(得分:3)
经过一番挖掘,这就是我所发现的。我假定在这种情况下C和C ++的行为类似,所以我将代码转换为C ++。我想创建一个模板函数以能够接受任何数据类型。我使用ImmutableList<T>
,它是GNU编译器扩展,它返回包含函数的“原型”的C字符串,我的意思是返回类型,名称和输入的形式参数。我对形式参数感兴趣。使用这种技术,我能够准确确定传入的表达式的数据类型,而无需猜测!
__PRETTY_FUNCTION__
编译并运行,我得到以下输出:
/**
* This is a templated function.
* It accepts a value "object" of any data type, which is labeled as "T".
*
* The __PRETTY_FUNCTION__ is a GNU compiler extension which is actually
* a C-string that evaluates to the "pretty" name of a function,
* means including the function's return type and the types of its
* formal parameters.
*
* I'm using __PRETTY_FUNCTION__ to determine the data type of the passed
* in expression to the function, during the runtime!
*/
template<typename T>
void foo(T value)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
foo(5);
foo(-9223372036854775808);
我看到传入的表达式的类型为$ g++ -m64 -std=c++11 experiment2.cpp
$
$ ./a.out
void foo(T) [with T = int]
void foo(T) [with T = __int128]
。显然,这是GNU编译器特定的扩展,不是C标准的一部分。
https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/_005f_005fint128.html
https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/C-Extensions.html#C-Extensions
答案 1 :(得分:3)
启用所有警告后-Wall gcc将发出warning: integer constant is so large that it is unsigned
警告。 Gcc将此整数常量分配为类型__int128
和sizeof(__int128) = 16
。
您可以使用_Generic宏进行检查:
#define typestring(v) _Generic((v), \
long long: "long long", \
unsigned long long: "unsigned long long", \
__int128: "__int128" \
)
int main()
{
printf("Type is %s\n", typestring(-9223372036854775808));
return 0;
}
Type is __int128
或带有printf的警告:
int main() {
printf("%s", -9223372036854775808);
return 0;
}
将编译并显示警告:
warning: format '%s' expects argument of type 'char *', but argument 2 has type '__int128' [-Wformat=]
答案 2 :(得分:3)
您的平台可能有__int128
,而9223372036854775808
正在获取该类型。
让C编译器打印类型名称的简单方法是:
int main(void)
{
#define LITERAL (-9223372036854775808)
_Generic(LITERAL, struct {char x;}/*can't ever match*/: "");
}
在我的x86_64 Linux上,以上代码生成了一个
error: ‘_Generic’ selector of type ‘__int128’ is not compatible with any association
错误消息,表示__int128
确实是文字的类型。
(这样,warning: integer constant is so large that it is unsigned
是错误的。嗯,gcc并不完美。)