WeHack BBS
C/C++ 下对无符号数计算 abs 的行为 - 可打印的版本

+- WeHack BBS (https://bbs.wehack.space)
+-- 版块: 计算机技术 (https://bbs.wehack.space/forum-5.html)
+--- 版块: 程序设计讨论区 (https://bbs.wehack.space/forum-14.html)
+--- 主题: C/C++ 下对无符号数计算 abs 的行为 (/thread-156.html)



C/C++ 下对无符号数计算 abs 的行为 - vimacs - 05-08-2020

因为无符号数是非负的,那么对它取绝对值显然是它本身。所以C/C++中的绝对值函数的参数类型是有符号的。但是,如果不小心传入了无符号整数,会发生什么呢?

首先看C语言。abs函数在stdlib.h里面,函数原型是 int abs(int). 那么无论怎么用 abs 函数,C 语言都是先把参数转为 int,再调用 abs. 事实上,对 uint8_t, uint16_t, uint32_t 求 abs,编译器都不会有警告。如果没包含 stdlib.h 头文件,GCC 会使用内建的 abs 函数,此时会有一个 uint32_t 和 int 不匹配的警告(-Wbuiltin-declaration-mismatch)。

C++ 因为有函数重载,会复杂一些。实际上就是把浮点和 long, long long 类型的绝对值函数重载了。

首先把头文件从 stdlib.h 改成 cstdlib,结果没发现 C++ 编译器报错。但是如果有 using namespace std 的话,那么 C++ 编译器会报错,针对的是 abs(uint32_t&),认为它有歧义。C++ 的 abs 有下列这些函数原型:

- abs(int): C 标准库的 stdlib.h 里的 abs
- std::abs(__float128)
- std::abs(__int128)
- std::abs(long double)
- std::abs(float)
- std::abs(double)
- std::abs(long long int)
- std::abs(long int)

就是说 std::abs 把 int 类型之外的所有有符号类型都覆盖了。而 uint32_t 作为 abs 的参数时,编译器不知道应该把它转成什么类型,于是就报了错误。

但是 uint8_t 和 uint16_t 并没有这个问题,事实上对它们用 abs 的时候,都会把它们转成 int 类型,而无符号数转成更长的整数的时候,用的是0扩展,于是对它们求绝对值就是它们本身。


RE: C/C++ 下对无符号数计算 abs 的行为 - vimacs - 05-25-2020

此外还有一个问题,如果对 int 类型使用 std::abs 会怎样?水木上有网友写了个程序,自己定义了一个 int abs(int) 覆盖了标准库的 abs,结果使用 std::abs(int) 的时候,也用了程序里自定义的 abs.
原因也可以在 C++ 的头文件 bits/std_abs.h 里面找到,里面除了重载了 abs 函数之外,还有 using ::abs. 也就是说,用 std::abs(int) 和用 abs(int) 是一样的。
至于有没有链接时符号重复定义出错的问题,可以看 libc.a. 在 libc.a 里面,abs 被定义在 abs.o 里面,而且这个目标文件里面只有 abs 一个函数,所以只要程序里面定义了 abs,链接的时候就不可能链接到 abs.o. 如果使用动态链接,就更不用担心这个问题,因为链接器不会让最终的可执行文件在动态库里面找在目标文件中就存在的符号。