如果C中的char
(使用gcc)是有符号的或无符号的,是什么原因导致的?我知道标准并没有规定一个在另一个之上,我可以从limits. h中检查CHAR_MIN
和CHAR_MAX
,但我想知道在使用gcc时是什么触发了另一个
如果我从libgcc-6中读取limits.h,我看到有一个宏__CHAR_UNSIGNED__
,它定义了一个“默认”字符,有符号或无符号,但我不确定这是否是编译器在构建时设置的。
我试图列出GCC预定义的makros与
$ gcc -dM -E -x c /dev/null | grep -i CHAR
#define __UINT_LEAST8_TYPE__ unsigned char
#define __CHAR_BIT__ 8
#define __WCHAR_MAX__ 0x7fffffff
#define __GCC_ATOMIC_CHAR_LOCK_FREE 2
#define __GCC_ATOMIC_CHAR32_T_LOCK_FREE 2
#define __SCHAR_MAX__ 0x7f
#define __WCHAR_MIN__ (-__WCHAR_MAX__ - 1)
#define __UINT8_TYPE__ unsigned char
#define __INT8_TYPE__ signed char
#define __GCC_ATOMIC_WCHAR_T_LOCK_FREE 2
#define __CHAR16_TYPE__ short unsigned int
#define __INT_LEAST8_TYPE__ signed char
#define __WCHAR_TYPE__ int
#define __GCC_ATOMIC_CHAR16_T_LOCK_FREE 2
#define __SIZEOF_WCHAR_T__ 4
#define __INT_FAST8_TYPE__ signed char
#define __CHAR32_TYPE__ unsigned int
#define __UINT_FAST8_TYPE__ unsigned char
但无法找到__CHAR_UNSIGNED__
背景:我有一些代码,我在两台不同的机器上编译:
台式机:
- Debian GNU/Linux 9.1(stretch)
- gcc版本6.3.0 20170516(Debian 6.3.0-18)
- 英特尔(R)酷睿(TM)i3-4150
- libgcc-6-dev:6.3.0-18
char
已签名
[***] Raspberry Pi 3公寓:
- Raspbian GNU/Linux 9.1(stretch)
- gcc版本6.3.0 20170516(Raspbian 6.3.0-18+ rpi 1)
- ARMv7处理器版本4(v7 l)
- libgcc-6-dev:6.3.0-18+rpi
char
无符号
所以唯一明显的区别是CPU架构…
7条答案
按热度按时间qnakjoqk1#
根据C11标准(读作n1570),
char
可以是signed
或unsigned
(所以实际上有两种C语言)。一些processors和instruction set architectures或application binary interfaces支持
signed
字符(字节)类型(例如,因为它很好地Map到一些machine code指令),其他则支持unsigned
类型。gcc
甚至有一些-fsigned-char
或-funsigned-char
选项,你几乎不应该使用(因为改变它会破坏calling conventions和ABI中的一些极端情况),除非你重新编译所有内容,包括你的C standard library。您可以在Linux上使用feature_test_macros(7)和
<endian.h>
(请参阅endian(3))或autoconf来检测系统中有什么。在大多数情况下,你应该写portable C代码,这不依赖于这些东西。你可以找到跨平台库(例如glib)来帮助你。
顺便说一句,
gcc -dM -E -x c /dev/null
也给出了__BYTE_ORDER__
等,如果你想要一个无符号的8位字节,你应该使用<stdint.h>
和它的uint8_t
标准limits.h定义了CHAR_MIN
和SCHAR_MIN
以及CHAR_MAX
和SCHAR_MAX
(您可以比较它们是否相等,以检测signed char
的实现)等等。顺便说一句,你应该关心character encoding,但今天大多数系统都使用UTF-8 everywhere。像libunistring这样的库很有帮助。另请参阅this并记住,实际上,用UTF-8编码的Unicode字符可以跨越几个字节(即
char
-s)。e5nqia272#
默认值取决于平台和本机代码集。例如,使用EBCDIC的机器(通常是大型机)必须使用
unsigned char
(或具有CHAR_BIT > 8
),因为C标准要求基本代码集中的字符为正数,而EBCDIC使用240这样的代码来表示数字0。(C11标准,§6.2.5Types¶2说:* 声明为类型char
的对象足够大,可以存储基本执行字符集的任何成员。如果基本执行字符集的成员存储在char
对象中,则其值保证为非负。*)您可以通过
-fsigned-char
或-funsigned-char
选项控制GCC使用哪个符号。这是否是一个好主意是一个单独的讨论。fbcarpbf3#
字符类型
char
为signed
或unsigned
,具体取决于平台和编译器。根据this参考链接:
C和C++标准允许字符类型char为有符号或无符号,取决于平台和编译器。
大多数系统,包括x86 GNU/Linux和Microsoft Windows,都使用带符号的char,
但是那些基于PowerPC和ARM处理器的通常使用无符号字符。
当在具有不同默认char类型的平台之间移植程序时,这可能会导致意外的结果。
GCC提供选项
-fsigned-char
和-funsigned-char
来设置默认类型char
。dba5bblo4#
至少在x86-64 Linux上,它由the x86-64 System V psABI定义
其他平台也会有类似的ABI标准文档,这些文档指定了一些规则,让不同的C编译器在调用约定、结构布局等方面达成一致。(请参阅x86标签wiki,以获得其他x86 ABI文档的链接,或其他架构的其他地方。大多数非x86架构只有一个或两个标准ABI。)
从x86-64 SysV ABI:图3.1:标量类型
bool
。***C和C的一些实现允许枚举大于int。底层类型按顺序碰撞为无符号int、long int或无符号long int。
在这种情况下,
char
是否签名实际上直接影响调用约定,因为clang依赖于一个当前未记录的要求:根据被调用者原型,当作为函数参数传递时,窄类型是符号或零扩展到32位的。因此对于
int foo(char c) { return c; }
,clang将依赖于 caller 来对参数进行符号扩展(code + asm用于此,并在Godbolt上调用)。即使不考虑调用约定,**C编译器也必须达成一致,以便以相同的方式编译
.h
中的内联函数。如果
(int)(char)x
在同一平台的不同编译器中表现不同,那么它们就不会真正兼容。368yc8dk5#
gcc有两个编译时选项来控制
char
的行为:除非您确切地知道自己在做什么,否则不建议使用任何这些选项。
默认值是依赖于平台的,并且在构建gcc本身时是固定的。选择它是为了与该平台上存在的其他工具具有最佳兼容性。
Source。
pvcm50d16#
一个重要的实际注意事项是,UTF-8字符串文字的类型,如
u8"..."
,是char
的数组,并且必须以UTF-8格式存储。基本集中的字符保证等效于正整数。然而,如果任何其他字符存储在char对象中,则结果值是实现定义的,但应在该类型中可以表示的值范围内。
(In C++中,UTF-8字符串常量的类型是
const char []
,并且根本没有指定基本集之外的字符是否具有数值表示。因此,如果你的程序需要旋转UTF-8字符串的位,你需要使用
unsigned char
。否则,任何检查UTF-8字符串的字节是否在某个范围内的代码都是不可移植的。最好显式强制转换为
unsigned char*
,而不是编写char
,并期望程序员使用正确的设置编译以将其配置为unsigned char
。但是,您可以使用static_assert()
来测试char
的范围是否包括从0到255的所有数字。qgelzfjb7#
https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html说
CHAR_未签名
GCC定义此宏当且仅当数据类型char在〉目标机器上是无符号的。它的存在是为了使标准头文件limits.h正确工作〉。您不应该自己使用此宏;请参考limits. h中定义的标准宏。
所以看起来你没有在列表中看到它的原因是你在一个系统上测试,char是有符号的,而宏在这样的系统上根本没有定义。我已经确认它确实出现在我的一个arm系统的
cc -dM -E -x c /dev/null | grep -i CHAR
的输出中。C标准把它留给了实现,当然这并没有说太多,因为“实现”把一堆东西混在一起,编译器,操作系统,CPU架构等。
在Linux上,它取决于CPU家族。对于某些架构,有或曾经有很好的理由。例如,早期的arm没有真实的支持有符号字节。对于其他人来说,它似乎更随意,可能是从运行在同一硬件上的其他操作系统复制的。
Afaict windows和mac OS在所有目标体系结构上(或者至少在当前支持的所有体系结构上)都使用签名字符。