免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
1234下一页
最近访问板块 发新帖
查看: 12214 | 回复: 37
打印 上一主题 下一主题

char 的符号 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-03-14 17:16 |只看该作者 |倒序浏览
我们 C 语言的课本是 [《C 语言程序设计基础》],点击书名可见介绍。

里头有不少错误的,实验书的错误更多。这个教材是教育科学“十五”国家规划课题的研究成果,代表了很高很高的水平。其中的错误想必都是非典且容易混淆的。我准备在这些错误中挑选一些,发在这里。一来可以确认一下是否真的错误,二来希望能对初学者有所帮助。

今天贡献第一篇:[Char 的符号]

[ 本帖最后由 retuor 于 2009-3-14 17:20 编辑 ]

评分

参与人数 1可用积分 +10 收起 理由
langue + 10 原创内容

查看全部评分

论坛徽章:
0
2 [报告]
发表于 2009-03-14 17:17 |只看该作者
书本 p24例题 2.10

一、问题提出:

整型变量 j 被赋值为 '\376',那么 j 的值是多少呢? 简化后的代码如下


  1. #include <stdio.h>

  2. int main()
  3. {
  4.         int j;

  5.         j='\376';
  6.         printf("%d\n", j);
  7. }
复制代码


书本说,输出的值应该为 -2,用 gcc 编译后运行,得到相同的结果。

二、ASCII 码表

j 变 -2,是因为编译系统把 '\376' 理解成负数了。我们知道,一个字符在 C 语言中被表示成其相应的整数编码。通常情况下,这个编码就是 ASCII 码。'\376' 对应的字符为........查一下 ASCII 码表就可以知道。

但如果你用的是课本后面的 ASCII 码表,你会发现,orz,此字符不在表中,越界了! 该表编码的最大值为8进制的 367。那这个字符到底是啥呢?是不是应该模个 2^8? 只要略加计算,就会发现 0367=247,才 247 啊。最大的编码应该是 10 进制的 255 ,再次 OTZ。显然书本后面的表有问题,耐心找一找,就会在表格中发现如下的索引序列 .......266, 267, 260, 261 ......


三、符号扩展

好了,不管它是什么字符了,反正它被编码成 0376,也就是 254。 它当然是个正数!但是为什么又变 -2 了呢? 我有一个猜想:在 16 位编译系统的时代,char 的大小与 short int 是一样,所以编译器的设计者偷了个懒,简单地把它们等同起来。现在虽然已经不是 16 位的时代了,但这个习惯被保留了下来。这就是传统的力量。

基于上述观点, 256 个字符被分成两部分,最高位是 1 的被视为负数。0376 写成二进制是 11111110,最高位是 1, 所以它是负的。它的相反数为 00000001 + 1 = 2,即它就是 -2.

下面我们看一下这个占有 8 位的‘小’ -2 是如何扩展成占有 32 位的 '大‘ -2 的。其实很简单,最高位若为 0,则添加 24 个 0;反之添加 24 个 1。

11111110  -> 111111111111111111111111 11111110

这个规则称为“符号括展”。虽然执行起来简单,但为什么它是合理的呢?不作理论推导,只举两个例子,就已经很清楚了。

如果扩展前的8位数是正的,比如 0123=83,写成 2 进制就是 01010011,前面添 0

01010011 ->000000000000000000000000 01010011

显然扩展后,还是原来的值。

如果原来的是负数,比如二进制  a=10010010,它的相反数是 01101101+1=01101110,即它的值为 -01101110.

a 括展后得到 111111111111111111111111 10010010 它的相反数为

000000000000000000000000 01101101+1=000000000000000000000000 01101110

所以括展之后的数为负的 000000000000000000000000 01101110 ,数值上与负 01101110 是相同的。

四、标准

书本上 -2 的输出是正确的,但它没有指出,这个输出其实是实现相关的。在 INTERNATIONAL STANDARD ©ISO/IEC ISO/IEC 9899:TC2 的 35 页有如下的文字:

The three types char, signed char, and unsigned char are collectively called
the character types. The implementation shall define char to have the same range,
representation, and behavior. as either signed char or unsigned char.


所以 char 有无符号,取决于编译器。

[ 本帖最后由 retuor 于 2009-3-14 17:23 编辑 ]

论坛徽章:
0
3 [报告]
发表于 2009-03-14 17:24 |只看该作者
不错,支持

论坛徽章:
0
4 [报告]
发表于 2009-03-14 18:30 |只看该作者
lz继续

论坛徽章:
95
程序设计版块每日发帖之星
日期:2015-09-05 06:20:00程序设计版块每日发帖之星
日期:2015-09-17 06:20:00程序设计版块每日发帖之星
日期:2015-09-18 06:20:002015亚冠之阿尔艾因
日期:2015-09-18 10:35:08月度论坛发贴之星
日期:2015-09-30 22:25:002015亚冠之阿尔沙巴布
日期:2015-10-03 08:57:39程序设计版块每日发帖之星
日期:2015-10-05 06:20:00每日论坛发贴之星
日期:2015-10-05 06:20:002015年亚冠纪念徽章
日期:2015-10-06 10:06:482015亚冠之塔什干棉农
日期:2015-10-19 19:43:35程序设计版块每日发帖之星
日期:2015-10-21 06:20:00每日论坛发贴之星
日期:2015-09-14 06:20:00
5 [报告]
发表于 2009-03-14 19:26 |只看该作者
楼主钻研的精神值得鼓励。

