为什么使用 bzero 而不是 memset?

在我上学期上的系统编程课上,我们必须用 C 语言实现一个基本的客户机/服务器。当初始化结构时,比如 sock_addr_in,或者 char buffer (我们用来在客户机和服务器之间来回发送数据) ,教授指示我们只使用 bzero而不是 memset来初始化它们。他从未解释过原因,我很好奇这是否有合理的解释?

我在这里看到: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdownbzero是更有效的,因为事实上它永远只会是零内存,所以它不必做任何额外的检查,memset可能做。但是,这仍然不一定是绝对不使用 memset来归零内存的理由。

bzero被认为是不推荐的,而且是一个非标准的 C 函数。根据手册,memset是优先于 bzero的这个原因。那么为什么还要使用 bzero而不是 memset呢?只是为了提高效率,还是别的什么?同样,memset相对于 bzero的优点是什么,使它成为新程序事实上的首选?

107809 次浏览

我猜想你使用了(或者你的老师受到了) ABc2的理查德·史蒂文斯。他经常使用 bzero而不是 memset,即使是在最新的版本。这本书非常受欢迎,我认为它已经成为网络编程的一个习惯用法,这就是为什么你仍然看到它被使用。

我坚持使用 memset仅仅是因为 bzero已经过时并且降低了可移植性。我怀疑你会看到任何真正的收益,从使用其中之一,而不是其他。

我看不出有任何理由选择 bzero而不是 memset

memset是一个标准的 C 函数,而 bzero从来就不是一个 C 标准函数。其基本原理可能是因为您可以使用 memset函数实现完全相同的功能。

现在,关于效率,像 gcc这样的编译器使用 memset的内置实现,当检测到一个常量 0时,它会切换到一个特定的实现。禁用内建时 glibc也是如此。

我认为 bzero()在将内存设置为零方面比 memset()有一个优势,那就是犯错误的几率降低了。

我不止一次遇到过这样的错误:

memset(someobject, size_of_object, 0);    // clear object

编译器不会抱怨(尽管可能会在某些编译器上提高一些警告级别) ,结果是内存没有被清除。因为这不会破坏对象-它只是让它单独存在-有一个很好的机会,这个 bug 可能不会显示成任何明显的东西。

事实上,bzero()不是标准的是一个小刺激。(FWIW,如果我的程序中的大多数函数调用是非标准的,我不会感到惊讶; 事实上,编写这样的函数是我的工作)。

在对另一个答案的评论中,Aaron Newton 引用了来自 Unix 网络编程,第1卷,Stevens 等人的第3版,第1.2节(强调添加)的以下内容:

bzero不是 ANSI C 函数,它源于早期的伯克利 尽管如此,我们在整个文本中使用它,而不是 因为 bzero更容易记忆 (只有两个参数)大于 memset(有三个参数) 每个支持套接字 API 的供应商也提供 bzero,并且 如果没有,我们在 unp.h头中提供一个宏定义。

事实上,TCPv3[ TCP/IP Illustrated,第3卷-Stevens 1996]的作者犯了交换第二个的错误 和第三个参数到 memset在第一个中的10个出现中 打印 。 C 编译器无法捕获此错误,因为两个参数 (实际上,第二个参数是 int和 第三个参数是 size_t它通常是 unsigned int, 但是分别指定的0和16仍然可以接受 对 memset的调用仍然有效, 因为只有少数套接字函数实际上要求 最后8个字节的 Internet 套接字地址结构设置为0。 然而,这是一个错误,一个可以通过使用 因为将两个参数交换到 bzero将始终是 如果使用函数原型,则由 C 编译器捕获。

我还相信,对 memset()的绝大多数调用都是为了清零内存,那么为什么不使用针对该用例的 API 呢?

bzero()的一个可能的缺点是,编译器可能更有可能优化 memcpy(),因为它是标准的,所以它们可能被编写来识别它。但是,请记住,正确的代码仍然优于已优化的不正确代码。在大多数情况下,使用 bzero()不会对程序的性能造成明显的影响,而且 bzero()可以是扩展到 memcpy()的宏函数或内联函数。

你可能使用 bzero,它实际上不是标准的 C,它是一个 POSIX 的东西。

注意单词“ was”——它是 POSIX.1-2001中的 不赞成,而是 POSIX.1-2008中的 被移除了,这是对 memset 的尊重,所以你最好使用标准的 C 函数。

对于 memset 函数,第二个参数是 int,第三个参数是 size_t,

void *memset(void *s, int c, size_t n);

这通常是一个 unsigned int但是如果第二个和第三个参数的 0 and 16分别以错误的顺序输入为16和0那么, 这样一个调用 memset 仍然可以工作,但什么也做不了。因为要初始化的字节数被指定为 0

void bzero(void *s, size_t n)

使用 bzero 可以避免这种错误,因为如果使用函数原型,C 编译器总是会捕获将两个参数交换为 bzero 的过程。

简而言之: memsetbzero需要更多的装配操作。

这是来源: Http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown

随便你怎么做。 : -)

#ifndef bzero
#define bzero(d,n) memset((d),0,(n))
#endif

请注意:

  1. 原来的 bzero不返回任何值,memset返回 void 指针(d)。这个问题可以通过在定义中添加类型转换来解决。
  2. 即使原始函数存在,#ifndef bzero也不会阻止您隐藏它。它测试宏的存在性。这可能会造成很多混乱。
  3. 不可能为宏创建一个函数指针,当通过函数指针使用 bzero时,这是行不通的。

想要提到一些关于 bzero 与 memset 参数的内容。安装 ltrace,然后比较它在引擎盖下的功能。 在使用 libc6(2.19-0ubuntu6.6)的 Linux 上,调用完全相同(通过 ltrace ./test123) :

long m[] = {0}; // generates a call to memset(0x7fffefa28238, '\0', 8)
int* p;
bzero(&p, 4);   // generates a call to memset(0x7fffefa28230, '\0', 4)

有人告诉我,除非我使用 腹腔深处或任何数量的内核/系统调用接口,否则我不必担心它们。 我所需要担心的是调用是否满足对缓冲区进行归零的要求。其他人已经提到哪一个比另一个更好,所以我就到此为止了。

Memset 有3个参数 bzero 有2个 在内存受限的情况下,额外的参数将占用4个字节,并且大多数情况下它将用于将所有内容设置为0