c语言中char有无符号,char代表有符号还是无符号?
提及了这个蛋疼的问题,一个被烤焦的问题:默认情况下,在Intel处理器,是有符号的;arm处理器上,却是无符号。
这里还是以arm架构为例,先来个没有任何争议的 n<127
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int main(void)
{
834c: e52db004 push {fp} ; (str fp, [sp, #-4]!)
8350: e28db000 add
fp, sp, #0 ; 0x0
8354: e24dd00c sub
sp, sp, #12 ; 0xc
char
n;
for
(n = 0; n < 127; n++)
8358: e3a03000 mov
r3, #0 ; 0x0
835c: e54b3005 strb r3, [fp,
#-5]
8360: ea000002 b 8370
8364: e55b3005 ldrb r3, [fp,
#-5]
8368: e2833001 add
r3, r3, #1 ; 0x1
836c: e54b3005 strb r3, [fp,
#-5]
8370: e55b3005 ldrb r3, [fp,
#-5]
8374: e353007e cmp
r3, #126 ;
0x7e//与126比较大小,若小于等于,以下就跳转,形成循环。
8378: 9afffff9 bls
8364
{
;
}
return
0;
837c: e3a03000 mov
r3, #0 ; 0x0
}
让我们越过这个signed or unsigned"是非界限",试个
n<129
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int main(void)
{
834c: e52db004 push {fp} ; (str fp, [sp, #-4]!)
8350: e28db000 add
fp, sp, #0 ; 0x0
8354: e24dd00c sub
sp, sp, #12 ; 0xc
char
n;
for
(n = 0; n < 129; n++)
8358: e3a03000 mov
r3, #0 ; 0x0
835c: e54b3005 strb r3, [fp,
#-5]
8360: ea000002 b 8370
8364: e55b3005 ldrb r3, [fp,
#-5]
8368: e2833001 add
r3, r3, #1 ; 0x1
836c: e54b3005 strb r3, [fp,
#-5]
8370: e55b3005 ldrb r3, [fp,
#-5]
8374: e3530080 cmp
r3, #128 ;
0x80
8378: 9afffff9 bls
8364
{
;
}
return
0;
837c: e3a03000 mov
r3, #0 ; 0x0
}
看来是一模一样。既然默认是unsigned char,也就无所谓与128的比较咯。
再来个 n <128 怎么样?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int main(void)
{
834c: e52db004 push {fp} ; (str fp, [sp, #-4]!)
8350: e28db000 add
fp, sp, #0 ; 0x0
8354: e24dd00c sub
sp, sp, #12 ; 0xc
char
n;
for
(n = 0; n < 128; n++)
8358: e3a03000 mov
r3, #0 ; 0x0
835c: e54b3005 strb r3, [fp,
#-5]
8360: ea000002 b 8370
8364: e55b3005 ldrb r3, [fp,
#-5]
8368: e2833001 add
r3, r3, #1 ; 0x1
836c: e54b3005 strb r3, [fp,
#-5]
8370: e55b3005 ldrb r3, [fp,
#-5]
8374: e1a03c03 lsl
r3, r3,
#24
//这里似乎出现了异常,先左移24位,以下再右移(thumb)24位
8378: e1a03c43 asr
r3, r3,
#24
//作用大家都懂得,但既然都unsigned,何必如此折腾?
837c: e3530000 cmp
r3, #0 ; 0x0
8380: aafffff7 bge
8364
{
;
}
return
0;
8384: e3a03000 mov
r3, #0 ; 0x0
}
最后,再看看有符号(signed char)的情况:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int main(void)
{
834c: e52db004 push {fp} ; (str fp, [sp, #-4]!)
8350: e28db000 add
fp, sp, #0 ; 0x0
8354: e24dd00c sub
sp, sp, #12 ; 0xc
signed
char n; //有符号
for
(n = 0; n < 110; n++)
8358: e3a03000 mov
r3, #0 ; 0x0
835c: e54b3005 strb r3, [fp,
#-5]
8360: ea000002 b 8370
8364: e55b3005 ldrb r3, [fp,
#-5]
8368: e2833001 add
r3, r3, #1 ; 0x1
836c: e54b3005 strb r3, [fp,
#-5]
8370: e15b30d5 ldrsb r3, [fp,
#-5] //有符号后确实不怎么一样:mem->reg带有了符号标记s
8374: e353006d cmp
r3, #109 ;
0x6d
8378: dafffff9 ble
8364
{
;
}
return
0;
837c: e3a03000 mov
r3, #0 ; 0x0
}
字符有没有符号,其实意义不是很大,本身计算机就把它当做个数字,所谓的printf("%c",ch)也只是将一个0~127的数字经过ascii表翻译成人类能懂的语言罢了。
不过,要记住几点:
通常都会认为char默认有符号,但arm下却是无符号,所以说,这个问题是和体系相关的东西。
对于处理器来说,也就是从汇编指令的角度去看,变量占几个字节,字符有没有符号,是有不同的指令,不同的方式去执行。处理器的心还是很细致di,呵呵。
不带signed或unsigned关键字的char型
无符号数? 有符号数? C标准规定为 Implementation Defined(由实作环境决定)。
arm-linux-gcc 规定 char 为 unsigned char 。
gcc规定为signed char
以前总以为 char 代表有符号,而 unsigned char 代表无符号。现在才知道如果换做 short 或 int
,这样理解都没问题,因为c标准明确定义了。但是,唯独 char 不能这样理解,因为c标准中对此是 Impementation
Defined,就是未明确定义,由具体的编译器明确定义。
为了代码移植,一定不要用 char !
我以前总以为 char 代表有符号,而 unsigned char 代表无符号。现在才知道如果换做 short 或 int ,
这样理解都没问题,因为c标准明确定义了。但是,唯独 char 不能这样理解,因为c标准中对此是 Impementation
Defined,就是未明确定义,由具体的编译器明确定义。
vc编译器、x86上的 gcc 都把 char 定义为 signed char;而 arm-linux-gcc 却把 char 定义为
unsigned char 。这样一来,在代码移植上就会出现问题。举个最简单的例子:
char a = 0xb6; if ( a == 0xb6) puts("hello world !");
在vc 或 x86的gcc 上,都是不会打印出 hello world! 的。用 arm-linux-gcc
编译,在arm板上,是可以打印出hello world !
的。是不是很神奇啊??? 我们再变化一下:
char a = 0xb6;
short b = 0xb600;
int c = 0xb6000000;
if ( a == 0xb6) puts("a");
if ( b == 0xb600) puts("b");
if ( c == 0xb6000000) puts("c");
在vc 或 x86的gcc 上,只会打印出 c 。用 arm-linux-gcc 编译,在arm板上,是可以打印出 a 和 c
。是不是发现了什么了呢?
首先,介绍 Integer Promotion 。通俗点说,c在处理整型(char short
int)时,都会自动提升为int(如果int范围不够,则提升成 unsigned
int)。比如 “a ==
0xb6”,首先0xb6会当一个int来处理,变为0x000000b6(关于常量,后面还会仔细说明)。a 会提升为int ,假如
char 被定义为有符合的,那么 a 为负数,因为最高位为1,所以 a会提升为 0xffffffb6。假如 char
被定义为无符号的,那么a会提升为 0x000000b6 。
即,在vc 或 x86的gcc 上,a == 0xb6 会变为 0xffffffb6 == 0x000000b6 ,而在
arm-linux-gcc 上,变为 0x000000b6 == 0x000000b6。
对于 short,因为c标准明确规定 不加关键字,就代表有符号数。所以,无论在什么编译器上 b == 0xb600 都会变成
0xffffb600 == 0x0000b600 。
对于 int,本身是int,也就不用 Integer Promotion
了,所以 c == 0xb60000 中
,c不做任何处理,直接从内存中读出来,即 0xb60000
== 0xb60000。
最后,简单说一下常量。我后面会转帖一部分资料,有兴趣可以看看,我现在只说用八进制(0开头)或十六进制(0x开头)表示的常量,他们都会当成无符号数处理!另外像
char a = 0xb6; 这句就有两个 Implementation
Defined,一个是char带不带符号,另外一个是,假如char为有符合, 0xb6 会当int 0x000000b6
处理,把这个int 变为 有符合的 char 有溢出,会有问题,0xb6本为正数,赋值到a中却变为负数,具体要怎么处理,c对此也是
Implementation Defined。
更多推荐

所有评论(0)