繁体   English   中英

为什么使用bzero而不是memset?

[英]Why use bzero over memset?

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

我在这里看到: httpbzero由于事实上只会将内存归零而效率更高,所以它没有我必须做任何memset可能做的额外检查。 尽管如此,这仍然不一定是绝对不使用memset来归零内存的原因。

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

我没有看到任何理由更喜欢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 C memset函数,因为bzeromemset (带有三个参数)更容易记住(只有两个参数)。 几乎每个支持套接字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

注意:

  1. 原始的bzero没有返回任何内容, memset返回void指针( d )。 这可以通过在定义中将类型转换添加到void来解决。
  2. #ifndef bzero不会阻止您隐藏原始功能,即使它存在。 它测试宏的存在。 这可能会引起很多混乱。
  3. 创建一个指向宏的函数指针是不可能的。 通过函数指针使用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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM