[英]Why use bzero over memset?
在我上一学期的系统编程课程中,我们必须在C中实现一个基本的客户端/服务器。在初始化结构时,比如sock_addr_in
或char缓冲区(我们用来在客户端和服务器之间来回发送数据)教授指示我们只使用bzero
而不是memset
来初始化它们。 他从未解释过为什么,而且我很好奇是否有正当理由呢?
我在这里看到: http : bzero
由于事实上只会将内存归零而效率更高,所以它没有我必须做任何memset
可能做的额外检查。 尽管如此,这仍然不一定是绝对不使用memset
来归零内存的原因。
bzero
被认为已弃用,而且不是标准的C函数。 根据手册,由于这个原因, memset
比bzero
更bzero
。 那么为什么你还想在memset
使用bzero
呢? 只是为了提高效率,还是更多? 同样, memset
比bzero
有什么好处,使它成为新程序的事实上的首选选项?
我没有看到任何理由更喜欢bzero
不是memset
。
memset
是标准的C函数,而bzero
从来就不是C标准函数。 基本原理可能是因为您可以使用memset
函数实现完全相同的功能。
现在关于效率,像gcc
这样的编译器使用memset
内置实现,当检测到常量0
时切换到特定的实现。 内置函数被禁用时, glibc
也是如此。
我猜你曾经使用过(或者你的老师受到了影响) W。Richard Stevens的UNIX网络编程 。 他经常使用bzero
而不是memset
,即使在最新版本中也是如此。 这本书如此受欢迎,我认为它已经成为网络编程的成语,这也是你仍然看到它被使用的原因。
我会坚持使用memset
因为bzero
已被弃用并降低了可移植性。 我怀疑你会看到使用一个而不是另一个真正的收益。
我认为bzero()
对memset()
设置内存为零的一个优点就是减少了出错的可能性。
不止一次,我遇到了一个看起来像的错误:
memset(someobject, size_of_object, 0); // clear object
编译器不会抱怨(虽然可能会在一些编译器上启动某些警告级别),但效果将是内存未被清除。 因为这不会破坏对象 - 它只是让它一个人留下 - 这个错误很可能不会显示出任何明显的东西。
bzero()
不标准的事实是轻微的刺激。 (FWIW,如果我的程序中的大多数函数调用都是非标准的,我不会感到惊讶;事实上,编写这样的函数是我的工作)。
在另一个答案的评论中,Aaron Newton引用了以下来自Unix网络编程,第1卷,第3版,Stevens等人,第1.2节(重点补充):
bzero
不是ANSI C函数。 它源于早期的Berkely网络代码。 然而,我们在整个文本中使用它,而不是ANSI Cmemset
函数,因为bzero
比memset
(带有三个参数)更容易记住(只有两个参数)。 几乎每个支持套接字API的供应商也提供bzero
,如果没有,我们在unp.h
标头中提供宏定义。实际上, TCPv3 [TCP / IP Illustrated,第3卷--Stevens 1996]的作者犯了错误,即在第一次打印中将第二个和第三个参数交换到
memset
中 。 AC编译器无法捕获此错误,因为两个参数的类型相同。 (实际上,第二个参数是一个int
,第三个参数是size_t
,它通常是一个unsigned int
,但是指定的值分别为0和16,对于另一种类型的参数仍然是可接受的。)对memset
的调用仍然工作,因为只有少数套接字函数实际上要求将Internet套接字地址结构的最后8个字节设置为0.然而,这是一个错误,可以通过使用bzero
来避免,因为交换两个参数如果使用函数原型,则始终会被C编译器捕获到bzero
。
我也相信对memset()
的绝大多数调用是零内存,那么为什么不使用针对该用例定制的API呢?
bzero()
一个可能的缺点是编译器可能更有可能优化memcpy()
因为它是标准的,所以可能会编写它们来识别它。 但是,请记住,正确的代码仍然比已经优化的错误代码更好。 在大多数情况下,使用bzero()
不会对程序的性能产生明显影响,并且bzero()
可以是扩展为memcpy()
的宏或内联函数。
简而言之: memset
需要比bzero
更多的装配操作。
这是来源: http : //fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown
你可能不应该使用bzero
,它实际上不是标准的C,它是一个POSIX的东西。
请注意单词“was” - 它在POSIX.1-2001中已被弃用 ,并在POSIX.1-2008中被删除 ,因此您最好使用标准C函数。
想提一下bzero和memset论点。 安装ltrace然后比较它在引擎盖下的作用。 在Linux上使用libc6(2.19-0ubuntu6.6),所做的调用完全相同(通过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)
or any number of kernel/syscall interface, I don't have to worry about them. 我被告知,除非我在的工作或任何数量的kernel / syscall接口工作,否则我不必担心它们。 我应该担心的是,该调用满足了对缓冲区进行置零的要求。 其他人提到哪一个优于另一个,所以我会在这里停下来。
有你喜欢的任何方式。 :-)
#ifndef bzero
#define bzero(d,n) memset((d),0,(n))
#endif
注意:
bzero
没有返回任何内容, memset
返回void指针( d
)。 这可以通过在定义中将类型转换添加到void来解决。 #ifndef bzero
不会阻止您隐藏原始功能,即使它存在。 它测试宏的存在。 这可能会引起很多混乱。 bzero
,这将不起作用。 对于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可以避免这样的错误,因为如果使用函数原型,则将两个参数交换到bzero将始终被C编译器捕获。
memset需要3个参数,bzero需要2个内存约束,额外参数需要4个字节,大部分时间用于将所有内容设置为0
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.