使用gcc时,是什么原因导致char被签名或取消签名?

And*_*ndy 49 c gcc

如果char在C(使用gcc)中签名或未签名,会导致什么?我知道,标准并没有规定一个比其他,我也可以检查CHAR_MIN,并CHAR_MAX从limits.h中,但我想知道是什么原因引发了另一种使用GCC时

如果我从libgcc-6读取limits.h,我看到有一个宏__CHAR_UNSIGNED__定义了一个"默认"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
Run Code Online (Sandbox Code Playgroud)

但是找不到 __CHAR_UNSIGNED__

背景:我有一些代码可以在两台不同的机器上编译:

桌上型电脑:

  • Debian GNU/Linux 9.1(拉伸)
  • gcc版本6.3.0 20170516(Debian 6.3.0-18)
  • 英特尔(R)Core(TM)i3-4150
  • libgcc-6-dev:6.3.0-18
  • char 签了

覆盆子Pi3:

  • Raspbian GNU/Linux 9.1(拉伸)
  • gcc版本6.3.0 20170516(Raspbian 6.3.0-18 + rpi1)
  • ARMv7处理器rev 4(v7l)
  • libgcc-6-dev:6.3.0-18 + rpi
  • char 没有签名

所以唯一明显的区别是CPU架构......

Bas*_*tch 52

根据C11标准(读n1570),char可以是signedunsigned(所以你实际上有两种口味的C).究竟是什么具体的实施.

一些处理器指令集体系结构应用程序二进制接口支持signed字符(字节)类型(例如,因为它很好地映射到某些机器代码指令),而另一些支持unsigned一个.

gcc除非你重新编译所有东西,包括你的C标准库,否则你甚至应该使用一些-fsigned-char或几乎不会使用的-funsigned-char 选项(因为更改它会破坏调用约定和ABI中的一些极端情况).

您可以在Linux上使用feature_test_macros(7)<endian.h>(请参阅endian(3))或autoconf来检测系统的内容.

在大多数情况下,您应该编写可移植的 C代码,它不依赖于这些东西.您可以找到跨平台库(例如glib)来帮助您.

BTW gcc -dM -E -x c /dev/null也给出了__BYTE_ORDER__等等,如果你想要一个无符号的8位字节,你应该使用<stdint.h>uint8_t(更便携,更可读).和标准limits.h中定义CHAR_MINSCHAR_MINCHAR_MAXSCHAR_MAX(你可以比较它们是否相等检测signed char小号实现),等等.

顺便说一下,你应该关心字符编码,但是现在大多数系统都使用UTF-8.像libunistring这样的库很有帮助.另请参阅此内容并记住,实际上,以UTF-8编码的Unicode字符可以跨越几个字节(即-s).char

  • 当然,解决这个问题的最简单和最便携的方法是只写你的意思:“signed char”或“unsigned”,视情况而定。 (2认同)

Jon*_*ler 42

默认值取决于平台和本机代码集.例如,使用EBCDIC(通常是大型机)的机器必须使用unsigned char(或拥有CHAR_BIT > 8),因为C标准要求基本代码集中的字符为正,而EBCDIC使用240代码来代表数字0.(C11标准,§6.2.5 类型 2说:声明为类型的对象char足以存储基本执行字符集的任何成员.如果基本执行字符集的成员存储在char对象中,则其值保证为非负.)

您可以控制GCC使用的标志-fsigned-char-funsigned-char选项.这是一个好主意是一个单独的讨论.

  • @luator一个好主意是编写代码,这样无论`char`是否签名都无关紧要,当需要有符号或无符号8位值时,请使用`int8_t`和`uint8_t`. (15认同)

msc*_*msc 12

字符类型charsigned或者unsigned,取决于平台和编译器.

根据这个参考链接:

C和C++标准允许字符类型char有符号无符号,具体取决于平台和编译器.

大多数系统,包括x86 GNU/Linux和Microsoft Windows,都使用signed char,

但基于PowerPC和ARM处理器的那些通常使用unsigned char.(29)

当在具有不同类型的char的默认平台之间移植程序时,这可能导致意外的结果.

GCC提供选项-fsigned-char-funsigned-char设置默认类型char.


Pet*_*des 7

至少在x86-64 Linux上,它由x86-64 System V psABI定义

其他平台将具有类似的ABI标准文档,这些文档指定了允许不同C编译器在调用约定,结构布局和类似内容上彼此一致的规则.(有关其他x86 ABI文档的链接或其他体系结构的其他位置,请参阅标记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
--------------------------------------------------------------
...
Run Code Online (Sandbox Code Playgroud)

*此类型bool在C++中调用.

***C++和C的一些实现允许枚举大于int.底层类型按顺序碰撞到unsigned int,long int或unsigned long int.


char在这种情况下,是否签名实际上直接影响调用约定,因为clang依赖于当前未记录的需求: 根据被调用者原型,当作为函数args传递时,narrow类型符号或零扩展为32位.

因此 int foo(char c) { return c; },clang将依赖调用者对arg进行符号扩展.(代码+ asm为此和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
Run Code Online (Sandbox Code Playgroud)

即使除了调用约定之外,C编译器也必须同意,因此它们.h以相同的方式编译内联函数.

如果(int)(char)x在同一平台的不同编译器中表现不同,它们就不会真正兼容.


n. *_* m. 6

gcc有两个编译时选项来控制以下行为char:

-funsigned-char
-fsigned-char
Run Code Online (Sandbox Code Playgroud)

除非您确切知道自己在做什么,否则不建议使用任何这些选项.

默认值取决于平台,并在构建gcc时固定.选择它是为了与该平台上存在的其他工具的最佳兼容性.

来源.