什么是16字节有符号整数数据类型?”

时间:2018-07-10 21:47:06

标签: c++ c types integer

我制作了这个程序来测试任意整数文字要评估的数据类型。该程序的灵感来自阅读有关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字节数据类型?你会怎么做?

3 个答案:

答案 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标准的一部分。

Why isn't there int128_t?

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

How is a 16 byte data type stored on a 64 bit machine

答案 1 :(得分:3)

启用所有警告后-Wall gcc将发出warning: integer constant is so large that it is unsigned警告。 Gcc将此整数常量分配为类型__int128sizeof(__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并不完美。)