【问题标题】:How exactly are data types represented in a computer?计算机中的数据类型究竟是如何表示的?
【发布时间】:2011-01-03 07:13:38
【问题描述】:

我是一名阅读 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 个可用值究竟是如何获得的?我读了一些关于模 2^n 的东西,其中 n = 8,但为什么它会起作用(与二进制有关?)。 “模2 ^ n”的模部分是什么意思(如果它与模运算有任何关系,我看不到关系......)?

【问题讨论】:

  • "当我们确定我们只有 256 个可能的 ASCII 字符时?" nit-pick:ASCII 中只有 128 个字符。
  • 不仅仅是“int”.. 还有 unsigned int (0-65535) 和 signed int (-32767 到 32767)... 在大多数实现中,普通字符在 unsigned 中是 0 到 255 .你也有短期和长期。 short 是 2 个字节,int 是 4 个字节,long 是 8 个字节。见这里:home.att.net/~jackklein/c/inttypes.html
  • 对不起 :s 那么我可以问为什么我们不能使用“char”类型的 256 个可用值,如果我们还使用函数 getchar() 并期望在某个时候出现 EOF?
  • 你的时间很有趣。我的意思是没有讽刺,只是鼓励。在这条路上,有很多非常有趣的东西摆在你面前。正如 Rob 在下面提到的,拿到 Petzold 的书并通读一遍。享受学习!
  • 下面的许多答案都假设数字的补码表示。 C 不做这样的保证。它可以在补码机器和符号幅度机器(或任何其他奇怪的编码!)上运行(并且确实运行)。

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


【解决方案1】:

好问题。 K&R 是在对计算机知之甚少的时代写成的,因此程序员对硬件了解得更多。每个程序员都应该熟悉这些东西,但(可以理解)许多初学者并不熟悉。

在卡内基梅隆大学,他们开发了一门完整的课程来填补这一知识空白,我曾担任该课程的助教。我推荐该课的教科书:《计算机系统:程序员的视角》http://amzn.com/013034074X/

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

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

C 中的一个字符是一个字节。一个 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 结尾,以此类推。

【讨论】:

  • 谢谢!你能解释一下 2^n 的“模”部分吗?
  • 我宁愿说“回到编程水平低得多,更接近硬件的时代,因此快速学习编程需要(并导致)对底层硬件有很好的基本了解” .
  • Software Monkey:说得好,我觉得比我写的更准确。
  • withchemicals:更新了答案,提供了一些关于模运算的信息;希望这会有所帮助。
  • 顺便说一句,在某些平台上,C 字符集中至少有一个字符的值超过了有符号字符的最大值。例如,在 EBCDIC 中,“0”是 0xF0。在这样的机器上,'char' 必须是无符号的。在其他一些平台(例如一些 DSP)上,sizeof(char)==sizeof(int) 和两者都能够保存值 -32767..32767(可能还有其他值)。在这样的机器上,'char' 必须被签名。进一步注意,在这样的机器上,-1 可能是一个有效的字符值,而 EOF 可能是 -1 以外的某个值。
【解决方案2】:

让我感到困惑的一个方面是 实际代表,或者我应该说 存在,内存中的变量。 数据类型具体说明了什么 变量?

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

当使用 getchar() 时,有人告诉我 最好使用类型“int”而不是 键入“char”,因为“int” 可以保存更多值,而“char”可以 只保存 256 个值。

这是因为 8 位有 2^8 或 256 种组合(因为一个位可以有两个可能的值),而 32 位有 2^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 中的不同数据类型以及可以应用于它们的修饰符。尺寸和数值范围见最后的表格。

【讨论】:

  • Nitpick:要处理字符,您必须远离 char 并使用 GLib 等库的更高级别抽象。
  • 当然,但我仍然认为了解较低级别的实际情况很重要。
  • 在 C 中,int 可以是 4 个字节,或更多或更少。 int 必须能够表示 -32767+32767 之间的值。
  • int 不需要 4 个字节。所有 C 说的是:short = 2 字节和 long >= 4 字节。请参阅“C 编程语言”,ANSI C 版本,K&R,第 36 页。
  • 此外,在 C 中,char 可能已签名,在这种情况下,它可以 存储 EOF,但当然 char 也可能未签名,并且这就是我们在这种情况下使用int 的原因。
【解决方案3】:

基本上,系统内存是一个巨大的位序列,每个位都可以是“开”或“关”。剩下的就是约定和解释。

首先,没有办法直接访问单个位;相反,它们被分组为字节,通常以 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 中,也可以同时寻址这些单独的字节,这就是变量可以重叠的方式。

作为旁注,大多数系统需要较大的类型来“字对齐”,即它们的地址必须是字长的倍数,因为这使硬件更容易。所以不可能有一个 int 变量从地址 999 或地址 17 开始(但 1000 和 16 都可以)。

【讨论】:

  • 同样,int 可能是 4 个字节或 2 个甚至 1 个字节,或任何值。它必须能够表示范围 +-32767。
  • 会不会是 2^7 而不是 2^8?
  • @Alok,是的,这就是我在上一段所说的。 @Roboto:不。 8 位表示 2^8 个不同的值。一个位有 2 个值 (2^1),每增加一个位就会加倍。
【解决方案4】:

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

目前,不要为内存中变量的电子表示而烦恼。将内存视为一个连续的 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 从不同的角度看一个位模式。

【讨论】:

    【解决方案5】:

    生日,

    为了更深入,我强烈推荐 Charles Petzold 的优秀书籍“Code

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

    HTH

    【讨论】:

      【解决方案6】:

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

      【讨论】:

      • OP 在硬件方面对其进行了构架,但我同意你的看法;对类型理论的简短介绍可以更好地回答各个问题。
      【解决方案7】:
      • 在 C 中,EOF 是一个“小负数”。
      • 在 C 中,char 类型可能是无符号的,这意味着它不能表示负值。
      • 对于无符号类型,当您尝试为其分配负值时,它们将转换为无符号值。如果MAX 是无符号类型可以容纳的最大值,那么将-n 分配给这种类型就相当于将MAX - (n % MAX) + 1 分配给它。因此,要回答您有关预测的具体问题,“是的,您可以”。例如,假设 char 是无符号的,并且可以保存值 0255 (含)。那么将-1 分配给一个char 就相当于将255 - 1 + 1 = 255 分配给它。

      鉴于上述情况,要能够将EOF 存储在c 中,c 不能是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。这取决于您碰巧使用的编码。

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

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

      【讨论】:

        【解决方案8】:

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

        【讨论】:

          【解决方案9】:

          K&R 的美妙之处在于它的简洁性和可读性,作家总是不得不为自己的目标做出让步;它不是一个 2000 页的参考手册,而是作为基本参考和学习语言的绝佳方式。我推荐 Harbinson 和 Steele "C: A Reference Manual" 作为一本优秀的 C 参考书,了解详细信息,当然还有 C 标准。

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

          【讨论】:

            【解决方案10】:

            一种语言所需的所有字符都由 ASCII 和扩展 ASCII 表示。所以没有超出扩展ASCII的字符。

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-01-20
              • 2015-02-20
              • 2021-07-03
              • 2021-07-05
              • 2023-03-27
              相关资源
              最近更新 更多