用于读取类对象(这里“艺术”是对象的名称)。我必须遵循以下代码filin 是输入和输出流。
filin.write((char*)&arts,sizeof(arts));
filin.read((char*)&arts,sizeof(arts));
你能解释一下这些。
为什么我们通过添加 (char *) 来键入 caste 以及为什么要添加到字符指针。
为什么在艺术之前添加 & 符号?
为什么要在最后添加 sizeof(arts) ?
用于读取类对象(这里“艺术”是对象的名称)。我必须遵循以下代码filin 是输入和输出流。
filin.write((char*)&arts,sizeof(arts));
filin.read((char*)&arts,sizeof(arts));
你能解释一下这些。
为什么我们通过添加 (char *) 来键入 caste 以及为什么要添加到字符指针。
为什么在艺术之前添加 & 符号?
为什么要在最后添加 sizeof(arts) ?
为什么我们通过添加 (char *) 来键入 caste 以及为什么要添加到字符指针。
char
是一个错误的名字。这是一个谎言——它应该被调用byte
,因为它就是:一个字节。您正在使用的read
andwrite
函数在字节缓冲区上工作——这是为了使它们成为通用的,即与任意类型一起工作(尽管这也是一个谎言)。
为什么在艺术之前添加 & 符号?
&
获取对象的内存地址。通过写入(char*) &obj
,我们有效地获取(地址)位于给定对象下的字节缓冲区——换句话说,以字节为单位的对象表示。
为什么要在最后添加 sizeof(arts) ?
sizeof(obj)
告诉我们obj
内存有多大——即它在字节缓冲区中占用了多少字节。给定起始地址(见前一点)和大小,我们可以完全描述对象在内存中的物理存在,我们可以使用它来将对象从主内存传输到文件中,反之亦然。
然而,正如我之前所说,这是一个谎言。C++ 实际上有许多类型的对象不支持这种复制它们的内存,因为这些对象实际上对它们所在的内存做出了假设。在最简单的情况下,它们包含一个指针成员,在复制字节表示之后对象,仍然指向旧位置,因此不再有用:
struct some_class {
some_class* self;
some_class() : self(this) { }
};
这个类有一个成员——<code>self——指向它自己。逐字节复制此类的实例将破坏此标识并使对象无用(复制后副本的self
成员仍将指向原始对象,而不是副本)。
这种逐字节复制也称为浅拷贝——与可以正确复制上述指针的深拷贝相反。
您显示的代码在 C++ 中通常不安全。事实上,它存在的问题比我迄今为止提到的还要多,而且在大多数情况下,对象的(反)序列化有更好的选择。
回答您的问题:
1 -write
并read
取一个char
指针 ( char *
)。这意味着它一个字节一个字节write
地read
工作,并且读取或写入与函数的第二个参数给出的字节数完全相同的字节数。
2-&
是address of
。这意味着您正在获取 的地址arts
,然后将地址转换为char
指针作为(char*)&arts
。
3 -sizeof()
返回其参数的长度(长度是指一个实例在内存中占用的大小)。在这种情况下,您会得到 和 的大小,arts
告诉write
和,或read
有多少位。write
read
首先,&
操作员获取您的艺术品的指针。假设您的对象arts
是类型Foo
:
Foo arts;
然后&arts
是类型Foo*
。
您代码中的方法write()
似乎处理字节数组(通常需要通过网络传输或写入文件),因此它使用指向char
(写为char*
)的指针,因为类型char
的大小为 1 字节,(嗯,在我知道的任何平台上),因此,如果您从 char 指针中添加或减去,您可以将指针前进或倒退该字节数(在 C++ 中,指针算术以对象字节大小的倍数进行) .
当您转换 fromFoo*
时,char*
您实际上是在告诉编译器将该指针视为 1 字节的倍数,而不是对象完整大小的倍数。这是处理字节数组的函数的习惯做法,就像你的write()
and read()
函数中的情况一样。
最后,在第一个参数中,您只传递了一个指针,一个内存地址。因此,要让函数write()
知道它必须从指针读取多少字节,您必须传入对象的长度(第二个参数)。该运算符sizeof(arts)
在编译时转换为对象的字节大小arts
(它也可以采用类型,因此如果是 的类型sizeof Foo
,则写入将是等效的)。Foo
arts
从第二个问题开始更容易。运算符 & 只是返回一个指向内存中对象的指针。然后我们将指向arts对象的指针转换为指向char的指针。Char 具有特定机器上最小可寻址内存单元的大小,它可以是 8 位(最常见),越来越少。我们也称它为“字节”。最后我们调用 sizeof 函数,它返回对象的大小,表示为存储该对象所需的最小内存单元(在我们的例子中是字符)的数量。在该函数接收指向内存中某个位置的指针以及要写入或读取的字节数之后。它需要知道的一切。
您发布的代码将对象存储在流中,然后从流中检索它。让我们把代码分成几块。
filin.write((char*)&arts,sizeof(arts));
首先,让我们检查一下 write 方法的签名。我猜,它看起来类似于以下内容:
stream::write(char * input, int size)
所以流对象需要一个指向内存中某个位置的指针,size
从那里获取字节并存储。
现在,让我们将您的命令与此签名进行比较。input
是 (char*)&arts。您有arts
某种类型的对象,并且想要存储它。input
必须是指向内存中存储对象的指针,因此您可以使用&
运算符检索它:&arts
。但是,该方法期望input
是 a char*
(意味着更多的是指向字节的指针,char 不仅在您想要处理字符时使用,而且在您想要处理字节的地方使用),但您的指针不是char *
. 这就是进行类型转换的原因:指向arts
类型的指针被转换为指向 a 的指针char *
。它的值保持不变,但现在编译器知道,你知道你在做什么。
接下来,有一个大小参数告诉流类消耗多少字节。由于您要存储艺术对象,sizeof(arts)
因此调用 a 来验证其大小。