7

我曾经认为每个内存位置包含 8、16、32 或 64 位。所以 0101 将作为 00000101 存储在 8 位机器中(如果它是负数,则符号扩展)。这一切都很好而且很花哨,直到我出于好奇用 java 编写了一个程序来了解这个系统的更多内部工作原理。

有问题的方法如下所示:

public void printBinaryRep(File f){
        try{
            FileInputStream inputStream = new FileInputStream(f);
            int next = 0;
            byte b = 0;
            while((next = inputStream.read()) != -1){
                b = (byte)next;
                System.out.println((char)next + " : "+Integer.toBinaryString(next));
            }
            inputStream.close();
        }
        catch(Exception e){System.out.println(e);}
 }

我从一个说 Hello World 的文件中得到了这个输出

H : 1001000
e : 1100101
l : 1101100
l : 1101100
o : 1101111
  : 100000
W : 1010111
o : 1101111
r : 1110010
l : 1101100
d : 1100100

除了空间,一切看起来都很好。它有 6 位而不是 8 位。我现在想知道所有这些信息是如何存储在内存中的。如果所有这些都存储在 8 位块中,例如

您好:10010001100101110110011011001101111

然后您可以简单地查看每个 8 位块并找出它所代表的数字(以及它所指的 ASCII 代码)。当不同大小的字符(如 6 位空间和 4 位 /n )与它们一起存储时,它是如何工作的?那么在大的位空间中存储一个小数不会浪费很多位吗?

我想我的一些基本理解是错误的(或者程序可能在某个地方出错了......)。对不起,如果这个问题听起来很奇怪或太不必要的深入。我只是想知道。我已经做了一些谷歌搜索,但它没有提出任何相关的信息。如果你能让我知道我哪里出了问题或指出我正确的方向,我将不胜感激。谢谢!

4

8 回答 8

9

你最好在 C 和/或汇编中进行试验,而不是 Java。这些语言是较低级别的,直接公开地址空间。

我曾经认为每个内存位置包含 8、16、32 或 64 位。所以 0101 将作为 00000101 存储在 8 位机器中(如果它是负数,则符号扩展)。这一切都很好而且很花哨,直到我出于好奇用 java 编写了一个程序来了解这个系统的更多内部工作原理。

x86 系统中的所有内存位置都包含 8 位(1 个字节)。如果一个值包含的数据多于单个字节,则使用多个字节进行存储。例如,在 C 中,“浮点”类型使用 4 个字节(32 位)存储。

除了空间,一切看起来都很好。它有 6 位而不是 8 位。我现在想知道所有这些信息是如何存储在内存中的。如果所有这些都存储在 8 位块中,例如

该空间也存储在单个字节中。您的打印代码忘记填充 8 个空格。100000 == 00100000 == 0x20。

于 2009-10-09T22:38:53.617 回答
7

该空间也有 8 位。只是 Integer.toBinaryString 没有0按照您使用它的方式打印前导位。

使用所有前导0位,它实际上在内存中看起来像这样:

H : 01001000
e : 01100101
l : 01101100
l : 01101100
o : 01101111
  : 00100000
W : 01010111
o : 01101111
r : 01110010
l : 01101100
d : 01100100
于 2009-10-09T22:38:41.370 回答
4

您最初的直觉(大部分)是正确的:所有内存位置都由相同数量的位组成。在所有现代机器上,一个“字节”中有八位,其中一个字节是机器可以单独访问的最小内存块。

仔细查看您的输出。除了空格之外,所有数字都有七位数字。空格恰好以二进制表示中的两个零开头,而其他字母以一开头。

于 2009-10-09T22:41:04.717 回答
3

其实你的方法是错误的。编码在这里非常重要。

如果您使用 ASCII,那么您可以轻松地说每个字符都存储在一个字节(八位)中,但是当编码更改时,您不能这么说。

例如:UTF-8 对字符串中的每个字符使用一到三个字节(8 到 24 位)。这就是为什么您会看到一个重载,您可以在其中指定输入流对象的编码。

选择错误的输入流绝对会导致错误的字符串输出。因此,您必须知道文件的编码才能理解哪个位意味着什么。实际上 fileinputstream 会为您执行此操作。

如果您将数字存储为字符串,它将在硬盘驱动器中占用一个字符长度。就像另一个角色一样。

但是,如果您将 123456789 存储为使用 ASCII 编码的字符串,它将占用 9*8 位 = 72 位。

如果将其存储为整数,(注意整数的数据宽度在不同的环境中有所不同)它只需要 16 位。

你也不能确定

