175

在上学期我参加的系统编程课程中,我们必须在 C 中实现一个基本的客户端/服务器。在初始化结构时,比如sock_addr_in或 char 缓冲区(我们用来在客户端和服务器之间来回发送数据),教授指示我们只使用bzero而不是memset初始化它们。他从来没有解释过为什么,我很好奇这是否有正当理由?

我在这里看到:http: //fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown效率bzero更高,因为它只会将内存归零,所以它不会必须做任何memset可能做的额外检查。不过,这似乎仍然不一定是绝对不memset用于归零内存的理由。

bzero被认为已弃用,而且不是标准的 C 函数。根据手册,由于这个原因,memset是首选。bzero那么你为什么还要使用bzeroovermemset呢?只是为了提高效率,还是更多?同样,使其成为新程序事实上的首选选项memset的好处是什么?bzero

4

9 回答 9

165

我看不出有任何理由更bzero喜欢memset.

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

现在关于效率,编译器喜欢gcc使用内置实现,memset当检测到常量时切换到特定实现0。禁用glibc内置函数时也是如此。

于 2013-06-13T21:06:22.217 回答
73

我猜你用过(或者你的老师受过)W. Richard Stevens 的UNIX Network Programming。即使在最新版本中,他也bzero经常使用而不是。memset这本书非常受欢迎,我认为它已成为网络编程中的一个成语,这就是为什么你仍然看到它被使用的原因。

我会坚持下去,memset因为bzero它已被弃用并降低了可移植性。我怀疑你会从使用其中一个中看到任何真正的收益。

于 2013-06-13T21:05:12.583 回答
60

我认为将内存设置为零的一个优势bzero()memset()减少了出错的机会。

我不止一次遇到了一个看起来像这样的错误:

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

编译器不会抱怨(尽管在某些编译器上可能会提高一些警告级别)并且效果将是内存没有被清除。因为这不会破坏对象 - 它只是让它不理会 - 这个错误很可能不会表现出任何明显的东西。

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

在对此处另一个答案的评论中,Aaron Newton 引用了 Unix Network Programming, Volume 1, 3rd Edition by Stevens 等人的以下内容,第 1.2 节(强调添加):

bzero不是 ANSI C 函数。它源自早期的伯克利网络代码。尽管如此,我们在整个文本中都使用它,而不是 ANSI Cmemset函数,因为(只有两个参数)比(三个参数)bzero更容易记住。memset几乎每个支持套接字 API 的供应商也提供了bzero,如果没有,我们会在我们的unp.h标头中提供一个宏定义。

事实上,TCPv3 [TCP/IP Illustrated, Volume 3 - Stevens 1996] 的作者犯了一个错误,即memset在第一次印刷时将第二个和第三个参数交换为 10 次。AC 编译器无法捕获此错误,因为两个参数属于同一类型。(实际上,第二个参数是 anint而第三个参数是size_t,通常是 an unsigned int,但指定的值,分别为 0 和 16,对于其他类型的参数仍然是可接受的。)调用memset仍然有效,因为只有 a实际上,很少有套接字函数需要将 Internet 套接字地址结构的最后 8 个字节设置为 0。不过,这是一个错误,可以通过使用 bzerobzero,因为如果使用函数原型,C 编译器总是会捕捉到交换两个参数。

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

一个可能的缺点bzero()是编译器可能更有可能进行优化memcpy(),因为它是标准的,因此它们可能被编写为识别它。但是,请记住,正确的代码仍然比经过优化的错误代码更好。在大多数情况下, usingbzero()不会对程序的性能造成明显影响,这bzero()可以是扩展为memcpy().

于 2013-06-13T22:06:59.443 回答
4

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

并注意“was”这个词 - 它在 POSIX.1-2001 中已被弃用,并在 POSIX.1-2008 中删除,以尊重 memset,因此您最好使用标准 C 函数。

于 2013-08-20T09:28:30.220 回答
4

想提一些关于 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)

有人告诉我,除非我在libc 的深处或任何数量的内核/系统调用接口中工作,否则我不必担心它们。我应该担心的是调用满足将缓冲区归零的要求。其他人已经提到哪一个比另一个更可取,所以我会在这里停下来。

于 2015-04-22T14:20:11.530 回答
3

对于 memset 函数,第二个参数是 an 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。

于 2013-12-23T06:08:08.807 回答
3

随心所欲。:-)

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

注意:

  1. 原来bzero什么都不返回,memset返回 void 指针(d)。这可以通过在定义中将类型转换添加到 void 来解决。
  2. #ifndef bzero即使它存在,也不会阻止您隐藏原始功能。它测试宏的存在。这可能会引起很多混乱。
  3. 不可能创建指向宏的函数指针。通过函数指针使用时bzero,这将不起作用。
于 2014-03-20T20:15:09.943 回答
2

简而言之: memset需要更多的组装操作然后bzero

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

于 2014-01-16T08:45:59.713 回答
-1

memset 需要 3 个参数,bzero 在内存中需要 2 个,因为额外的参数会多占用 4 个字节,并且大多数情况下它将用于将所有内容设置为 0

于 2019-07-23T17:20:07.233 回答