9

我是C-Headers的新手-stdint.hinttypes.h. 我正在尝试一些代码以了解其uint8_t工作原理。但是,它似乎遇到了问题。

我已经声明了 4 个uint8_t整数,边界值分别为 0、255、256、-1,并对其执行了一些简单的算术运算。我这样做是因为我想知道c 编译器(我在 linux 上使用)会生成哪些错误/警告。gcc 5.4.0如果没有,我很想知道输出是什么样子的。下面给出的代码。

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

int main() {
        int a = 10;
        printf("%d\n", a++);
        printf("%d\n\n", a);

        // 8 bit unsigned integer -> range[0, 255]
        uint8_t ua81=0, ua82=255, ua83=256, ua84=-1;
        printf("--------STDINT.H----uint8_t--DEMO--------\nua81 = %" PRIu8 "\n", ua81);
        printf("ua82 = %" PRIu8 "\nua83 = %" PRIu8 "\nua84 = %" PRIu8 "\n\n", ua82, ua83, ua84);
        printf("ua81+1 = %" PRIu8 "\nua82-3 = %" PRIu8 "\nua83-4+7 = %" PRIu8 "\nua84-1+20 = %" PRIu8 "\n----------\n\n", ua81+1, ua82-3, ua83-4+7, ua84-1+20);

        return 0;
}

这段代码的输出如下:

vagrant@ubuntu-xenial:~/Documents/Coding Practice/p_c$ vi stdint_h.c
vagrant@ubuntu-xenial:~/Documents/Coding Practice/p_c$ gcc -Wall stdint_h.c -o a
stdint_h.c: In function ‘main’:
stdint_h.c:11:33: warning: large integer implicitly truncated to unsigned type [-Woverflow]
  uint8_t ua81=0, ua82=255, ua83=256, ua84=-1;
                                 ^
vagrant@ubuntu-xenial:~/Documents/Coding Practice/p_c$ ./a
10
11

--------STDINT.H----uint8_t--DEMO--------
ua81 = 0
ua82 = 255
ua83 = 0
ua84 = 255

ua81+1 = 1
ua82-3 = 252
ua83-4+7 = 3
ua84-1+20 = 274
----------

如前所述,我正在使用带有gcc 5.4.0编译器的 Linux 机器。

vagrant@ubuntu-xenial:~/Documents/Coding Practice/p_c$ gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

我不明白为什么,对于变量ua84,值0在达到之后没有滚动到255,而对于ua83. 我认为,考虑到 255 之后的翻转,该值ua84应该是18.

我觉得这与编译时生成的与变量有关的隐式截断有关。我不知道这到底有什么问题。我也想知道我仍然想继续使用的方法。warningua83uint8_t

4

3 回答 3

10

由于隐式提升,ua84-1+20将是一个int,它的值是 274。您应该明确地将结果转换为uint8_t使其变为 18。

printf没有足够的魔力来根据格式(*)转换其参数。它只期望它收到它需要的东西并提取传递的参数。所以在这里你应该写:

    printf("ua81+1 = %" PRIu8 "\nua82-3 = %" PRIu8 "\nua83-4+7 = %" PRIu8 "\nua84-1+20 = %"
            PRIu8 "\n----------\n\n", (uint8_t)(ua81+1), (uint8_t)(ua82-3), (uint8_t)(ua83-4+7),
            (uint8_t)(ua84-1+20));

(*)更准确地说,这是格式说明符的问题。在第 7.21.6.1 段中,C11 的 fprintf 函数 §7 草案 n1570 说:

hh指定后面的 d、i、o、u、x 或 X 转换说明符应用于有符号字符或无符号字符参数(该参数将根据整数提升进行提升,但其值应转换为有符号字符或打印前的无符号字符);...

因此,如果您明确使用%hhu,您将获得 18 (即使已通过 274 )。但不幸的是,来自 inttype.h 的宏只需要正确显示预期范围的值,因此许多实现都转换PRIu8u(感谢 Eric Postpischil 注意到这一点)。

于 2019-05-09T08:36:23.770 回答
2

printf函数族非常危险且不稳定,不存在类型安全性。因此,如果您提供的参数不是您使用格式说明符指定的类型,则会调用未定义的行为。这是库的程序员会告诉你的。

表达式ua84-1+20导致隐式类型提升int,请参阅隐式类型提升规则。所以你用 对 printf 撒谎PRIu8,告诉它等待uint8_t你通过一个int.

可以通过转换回预期的类型来修复它:(uint8_t)(ua84-1+20),它将按预期打印 18 。

值得注意的是,所有像这样的可变参数函数printf也有一种类似的隐式类型提升,称为“默认参数提升”。int无论您做什么,这些实际上都会将参数转换为。但是图书馆希望这种提升会发生,所以当你键入PRIu8后跟时(uint8_t)whatever,他们希望在(int)(uint8_t)whatever内部处理。

274但是,图书馆打印,UB 与否是非常可疑的。这表明该printf实现实际上并未将参数转换回uint8_t内部。库如何证明这是一个很好的问题,因为我们可以看到它会导致错误和不那么坚固的代码。

于 2019-05-09T08:48:01.920 回答
2

该表达式ua84-1+20使变量ua84经历称为整数提升的过程:它变成了一个int,然后整个表达式的计算结果为一个int

然后它被打印为一个无符号整数 - 你可能会看到它PRIu8被定义为"u",这当然期望unsigned int(并且即使传递了一个uint8_t由于可变参数函数如何提升其整数参数(uint8_t值被提升为int,然后得到被视为unsigned intby printf()))。

详细信息:https ://en.cppreference.com/w/c/language/conversion

于 2019-05-09T08:34:52.213 回答