H : 01001000
e : 01100101
l : 01101100
l : 01101100
o : 01101111
  : 00100000
W : 01010111
o : 01101111
r : 01110010
l : 01101100
d : 01100100
\n: 00001010

存储在Hard Drive AS H:01001000 E:01100101 L:01101100 L:01101100 O:01101111:001101111:00100000 W:01010111 O:01101111 r:01110010 L:01101100 D:01101100 D:01101100 D:01100100 \ 001100100 \ \ n:001100100

你不能确定这一点。文件系统不是那么简单。也许 Hello 是连续的,但 World 字符串在驱动器的末尾。这就是为什么有碎片整理命令的原因。

但是,如果我们在定义字符串时谈论主存储器(RAM),我希望位是连续的。至少在 C 中是这样。你定义一个这样的字符串。

char[100] value; // c is a char array. (there is no string type in c)

这里 value[0] 是我们字符串的第一个字符。并且值仅地址到内存中的字符数组位置。

如果 value[0] 的地址是 10,那么 value[1] 的地址是 10+8 = 18。

于 2009-10-09T23:01:11.213 回答
3

计算机存储数字的方式可以比作汽车中的里程表。如果里程表有 4 位数字,它会将数字 33 存储为“0033”。

如果有人你的里程是多少,你不会说“零万零三十三”。默认情况下,Java 也没有。(虽然你可以告诉它。)

那么在大的位空间中存储一个小数不会浪费很多位吗?

嗯,不是真的。假设您在某处的内存中有 11000100 。计算机应该如何知道这是否意味着 11000100,或 11000 后接 100,或 1 后接 1000 后接 100,等等?

嗯,实际上计算机只是按照给定的程序运行(请记住,Java 程序部分是由您创建的,部分是由设计 Java 的人创建的)。如果你能创建一个可行的系统来节省比特,你就可以让计算机去做。

但是,请记住,在处理器使用和编程难度方面存在权衡。由于典型的计算机处理字节速度比处理 7 位或可变位数的速度要快得多,因此以字节为单位存储 ASCII 代码是存储文本的一种非常常见的选择。

但让我回到你的问题。

那么在大的位空间中存储一个小数不会浪费很多位吗?

从数学上讲,没有。一个称为信息论的数学分支告诉我们,绝对必要的位数取决于您想要编码的可能性以及每个可能性的可能性。

假设您只有一个四个字母的字母(A、B、C、D),并使用两位数字(分别为 00、01、10、11)来表示它。如果这些字母中的每一个都具有相同的可能性,则每个字母所需的最小位数(平均)为 2。换句话说,即使 A 为 00 而 B 为 01 ,也没有浪费的位。

另一方面,如果您使用 ASCII 并将 A、B、C、D 编码为以下 7 位数字:

A: 1000001
B: 1000010
C: 1000011
D: 1000100

那么您每个字母“浪费”了 5 位(即使您没有“在大位空间中存储小数字”)。

在设计压缩算法时,这些考虑因素很重要,而对于日常应用程序则不那么重要。如果您想学习 C,了解位和字节当然很重要。

于 2009-10-10T00:16:45.740 回答
2

根据Java 4 API

如果参数为负数,则无符号整数值是参数加上 232;否则它等于参数。此值被转换为二进制(以 2 为基数)的 ASCII 数字字符串,没有额外的前导 0。

实际上,数据存储实际上要复杂得多。为了提高处理效率,大多数数据类型都存储在字边界上,这意味着在 32 位机器上是 4 个字节,在 64 位机器上是 8 个字节。数组可能会更紧密地打包,因此char [4]最终可能会使用与char.

Java 是一个虚拟机,我不确定它使用什么内存架构(如果有的话)。

于 2009-10-09T22:38:06.440 回答
1

这清除了它。我的主要问题是一开始我忽略了零。当我阅读有关压缩算法(即 gzip)的更多信息时,我正在对此进行试验,我假设所有这些都是 ASCII。看到表示不是程序的目标,但是每个字的不同位数使我偏离了为我正在处理的文件类型实现基本的、基于索引的压缩的最初目标。一旦我在 Java 中获得了概念证明,我将尝试用 C 重写它。

谢谢!

于 2009-10-12T19:39:44.933 回答
0

http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Integer.html#toBinaryString%28int%29
Integer.ToBinarys 的规范如下:

“此值转换为二进制(以 2 为基数)的 ASCII 数字字符串,没有额外的前导 0”

你忽略了这个事实是导致你困惑的原因。

于 2012-08-03T04:16:31.557 回答