11

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

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

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

另外,“char”和“int”之间的实际重要区别是什么?如果我们可以使用“int”类型而不是“char”类型,为什么我们决定在某些时候使用一种而不是另一种呢?是为了节省“内存”(我使用引号,因为我实际上并不知道“内存”是如何工作的)。

最后,这 256 个 char 类型的可用值究竟是如何获得的?我读了一些关于模 2^n 的东西,其中 n = 8,但为什么它会起作用(与二进制有关?)。“模2 ^ n”的模部分是什么意思(如果它与模算术有任何关系,我看不到关系......)?

4

10 回答 10

11

好问题。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 结束,以此类推。

于 2010-01-09T17:34:08.513 回答
5

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

int在机器级别,和之间的区别char只是编程语言为其分配的内存的大小或字节数。在 C、IIRC 中,achar是一个字节,而 anint是 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”只有在我们简单地手动为变量赋值时才可以使用,例如 0char c = 'a',当我们明确知道我们将只有 256 个可能的 ASCII人物?

是的,尤其是在这种情况下,您正在分配一个字符文字。

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

最重要的是,您可以根据是否要将变量视为数字或字母来选择intchar在语​​言级别(要切换,您需要转换为其他类型)。如果你想要一个占用更少空间的整数值,你可以使用 a short int(我相信它是 2 个字节),或者如果你真的关心内存使用,你可以使用 a char,尽管大多数情况下这不是必需的。

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

于 2010-01-09T17:31:49.360 回答
4

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

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

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

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

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

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

于 2010-01-09T17:40:55.433 回答
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 从不同的角度看一个位模式。

于 2010-01-09T18:07:02.383 回答
2

天,

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

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

高温高压

于 2010-01-09T17:28:19.493 回答
1

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

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

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

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

如果您直接赋值,则 C 标准保证表达式 like'a'将适合 a char。请注意,在 C 中,'a'是 type int,而不是 char,但可以这样做char c = 'a',因为'a'它能够适合char类型。

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

请注意,有时很难弄清楚数据的类型,即使是“专业人士”也可能会犯错误。 gzip例如,格式将未压缩数据的大小存储在文件的最后 4 个字节中。对于大小 > 4GB 的大型文件,这会中断,这在当今相当普遍。

你应该小心你的术语。在 C 中,achar c = 'a'分配一个与 对应的整数值'a'c但它不必是 ASCII。这取决于您碰巧使用的任何编码。

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

最后,您可以使用char数组从 C 中的文件中读取数据,即使您最终可能会遇到EOF,因为 C 提供了其他检测方法,EOF而无需在变量中显式读取它,但是稍后您将了解它阅读有关数组和指针的信息(看看fgets()你是否对一个例子感到好奇)。

于 2010-01-09T17:38:13.263 回答
0

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

于 2010-01-09T17:37:27.810 回答
0

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 位。如果您真的对该主题感兴趣,您还应该查找内存对齐。您还可以通过使用“&”运算符获取变量的地址,然后查看该地址,在调试器中查看确切的布局。在查看内存中的值时,英特尔平台可能会让您有些困惑,因此请同时查找小端/大端。我确信堆栈溢出也有一些很好的总结。

于 2010-01-09T17:40:11.207 回答
0

一种语言所需的所有字符都由 ASCII 和扩展 ASCII 表示。所以除了扩展 ASCII 之外没有字符。

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

于 2014-01-14T19:55:14.923 回答