为什么在memset上使用bzero?

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

我在这里看到: http : bzero效率更高,因为它只是将内存调零,所以它不会“不得不做任何额外的memset可能做的检查。 这仍然不一定是绝对不使用memset来清零内存的原因。

bzero被认为是弃用的,而且不是一个标准的C函数。 根据手册, memsetbzero更受青睐。 那么,为什么你还想在memset使用bzero呢? 只是为了提高效率,还是更多? 同样的, memset优于bzero的好处是使它成为新程序事实上的首选。

我看不出有什么理由比memset更喜欢bzero

memset是一个标准的C函数,而bzero从来不是一个C标准函数。 理由可能是因为你可以使用memset函数实现完全相同的function。

现在关于效率,像gcc这样的编译器使用memset内置实现,当检测到常量0时,它将切换到特定的实现。 当内build函数被禁用时, glibc也一样。

我猜你已经使用了(或者你的老师受到了)由W. Richard Stevens 编写UNIXnetworking编程 。 他经常使用bzero而不是memset ,即使在最新的版本中也是如此。 这本书是如此受欢迎,我认为它已经成为networking编程的一个成语,这就是为什么你仍然看到它被使用。

我会坚持使用memset因为bzero已被弃用,降低了可移植性。 我怀疑你会看到使用一个在另一个上的任何真正的收益。

我认为bzero()memset()将memory设置为零的一个优点是减less了出错的几率。

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

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

编译器不会抱怨(尽pipe可能会在某些编译器上启动一些警告级别),结果是内存不会被清除。 因为这不会让对象变成垃圾 – 它只是让它一个人呆着 – 这个错误可能不会显现出来。

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

在这里的另一个答案的评论中,Aaron Newton引用了Stevens等人的第1版第3版的Unix Network Programming第1卷第1.2节(着重部分):

bzero不是ANSI C函数。 它来源于早期的Berkelynetworking代码。 不过,我们在整个文本中使用它,而不是ANSI C memset函数,因为bzeromemset (带有三个参数)更容易记住(只有两个参数)。 几乎每个支持套接字API的供应商也提供bzero ,如果没有的话,我们在我们的unp.h头文件中提供一个macros定义。

事实上, TCPv3 [TCP / IP Illustrated,第3卷 – Stevens 1996]的作者在第一次印刷中发生了10次交换第二和第三个参数到memset的错误 。 AC编译器不能捕获此错误,因为这两个参数是相同的types。 (实际上,第二个参数是一个int ,第三个参数是size_t ,通常是一个unsigned int ,但是指定的值分别是0和16,对于其他types的参数仍然是可以接受的)。对memset的调用仍然是因为只有一些套接字函数实际上要求Internet套接字地址结构的最后8个字节被设置为0.然而,这是一个错误,可以通过使用bzero来避免,因为交换了两个参数如果使用函数原型的话, bzero总会被C编译器捕获。

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

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

简而言之: memset需要更多的汇编操作,然后bzero

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

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

并注意单词“was” – 它在POSIX.1-2001中被弃用 ,并在POSIX.1-2008中被移除 ,因为memset所以你最好使用标准的C函数。

有任何你喜欢的方式。 🙂

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

注意:

  1. 原始的bzero返回任何内容, memset返回void指针( d )。 这可以通过在定义中添加types转换为void来解决。
  2. #ifndef bzero不会阻止你隐藏原来的function,即使它存在。 它testing一个macros的存在。 这可能会造成很多混乱。
  3. 创build一个指向macros的函数指针是不可能的。 当通过函数指针使用bzero ,这将不起作用。

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

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

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

 void bzero(void *s, size_t n) 

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

想要提到一些关于bzero与memset的争论。 安装ltrace,然后比较它在底层的function。 在使用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或任何内核/系统调用接口的深层次工作,我不必担心它们。 所有我应该担心的是这个呼叫满足了缓冲区置零的要求。 其他人提到哪一个比另一个更好,所以我会在这里停下来。