如果C中的char
(使用gcc)是签名还是未签名,会导致什么?我知道标准并没有规定另一个标准,我可以从limits.h检查CHAR_MIN
和CHAR_MAX
但是我想知道在使用gcc时触发一个是什么< / p>
如果我从libgcc-6读取limits.h,我发现有一个宏__CHAR_UNSIGNED__
定义了一个&#34;默认&#34; char签名或未签名但我不确定这是否由编译器在(他)的构建时间设置。
我尝试用
列出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__
背景:我在两台不同的机器上编译了一些代码:
台式电脑:
char
已签名Raspberry Pi3 :
char
未签名所以唯一明显的区别是CPU架构......
答案 0 :(得分:52)
根据C11标准(阅读n1570),char
可以是signed
或unsigned
(因此您实际上有两种C语言)。究竟是什么具体的实施。
某些processors和instruction set architectures或application binary interfaces支持signed
字符(字节)类型(例如,因为它很好地映射到某些machine code指令),其他赞成unsigned
一个。
gcc
甚至有一些-fsigned-char
或-funsigned-char
option你几乎不会使用它们(因为更改它会破坏calling conventions和ABI中的一些极端情况)除非您重新编译所有内容,包括C standard library。
您可以在Linux上使用feature_test_macros(7)和<endian.h>
(请参阅endian(3))或autoconf来检测您的系统。
在大多数情况下,您应该编写portable C代码,这不依赖于这些内容。您可以找到跨平台的库(例如glib)来帮助您。
BTW 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
的实现),等等...
char
- s)。
答案 1 :(得分:41)
默认值取决于平台和本机代码集。例如,使用EBCDIC(通常是大型机)的机器必须使用unsigned char
(或具有CHAR_BIT > 8
),因为C标准要求基本代码集中的字符为正,而EBCDIC使用240代码作为数字0 。(C11标准,§6.2.5类型¶2说:声明为类型char
的对象足够大,可以存储基本执行字符集的任何成员。基本执行字符集的成员存储在char
对象中,其值保证为非负值。)
您可以使用-fsigned-char
或-funsigned-char
选项控制GCC使用的标志。这是一个好主意是一个单独的讨论。
答案 2 :(得分:12)
字符类型char
为signed
或unsigned
,具体取决于平台和编译器。
根据this参考链接:
C和C ++标准允许字符类型char 签名或 未签名,,具体取决于平台和编译器。
大多数系统,包括 x86 GNU / Linux和Microsoft Windows,都使用签名字符,
但 基于 PowerPC和ARM处理器的那些通常使用无符号 char 。(29)
移植程序时可能会导致意外结果 在具有不同的char类型默认值的平台之间。
GCC提供选项-fsigned-char
和-funsigned-char
来设置默认类型char
。
答案 3 :(得分:7)
至少在x86-64 Linux上,它由the x86-64 System V psABI
定义其他平台将有类似的ABI标准文档,这些文档指定了允许不同C编译器在调用约定,结构布局和类似内容上彼此一致的规则。 (有关其他x86 ABI文档的链接或其他体系结构的其他位置,请参阅x86标记wiki。大多数非x86体系结构只有一个或两个标准ABI。)
来自x86-64 SysV ABI:图3.1:标量类型
C sizeof Alignment AMD64 (bytes) Architecture _Bool* 1 1 boolean ----------------------------------------------------------- char 1 1 signed byte signed char --------------------------------------------------------- unsigned char 1 1 unsigned byte ---------------------------------------------------------- ... ----------------------------------------------------------- int 4 4 signed fourbyte signed int enum*** ----------------------------------------------------------- unsigned int 4 4 unsigned fourbyte -------------------------------------------------------------- ...
*此类型在C ++中称为
bool
。*** C ++和C的一些实现允许枚举大于一个 INT。底层类型碰到unsigned int,long int或 unsigned long int,按顺序。
在这种情况下,char
是否已签名实际上直接影响调用约定,因为根据被调用者原型,clang依赖于当前未记录的需求:narrow types are sign or zero-extended to 32 bit when passed as function args。
因此对于int foo(char c) { return c; }
,clang将依赖调用者来对arg进行符号扩展。 (code + asm for this and a caller on Godbolt)。
gcc:
movsx eax, dil # sign-extend low byte of first arg reg into eax
ret
clang:
mov eax, edi # copy whole 32-bit reg
ret
即使除了调用约定之外, C编译器也必须同意,因此他们以.h
以相同的方式编译内联函数。
如果(int)(char)x
在同一平台的不同编译器中表现不同,则它们实际上并不兼容。
答案 4 :(得分:6)
gcc有两个编译时选项来控制char
的行为:
-funsigned-char
-fsigned-char
除非您确切知道自己在做什么,否则不建议使用任何这些选项。
默认值取决于平台,并在构建gcc时固定。选择它是为了与该平台上存在的其他工具的最佳兼容性。
答案 5 :(得分:1)
一个重要的实际注意事项是UTF-8字符串文字的类型(例如u8"..."
)是char
的数组,并且必须以UTF-8格式存储。基本集中的字符保证等于正整数。然而,
如果任何其他字符存储在char对象中,则结果值是实现定义的,但应在可以在该类型中表示的值范围内。
(在C ++中,UTF-8字符串常量的类型为const char []
,并且未指定基本集外的字符是否具有数字表示。)
因此,如果您的程序需要旋转UTF-8字符串的位,则需要使用unsigned char
。否则,任何检查UTF-8字符串字节是否在某个范围内的代码都不可移植。
最好显式转换为unsigned char*
而不是编写char
,并期望程序员使用正确的设置进行编译,以将其配置为unsigned char
。但是,您可以使用static_assert()
来测试char
的范围是否包含0到255之间的所有数字。