为什么在memset上使用bzero?
在一个系统编程类中,我在上一学期学习了C语言,我们必须在C中实现一个基本的客户端/服务器。当初始化sock_addr_in
或者char缓冲区(我们用来在客户端和服务器之间来回发送数据)时,教授指示我们只使用bzero
而不是memset
来初始化它们。 他从来没有解释为什么,我很好奇,如果有这个有正当的理由?
我在这里看到: http : bzero
效率更高,因为它只是将内存调零,所以它不会“不得不做任何额外的memset
可能做的检查。 这仍然不一定是绝对不使用memset
来清零内存的原因。
bzero
被认为是弃用的,而且不是一个标准的C函数。 根据手册, memset
比bzero
更受青睐。 那么,为什么你还想在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 Cmemset
函数,因为bzero
比memset
(带有三个参数)更容易记住(只有两个参数)。 几乎每个支持套接字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
注意:
- 原始的
bzero
返回任何内容,memset
返回void指针(d
)。 这可以通过在定义中添加types转换为void来解决。 -
#ifndef bzero
不会阻止你隐藏原来的function,即使它存在。 它testing一个macros的存在。 这可能会造成很多混乱。 - 创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或任何内核/系统调用接口的深层次工作,我不必担心它们。 所有我应该担心的是这个呼叫满足了缓冲区置零的要求。 其他人提到哪一个比另一个更好,所以我会在这里停下来。