3

对于 stdint.h,我必须参考此页面:

http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/stdint.h.html

我完全惊讶地发现我可以在定义为 uint64_t 类型的变量中存储一个负数。我脑子里有一个想法,即无符号数据类型总是一个正数,如果我试图在其中存储一个负数,我会得到一个错误。以下让我感到惊讶:

#define _XOPEN_SOURCE 600

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include <errno.h>

int main ( int argc, char *argv[] ) { 

    uint64_t a_big_positive_number;
    unsigned long another_big_positive_number;

    if ( argc < 2 ) {
        printf ( "usage : %s some_integer\n", argv[0] );
        exit( EXIT_FAILURE );
    }

    a_big_positive_number = 0;

    a_big_positive_number = strtol( argv[1], (char **)NULL, 10);
    fprintf ( stderr, "%i : %s\n", errno, strerror(errno) );
    another_big_positive_number = a_big_positive_number;
    fprintf ( stderr, "%i : %s\n", errno, strerror(errno) );

    return ( EXIT_SUCCESS );

}

当我用完整的 C99 迂腐标志编译它时,我得到了一个二进制文件,它做了以下令人惊讶的事情:

$ ./boffo -12345
0 : Error 0
0 : Error 0

真的吗 ?

我在调试器中运行它,我看到确实是的,uint64_t 非常适合有一个负数并且接近我可以告诉我可以在那里填充我想要的任何 64 位:

(dbx) run -12345
Running: boffo -12345 
(process id 16270)
stopped in main at line 15 in file "boffo.c"
   15       if ( argc < 2 ) {
(dbx) step      
stopped in main at line 20 in file "boffo.c"
   20       a_big_positive_number = 0;

首先,我想看看这个变量在内存中的位置(我猜在堆栈上):

(dbx) print &a_big_positive_number
&a_big_positive_number = 0xffffffff7ffff620

太好了...现在获取 argv[1] 输入字符串并将其转换为长整数:

(dbx) step                          
stopped in main at line 22 in file "boffo.c"
   22       a_big_positive_number = strtol( argv[1], (char **)NULL, 10);
(dbx) step
stopped in main at line 23 in file "boffo.c"
   23       fprintf ( stderr, "%i : %s\n", errno, strerror(errno) );

我们现在在那个地址的内存中有什么?

(dbx) x &a_big_positive_number / 4 x
0xffffffff7ffff620:      0xffff 0xffff 0xffff 0xcfc7

无符号 64 位数据类型中的负数。完全没有错误!

(dbx) cont                          
Reading libc_psr.so.1
0 : Error 0
0 : Error 0

execution completed, exit code is 0
(dbx) quit

所以问题是,当我似乎可以使用 64 位位域或八个无符号字符的数组或任何足够大的内存块时,stdint.h 中无符号整数的用途或价值是什么?这里的价值在哪里?便携性?

/************** COMMENT AFTER SEEING GREAT ANSWERS BELOW ************/

下面的人的好答案教育了我,我看到 stdint.h 类型可以跨平台移植,它们允许更大范围的正整数值。存储在变量中的数据的解释是我要处理的事情,而不是期望我的编译器或调试器神秘地或神奇地知道我们在这里只使用正数。

实际上,我认为最安全的做法是坚持使用 C99 标准中的 uint32_t ,然后这样做以获得正确的正值:

 a_big_positive_number = (uint32_t) labs( strtol( argv[1], (char **)NULL, 10) );

为了超级“腰带和吊带”安全,我应该在接受之前自己检查 argv[1]。谁知道呢,可能是负数,也可能是垃圾数据。

4

2 回答 2

5

有符号整数类型到无符号整数类型的转换在 C 中完全定义,并且隐式发生在 中a_big_positive_number = strtol(…);,就像您编写了a_big_positive_number = (uint64_t) strtol(…);.

C99 标准的 6.3.1.3:2 条款适用:

否则,如果新类型是无符号的,则在新类型可以表示的最大值的基础上重复加减一,直到该值在新类型的范围内。

(在从另一种类型转换的情况下)

并且 6.5.16.1:2 子句说转换发生在赋值:

在简单赋值 (=) 中,右操作数的值被转换为赋值表达式的类型,并替换存储在左操作数指定的对象中的值。

关于 的值uint64_t,我不确定你在问什么。类型uint64_t是正好 64 位的无符号类型。unsigned long long bf:64;位域不能保证在内存中只占用 64 位,八个无符号字符的数组也不能保证(并且运算符不能直接在后者上工作)。

于 2013-08-04T18:38:54.023 回答
3

有符号/无符号类型的原因不是存储,而是关于如何解释位模式。一个例子:

long signedNumber = -1l;
assert(signedNumber < 0);    //Bit pattern 0xffffffffffffffff is interpreted as negative, so this succeeds.
unsigned long unsigendNumber = (unsigned long)signedNumber;    //same bit pattern ...
assert(unsignedNumber >= 0ul);    //... but interpreted as an unsigned, it's roughly 18e18.

实际上,无符号数和有符号数的加法、减法和乘法是相同的,硬件并不关心。它只是用于编译器需要知道的一些特殊操作,例如比较,是否必须将第一位解释为符号位。

于 2013-08-04T18:43:59.653 回答