BTW,附个小例子,这样看的更清楚些
ACA80110:tmp lee$ cat t.c
#include <stdio.h>

#define CHAR '\376'

int main(void)
{
        int i, j, k;

        i = CHAR;
        j = (unsigned char) CHAR;
        k = (signed char) CHAR;
        printf("i = 0x%x, j = 0x%x, k = 0x%x\n", j, j, k);

        return 0;
}
ACA80110:tmp lee$ ./a.out
i = 0xfe, j = 0xfe, k = 0xfffffffe

[ 本帖最后由 MMMIX 于 2009-3-14 19:31 编辑 ]

论坛徽章:
0
6 [报告]
发表于 2009-03-14 20:39 |只看该作者
1)正宗的ASCII表,只定义了0-127。128-255是非标扩充。不过这个问题与ASCII表无关。
2)无前缀signed或unsigned的char,要看编译器,或默认是signed,或默认是unsigned。C标准好像没有规定。如果有一个C编译器,把char默认为unsigned,也并不违法。

论坛徽章:
0
7 [报告]
发表于 2009-03-14 21:19 |只看该作者
原帖由 MMMIX 于 2009-3-14 19:26 发表
楼主钻研的精神值得鼓励。

BTW,附个小例子,这样看的更清楚些


我正想来补充下面的代码呢,但被你抢先了。


  1. #include <stdio.h>

  2. int main()
  3. {
  4.         unsigned char c1='\376';
  5.         char c2='\376';
  6.         signed char c3='\376';

  7.         printf("%d\n", c1);
  8.         printf("%d\n", c2);
  9.         printf("%d\n", c3);
  10. }
复制代码


不得不承认,你的代码更漂亮,更正式,

谢谢。

论坛徽章:
0
8 [报告]
发表于 2009-03-14 21:30 |只看该作者
原帖由 beepbug 于 2009-3-14 20:39 发表
1)正宗的ASCII表,只定义了0-127。128-255是非标扩充。不过这个问题与ASCII表无关。
2)无前缀signed或unsigned的char,要看编译器,或默认是signed,或默认是unsigned。C标准好像没有规定。如果有一个C编译器 ...


标准 ASCII 的确只有 7 位,不过我们书上的 ASCII 码表是带扩充的。我在这里提到 ASCII 只是想顺带说一下书后的表格有错。

书中第 9 页还说:

字符型用 char 表示,一般用 8 位来存放一个字符,实际上存放的是该 ASCII 码值(即一个整数)。


这个说法有问题,如果改为一般存放的是 ASCII 码值还差不多。因为根据标准,字符的编码方式是实现定义的。

[ 本帖最后由 retuor 于 2009-3-14 21:32 编辑 ]

论坛徽章:
0
9 [报告]
发表于 2009-03-15 09:20 |只看该作者
原帖由 retuor 于 2009-3-14 17:17 发表
书本 p24例题 2.10

一、问题提出:

整型变量 j 被赋值为 '\376',那么 j 的值是多少呢? 简化后的代码如下


#include

int main()
{
        int j;

        j='\376';
        printf("%d\ ...

这里涉及三个环节。
1)char的符号默认。好像后来的C编译器大多将char默认为signed char。
2)在C里,其实是没有字符型的。char、short、int、long以及它们加上前缀后共十多种类型,C都当整数处理的。统称为integer型(整型)。在规范的C书里,integer型与int型是两个概念。
3)integer型数据向上做类型转换时,如果是unsigned的,符号位向左扩展。这也是C标准规定的。
因此,这个答案仅取决于对char的符号的默认。

论坛徽章:
0
10 [报告]
发表于 2009-03-15 11:17 |只看该作者
原帖由 beepbug 于 2009-3-15 09:20 发表

这里涉及三个环节。
1)char的符号默认。好像后来的C编译器大多将char默认为signed char。
2)在C里,其实是没有字符型的。char、short、int、long以及它们加上前缀后共十多种类型,C都当整数处理的。统称为integer型(整型)。在规范的C书里,integer型与int型是两个概念。
3)integer型数据向上做类型转换时,如果是unsigned的,符号位向左扩展。这也是C标准规定的。
因此,这个答案仅取决于对char的符号的默认。


1) 是的,这个就是标准里说的 “The implementation shall define char to......” 吧

2) 这个说法我倒不敢苟同。Integer 是对类型的分类,而 int, char 是具体的类型。正如你说说,都叫型( type),却是两个不同的概念。标准上还说:“Integer and floating types are collectively called arithmetic types” 呢。

3) 是的,所以这个贴的名字就叫 char 的符号。

[ 本帖最后由 retuor 于 2009-3-15 11:18 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP