数据类型究竟是如何在计算机中表示的?

时间:2010-01-09 17:24:10

标签: c types kernighan-and-ritchie kr-c

我是一名读K& R的初级程序员,我觉得这本书假定了很多以前的知识。令我困惑的一个方面是内存中变量的实际表示,或者我应该说存在。数据类型到底为变量指定了什么?我不太确定如何说出这个问题......但我会问几个问题,也许有人可以为我提出一个连贯的答案。

当使用getchar()时,我被告知使用类型“int”比键入“char”更好,因为“int”可以容纳更多值而“char”只能容纳256个值。由于我们可能需要变量来保存EOF值,因此我们需要超过256或者EOF值将与256个字符中的一个重叠。在我看来,我认为这是一堆空洞的盒子。有人能给我一个更好的代表吗?这些“盒子”有索引号吗?当EOF与256个可用值中的值重叠时,我们可以预测它将与哪个值重叠吗?

另外,这是否意味着数据类型“char”只能在我们简单地手动为变量赋值时使用,例如char c ='a',当我们肯定知道我们只有256个可能的ASCII字符?

另外,“char”和“int”之间的实际重要区别是什么?如果我们可以使用“int”类型而不是“char”类型,为什么我们决定在某些时候使用一个而不是另一个?它是为了保存“记忆”(我使用引号,因为我实际上并不是“记忆”究竟如何工作)。

最后,char类型的256个可用值究竟是如何获得的?我读了一些关于modulo 2 ^ n的内容,其中n = 8,但为什么这样做(与二进制有关?)。什么是“modulo 2 ^ n”的模数部分(如果它与模运算有任何相关性,我看不到关系......)?

10 个答案:

答案 0 :(得分:10)

很棒的问题。 K& R是在对计算机知之甚少的日子里写回来的,因此程序员对硬件了解得更多。每个程序员应该熟悉这些东西,但(可以理解)很多初级程序员都不熟悉。

在卡内基梅隆大学,他们开发了一整套课程来填补这方面的知识空白,我是其中的一员。我推荐该课程的教科书:“计算机系统:程序员的视角”http://amzn.com/013034074X/

你的问题的答案比这里真正涵盖的要长,但我会给你一些简短的指示,供你自己研究。

基本上,计算机存储所有信息 - 无论是在内存(RAM)还是在磁盘上 - 二进制,基数为2的数字系统(与十进制相对,基数为10)。一位二进制数字称为位。计算机倾向于使用称为字节的8位块内存。

C中的char是一个字节。 int通常是四个字节(尽管在不同的机器上它可以是不同的)。因此char只能容纳256个可能的值,即2 ^ 8。 int可以容纳2 ^ 32个不同的值。

更多信息,请务必阅读本书,或阅读一些维基百科页面:

祝你好运!

更新,提供有关模块化算术的信息:

首先,阅读模块化算术:http://en.wikipedia.org/wiki/Modular_arithmetic

基本上,在二进制补码系统中,n位数实际上代表一个模数为2 ^ n的等价类。

如果这似乎使它变得更复杂而不是更少,那么关键的事情就是:

  • 无符号n位数保持0到2 ^ n-1的值。值“环绕”,例如,当你添加两个数字并得到2 ^ n时,你真的得到零。 (这称为“溢出”。)
  • 带符号的n位数保持从-2 ^(n-1)到2 ^(n-1)-1的值。数字仍然环绕,但最高的数字包围到最负数,并从那里开始向零计数。

因此,无符号字节(8位数)可以是0到255. 255 + 1包围到0. 255 + 2最终为1,依此类推。带符号的字节可以是-128到127. 127 + 1最终为-128。 (!)127 + 2最终为-127等。

答案 1 :(得分:5)

  

令我困惑的一个方面是   实际的表示,或者我应该说   存在,变量在记忆中。   数据类型究竟指定了什么   变量?

在机器级别,intchar之间的差异只是编程语言为其分配的内存的大小或字节数。在C,IIRC中,char是一个字节而int是4个字节。如果您要“查看”机器内部的这些内容,您会看到每个机器的位序列。能够将它们视为intchar取决于语言如何解释它们(这也是为什么它可以在两种类型之间来回转换的原因)。

  

使用getchar()时,我被告知   使用类型“int”比使用更好   键入“char”,因为“int”的事实   可以容纳更多的值而“char”可以   只保留256个值。

这是因为存在2 ^ 8或256位的8位组合(因为一个位可以具有两个可能的值),而存在2 ^ 32个32位的组合。 EOF常量(由C定义)是负值,不在0和255的范围内。如果您尝试将此负值分配给char(将其4个字节压缩为1),则高位将丢失,你将得到一个与EOF不同的有效char值。这就是为什么你需要将它存储到int中并在转换为char之前进行检查。

  

另外,这是否意味着数据   类型“char”只能在使用时使用   我们只是为a赋值   手动变量,例如0char c =   'a',当我们肯定知道我们的时候   将只有256个可能的ASCII   字符?

是的,特别是因为在这种情况下你要分配一个字符文字。

  

另外,实际重要的是什么   “char”和“int”之间的区别?   如果我们可以使用“int”类型而不是   “char”类型,我们为什么决定使用   在某些时候一个在另一个上面?

最重要的是,您可以在语言级别选择intchar,具体取决于您是要将变量视为数字还是字母(要切换,您需要转换为其他类型)。如果你想要一个占用更少空间的整数值,你可以使用short int(我认为是2个字节),或者如果你真的关心内存使用,你可以使用char,虽然大多数情况下这不是必要的。

编辑:这里是一个link,用于描述C中的不同数据类型以及可应用于它们的修饰符。请参阅末尾的表格了解尺寸和数值范围。

答案 2 :(得分:4)

基本上,系统内存是一个巨大的位系列,每个位都可以“开”或“关”。其余的是惯例和解释。

首先,没有办法直接访问各个位;相反,它们被分组为字节,通常以8个为一组(有一些奇特的系统不是这种情况,但你现在可以忽略它),每个字节都有一个内存地址。所以内存中的第一个字节有地址0,第二个字节有地址1,等等。

8位字节有2 ^ 8个可能的不同值,可以解释为0到255之间的数字(无符号字节),或者是-128到+127之间的数字(有符号字节),或者是ASCII字符。每个C标准的char类型的变量的大小为1个字节。

但是字节对于很多东西来说太小了,所以已经定义了更大的其他类型(即它们由多个字节组成),并且CPU通过特殊的硬件结构支持这些不同的类型。 int现在通常是4个字节(虽然C标准没有指定它,并且不同系统上的int可以更小或更大),因为4个字节是32位,直到最近才是主流CPU支持的“字大小“。

因此int类型的变量大4个字节。这意味着当它的存储器地址是例如1000,然后它实际上覆盖地址1000,1001,1002和1003处的字节。在C中,也可以同时处理这些单独的字节,这就是变量可以重叠的方式。

作为旁注,大多数系统要求较大的类型“字对齐”,即它们的地址必须是字大小的倍数,因为这使得硬件更容易。因此,不可能在地址999或地址17处开始使用int变量(但1000和16都可以)。

答案 3 :(得分:3)

我不会完全回答你的问题,但我想帮助你理解变量,因为当我开始自己编程时,我也有同样的理解它们的问题。

目前,不要打扰内存中变量的电子表示。将存储器视为1字节单元的连续块,每个单元存储一个位模式(由0和1组成)。

通过单独查看内存,您无法确定其中的位代表什么!它们只是0和1的任意序列。是你,谁指定,如何解释这些位模式!看一下这个例子:

int a, b, c;
...
c = a + b;

您也可以写下以下内容:

float a, b, c;
...
c = a + b;

在这两种情况下,变量a,b和c都存储在内存中(并且你无法告诉它们的类型)。现在,当编译器编译你的代码(即将你的程序翻译成机器指令)时,它确保在第一种情况下将“+”转换为integer_add而在第二种情况下转换为float_add,因此CPU将解释位模式正确地执行,你想要的。

变量类型就像眼镜,让CPU从不同的角度看待一些模式。

答案 4 :(得分:2)

天儿真好,

为了更深入,我强烈推荐Charles Petzold出色的书“Code

它涵盖的内容超出了您的要求,所有这些都有助于更好地了解实际发生的事情。

HTH

答案 5 :(得分:1)

实际上,数据类型是一种抽象,允许您的编程语言将某个地址处的几个字节视为某种数字类型。将数据类型视为镜头,可以将内存视为int或float。实际上,它只是计算机的一部分。

答案 6 :(得分:1)

  • 在C中,EOF是一个“小负数”。
  • 在C中,char类型可能是无符号的,这意味着它不能代表负值。
  • 对于无符号类型,当您尝试为它们分配负值时,它们将转换为无符号值。如果MAX是无符号类型可以容纳的最大值,则将-n分配给此类型相当于为其分配MAX - (n % MAX) + 1。所以,要回答关于预测的具体问题,“是的,你可以”。例如,假设char是无符号的,并且可以将值0保持为255。然后将-1分配给char就等同于为其分配255 - 1 + 1 = 255

鉴于上述情况,为了能够在EOF中存储cc不能为char类型。因此,我们使用int,因为它可以存储“小负值”。特别是,在C中,int保证存储-32767+32767范围内的值。这就是getchar()返回int

的原因
  

另外,这是否意味着数据类型“char”只能在我们简单地手动为变量赋值时使用,例如char c ='a',当我们肯定知道我们只有256个可能的ASCII字符?

如果直接分配值,则C标准保证'a'之类的表达式适合char。请注意,在C中,'a'的类型为int,而不是char,但可以char c = 'a',因为'a'能够适合char类型。

关于变量应该包含什么类型的问题,答案是:使用任何有意义的类型。例如,如果您正在计算或查看字符串长度,则数字只能大于或等于零。在这种情况下,您应该使用无符号类型。 size_t就是这种类型。

请注意,有时很难弄清楚数据的类型,甚至“专业人士”也可能会犯错误。例如,gzip格式,将未压缩数据的大小存储在文件的最后4个字节中。这打破了巨大的文件>尺寸为4GB,这些日子相当普遍。

你应该小心你的术语。在C中,char c = 'a''a'对应的整数值分配给c,但不一定是ASCII。这取决于你碰巧使用的编码。

关于“modulo”部分,以及类型char的256个值:如果数据类型中有n个二进制位,则每个位可以编码2个值:0和1.所以,你有2*2*2...*2n次)可用值,或2 n 。对于 unsigned 类型,任何溢出都是明确定义的,就好像你将数字除以(最大可能值+ 1),然后取余数。例如,假设unsigned char可以存储值0..255(总共256个值)。然后,将257分配给unsigned char基本上将其除以256,取余数(1),并将该值赋给变量。这种关系仅适用于无符号类型。有关详情,请参阅my answer to another question

最后,您可以使用char数组从C中的文件中读取数据,即使您最终可能会遇到EOF,因为C提供了其他方法来检测EOF而无需明确地在变量中读取它,但是稍后当你阅读有关数组和指针的内容时,你将会了解它(如果你对一个例子感到好奇,请参阅fgets()。)

答案 7 :(得分:0)

根据“stdio.h”,getchars()返回值为int,EOF定义为-1。 根据实际编码,可能会出现0..255之间的所有值,因为unsigned char不足以表示-1并且使用int。 这是一张包含详细信息http://en.wikipedia.org/wiki/ISO/IEC_8859

的漂亮表格

答案 8 :(得分:0)

K& R的美妙之处在于它的简洁性和可读性,作家总是必须为他们的目标做出让步;它不是一本2000页的参考手册,而是作为基本参考资料和一般学习语言的绝佳方式。我推荐Harbinson和Steele“C:A参考手册”,以获得详细的C参考书,当然还有C标准。

你需要愿意谷歌这个东西。变量在特定位置的存储器中表示,并且对于它们是给定范围内的一部分的程序是已知的。 char通常存储在8位内存中(在一些罕见的平台上,这不一定是真的)。 2 ^ 8表示变量的256个不同的可能性。不同的CPU /编译器/ etc表示基本类型int,长度各不相同。我认为C标准可能会指定这些的最小尺寸,但不是最大尺寸。我认为对于double,它指定至少64位,但这并不妨碍英特尔在浮点单元中使用80位。无论如何,32位英特尔平台上的内存中的典型大小对于无符号/有符号int和浮点数为32位(4字节),对于双重为64位(8字节),对于char(有符号/无符号)为8位。如果您对该主题非常感兴趣,还应该查找内存对齐。您还可以使用“&”获取变量的地址,从而在调试器的确切布局中使用操作员然后偷看该地址。在查看内存中的值时,英特尔平台可能会让您感到困惑,所以请查看小端/大端。我确信堆栈溢出也有一些很好的总结。

答案 9 :(得分:0)

语言中所需的所有字符均由ASCII和扩展ASCII表示。因此,扩展ASCII之外没有任何字符。

使用char时,有可能获得垃圾值,因为它直接存储字符但使用int,因为它存储字符的ASCII值的可能性较小。

相关问题