繁体   English   中英

memcpy和memset与分配和初始化的历史记录

[英]history of memcpy and memset vs assignment and initialization

所以,我的理解是以下代码:

somestruct_t a = {0};
somestruct_t b;
b = a;

在可能的情况下,总是更可取的:

somestruct_t a;
somestruct_t b;
memset(&a, 0, sizeof(a));
memcpy(&b, &a, sizeof(a));

顶级构造几乎总是可能的...这引出了我的问题:由于顶级代码都表现良好,对我来说,对于学习该语言的人来说显然更直观,为什么memsetmemcpy模式如此惊人地流行于C甚至非OO C ++代码? 几十年来我所做的每一个项目都倾向于最底层的模式。

我假设有一些历史原因,例如非常老的编译器不支持它或某些,但我非常想知道具体原因。

我知道一般历史问题是偏离主题的,但这是一个非常具体的不良做法,我想更好地理解。

编辑我不是试图断言memcpy和memset一般都不好。 我在谈论一个非常具体的使用模式的分配或初始化单个结构。

听起来你的经历与我的经历有很大不同,而且这里的其他一些评论员也是如此。

我不认识任何更喜欢的人

memcpy(&a, &b, sizeof(a));

过度

a = b;

在我的编程世界中(以及几乎任何我能想象到的世界),简单的赋值比memcpymemcpy memcpy用于移动任意数据块(类似于strcpy ,但是当它是任意字节而不是以空字符结尾的字符串时)。 很难想象为什么有人会主张使用memcpy而不是struct assignment。 当然,到处都有个别程序员遇到各种不良习惯,所以我想如果有人喜欢相反的话,我不会感到惊讶,但我不得不说,我一般不同意他们正在做的事情。

有人在评论中推测可能有一些历史先例在起作用,但至少对于memcpy -versus-assignment问题,我可以肯定地说,事实并非如此。

曾几何时,之前有C90 memcpy ,有BSD bcopy ,但在此之前有bcopy 有没有做一堆字节的有效副本,从A点到B点的标准功能。 但是有结构分配,几乎从一开始就在语言中。 结构赋值通常使用一个漂亮,紧凑,编译器生成的字节复制循环。 所以曾经有一段时间做这样的事情很时髦:

#define bcpy(a, b, n) (*(struct {char x[n];} *)a = *(struct {char x[n];} *)b)

我可能得到了错误的语法,但这会劫持编译器执行高效结构分配的能力,并重新利用它将n个字节从任意指针b复制到任意指针a ,即就像bcopymemcpy

换句话说,它不像memcpy首先出现,然后是结构赋值 - 实际上恰恰相反!

现在, memset与struct初始化是一个不同的故事。

将结构归零的大多数“干净”方法都是初始化,但当然,想要在某个时间点将某个结构设置为全零时并不常见。 拥有动态分配的结构并使用malloc / realloc而不是calloc也是不常见的。 所以在这些情况下, memset很有吸引力。 我认为现代C有结构常量,你可以随时使用,但我猜我不是唯一还没有学过它们的人,所以仍然倾向于使用memset

所以我不会考虑使用memset是糟糕的风格,而不是像memcpy是结构赋值的糟糕风格。

虽然我已经看到并编写了类似的代码

struct s zerostruct = { 0 };

然后是

a = zerostruct;

作为“更好的风格”的替代品

memset(&a, 0, sizeof(a));   

一句话:我不同意建议使用memcpy而不是结构分配,而且我批评任何喜欢它的人。 memset对于归零结构非常有用(并且不被推荐),因为替代方案并不那么引人注目。

只有一个用例在哪里

struct somestruct  foo = { 0 };

是不够的,而且

struct somestruct  foo;
memset(&foo, 0, sizeof foo);

需要改为使用:当结构中的填充可能很重要时。

你看,两者之间唯一的区别是后者保证将结构填充清除为零,而前者只能保证将结构成员清零。

人们可能关心填充的原因是基于向上/未来的兼容性。 如果在当前程序中保证填充为零,则库的未来版本可以“重用”新数据字段的填充,并且仍然可以使用较旧的二进制文件。

从C99开始,新的C库确实应该为这个目的保留一些成员。 这通常是您在许多库定义的结构中看到“保留”字段的原因,甚至在Linux内核用户空间界面中也是如此。 因此,填充问题实际上只与C99支持普及之前开发的结构有关; 换句话说,只在旧图书馆。

我知道的一个结构应该总是使用memset()清除,是struct sigaction ,在POSIX.1中定义。 在大多数POSIXy系统中,它是一个完全正常的结构(所以只清除结构成员的代码在这些系统上工作得非常好),但是由于不同时间的各种不同实现(特别是如何实现信号掩码) ,我相信仍然有C库的系统有一个结构版本,清除填充仍然很重要。

(这是因为sa_handlersa_sigaction成员通常在联合中,和/或因为sigset_t的定义可能已更改。)

在其他一些较旧的库中可能还有其他库,因此我建议在使用具有1999之前版本的库时使用memset()习惯用法,其示例代码也使用它。

嗯,结构赋值和“零初始化” 总是优于memset()memcpy() 对于大小或速度来说,编译器可以更好地优化memset/cpy() (具有这两个标准库函数的“特殊知识”)。 当然,会有点奇怪,但是,有可能。

此外,由于“零初始化”对堆分配的结构不起作用,因此总是使用memset()可以说一致性。

类似的情况适用于您可能希望复制一些相邻结构(数组的一部分)的情况 - 再次,如果您始终使用memcpy() ,则代码更加一致。

在一个历史性的说明中,我使用了工具链,其中本地结构初始化被破坏了。 在经历过这样的工具链的项目中,即使在这样的工具链被解雇之后,“始终使用memset() ”也将占上风。 问题是,即使您的工具链的memset()被破坏,您也可以创建自己的,但是您无法进行自己的本地struct初始化...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM