简体   繁体   English

为什么使用bzero而不是memset?

[英]Why use bzero over memset?

In a Systems Programming class I took this previous semester, we had to implement a basic client/server in C. When initializing the structs, like sock_addr_in , or char buffers (that we used to send data back and forth between client and server) the professor instructed us to only use bzero and not memset to initialize them. 在我上一学期的系统编程课程中,我们必须在C中实现一个基本的客户端/服务器。在初始化结构时,比如sock_addr_in或char缓冲区(我们用来在客户端和服务器之间来回发送数据)教授指示我们只使用bzero而不是memset来初始化它们。 He never explained why, and I'm curious if there is a valid reason for this? 他从未解释过为什么,而且我很好奇是否有正当理由呢?

I see here: http://fdiv.net/2009/01/14/memset-vs-bzero-ultimate-showdown that bzero is more efficient due to the fact that is only ever going to be zeroing memory, so it doesn't have to do any additional checking that memset may do. 我在这里看到: httpbzero由于事实上只会将内存归零而效率更高,所以它没有我必须做任何memset可能做的额外检查。 That still doesn't necessarily seem like a reason to absolutely not use memset for zeroing memory though. 尽管如此,这仍然不一定是绝对不使用memset来归零内存的原因。

bzero is considered deprecated, and furthermore is a not a standard C function. bzero被认为已弃用,而且不是标准的C函数。 According to the manual, memset is preferred over bzero for this reason. 根据手册,由于这个原因, memsetbzerobzero So why would you want to still use bzero over memset ? 那么为什么你还想在memset使用bzero呢? Just for the efficiency gains, or is it something more? 只是为了提高效率,还是更多? Likewise, what are the benefits of memset over bzero that make it the de facto preferred option for newer programs? 同样, memsetbzero有什么好处,使它成为新程序的事实上的首选选项?

I don't see any reason to prefer bzero over memset . 我没有看到任何理由更喜欢bzero不是memset

memset is a standard C function while bzero has never been a C standard function. memset是标准的C函数,而bzero从来就不是C标准函数。 The rationale is probably because you can achieve exactly the same functionality using memset function. 基本原理可能是因为您可以使用memset函数实现完全相同的功能。

Now regarding efficiency, compilers like gcc use builtin implementations for memset which switch to a particular implementation when a constant 0 is detected. 现在关于效率,像gcc这样的编译器使用memset内置实现,当检测到常量0时切换到特定的实现。 Same for glibc when builtins are disabled. 内置函数被禁用时, glibc也是如此。

I'm guessing you used (or your teacher was influenced by) UNIX Network Programming by W. Richard Stevens. 我猜你曾经使用过(或者你的老师受到了影响) W。Richard Stevens的UNIX网络编程 He uses bzero frequently instead of memset , even in the most up-to-date edition. 他经常使用bzero而不是memset ,即使在最新版本中也是如此。 The book is so popular, I think it's become an idiom in network programming which is why you still see it used. 这本书如此受欢迎,我认为它已经成为网络编程的成语,这也是你仍然看到它被使用的原因。

I would stick with memset simply because bzero is deprecated and reduces portability. 我会坚持使用memset因为bzero已被弃用并降低了可移植性。 I doubt you would see any real gains from using one over the other. 我怀疑你会看到使用一个而不是另一个真正的收益。

The one advantage that I think bzero() has over memset() for setting memory to zero is that there's a reduced chance of a mistake being made. 我认为bzero()memset()设置内存为零的一个优点就是减少了出错的可能性。

More than once I've come across a bug that looked like: 不止一次,我遇到了一个看起来像的错误:

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

The compiler won't complain (though maybe cranking up some warning levels might on some compilers) and the effect will be that the memory isn't cleared. 编译器不会抱怨(虽然可能会在一些编译器上启动某些警告级别),但效果将是内存未被清除。 Because this doesn't trash the object - it just leaves it alone - there's a decent chance that the bug might not manifest into anything obvious. 因为这不会破坏对象 - 它只是让它一个人留下 - 这个错误很可能不会显示出任何明显的东西。

The fact that bzero() isn't standard is a minor irritant. bzero()不标准的事实是轻微的刺激。 (FWIW, I wouldn't be surprised if most function calls in my programs are non-standard; in fact writing such functions is kind of my job). (FWIW,如果我的程序中的大多数函数调用都是非标准的,我不会感到惊讶;事实上,编写这样的函数是我的工作)。

In a comment to another answer here, Aaron Newton cited the following from Unix Network Programming, Volume 1, 3rd Edition by Stevens, et al., Section 1.2 (emphasis added): 在另一个答案的评论中,Aaron Newton引用了以下来自Unix网络编程,第1卷,第3版,Stevens等人,第1.2节(重点补充):

bzero is not an ANSI C function. bzero不是ANSI C函数。 It is derived from early Berkely networking code. 它源于早期的Berkely网络代码。 Nevertheless, we use it throughout the text, instead of the ANSI C memset function, because bzero is easier to remember (with only two arguments) than memset (with three arguments). 然而,我们在整个文本中使用它,而不是ANSI C memset函数,因为bzeromemset (带有三个参数)更容易记住(只有两个参数)。 Almost every vendor that supports the sockets API also provides bzero , and if not, we provide a macro definition in our unp.h header. 几乎每个支持套接字API的供应商也提供bzero ,如果没有,我们在unp.h标头中提供宏定义。

Indeed, the author of TCPv3 [TCP/IP Illustrated, Volume 3 - Stevens 1996] made the mistake of swapping the second and third arguments to memset in 10 occurrences in the first printing . 实际上, TCPv3 [TCP / IP Illustrated,第3卷--Stevens 1996]的作者犯了错误,即在第一次打印中将第二个和第三个参数交换到memset AC compiler cannot catch this error because both arguments are of the same type. AC编译器无法捕获此错误,因为两个参数的类型相同。 (Actually, the second argument is an int and the third argument is size_t , which is typically an unsigned int , but the values specified, 0 and 16, respectively, are still acceptable for the other type of argument.) The call to memset still worked, because only a few of the socket functions actually require that the final 8 bytes of an Internet socket address structure be set to 0. Nevertheless, it was an error, and one that could be avoided by using bzero , because swapping the two arguments to bzero will always be caught by the C compiler if function prototypes are used. (实际上,第二个参数是一个int ,第三个参数是size_t ,它通常是一个unsigned int ,但是指定的值分别为0和16,对于另一种类型的参数仍然是可接受的。)对memset的调用仍然工作,因为只有少数套接字函数实际上要求将Internet套接字地址结构的最后8个字节设置为0.然而,这是一个错误,可以通过使用bzero来避免,因为交换两个参数如果使用函数原型,则始终会被C编译器捕获到bzero

I also believe that the vast majority of calls to memset() are to zero memory, so why not use an API that is tailored to that use case? 我也相信对memset()的绝大多数调用是零内存,那么为什么不使用针对该用例定制的API呢?

A possible drawback to bzero() is that compilers might be more likely to optimize memcpy() because it's standard and so they might be written to recognize it. bzero()一个可能的缺点是编译器可能更有可能优化memcpy()因为它是标准的,所以可能会编写它们来识别它。 However, keep in mind that correct code is still better than incorrect code that's been optimized. 但是,请记住,正确的代码仍然比已经优化的错误代码更好。 In most cases, using bzero() will not cause a noticeable impact on your program's performance, and that bzero() can be a macro or inline function that expands to memcpy() . 在大多数情况下,使用bzero()不会对程序的性能产生明显影响,并且bzero()可以是扩展为memcpy()的宏或内联函数。

In short: memset require more assembly operations then bzero . 简而言之: memset需要比bzero更多的装配操作。

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

You probably shouldn't use bzero , it's not actually standard C, it was a POSIX thing. 你可能不应该使用bzero ,它实际上不是标准的C,它是一个POSIX的东西。

And note that word "was" - it was deprecated in POSIX.1-2001 and removed in POSIX.1-2008 in deference to memset so you're better off using the standard C function. 请注意单词“was” - 它在POSIX.1-2001中弃用 ,并在POSIX.1-2008中被删除 ,因此您最好使用标准C函数。

Wanted to mention something about bzero vs. memset argument. 想提一下bzero和memset论点。 Install ltrace and then compare what it does under the hood. 安装ltrace然后比较它在引擎盖下的作用。 On Linux with libc6 (2.19-0ubuntu6.6), the calls made are exactly the same (via ltrace ./test123 ): 在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)

I've been told that unless I am working in the deep bowels of libc or any number of kernel/syscall interface, I don't have to worry about them. 我被告知,除非我在libc深层工作或任何数量的kernel / syscall接口工作,否则我不必担心它们。 All I should worry about is that the call satisfy the requirement of zero'ing the buffer. 我应该担心的是,该调用满足了对缓冲区进行置零的要求。 Others have mentioned about which one is preferable over the other so I'll stop here. 其他人提到哪一个优于另一个,所以我会在这里停下来。

Have it any way you like. 有你喜欢的任何方式。 :-) :-)

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

Note that: 注意:

  1. The original bzero returns nothing, memset returns void pointer ( d ). 原始的bzero没有返回任何内容, memset返回void指针( d )。 This can be fixed by adding the typecast to void in the definition. 这可以通过在定义中将类型转换添加到void来解决。
  2. #ifndef bzero does not prevent you from hiding the original function even if it exists. #ifndef bzero不会阻止您隐藏原始功能,即使它存在。 It tests the existence of a macro. 它测试宏的存在。 This may cause lots of confusion. 这可能会引起很多混乱。
  3. It's impossible to create a function pointer to a macro. 创建一个指向宏的函数指针是不可能的。 When using bzero via function pointers, this will not work. 通过函数指针使用bzero ,这将不起作用。

For memset function, the second argument is an int and the third argument is size_t , 对于memset函数,第二个参数是int ,第三个参数是size_t

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

which is typically an unsigned int , but if the values like, 0 and 16 for second and third argument respectively are entered in wrong order as 16 and 0 then, such a call to memset can still work, but will do nothing. 这通常是一个unsigned int ,但是如果第二个和第三个参数的值分别为0 and 16分别以16和0的错误顺序输入,那么对memset的这种调用仍然可以工作,但是什么都不做。 Because the number of bytes to initialize are specified as 0 . 因为要初始化的字节数指定为0

void bzero(void *s, size_t n)

Such an error can be avoided by using bzero, because swapping the two arguments to bzero will always be caught by the C compiler if function prototypes are used. 使用bzero可以避免这样的错误,因为如果使用函数原型,则将两个参数交换到bzero将始终被C编译器捕获。

memset需要3个参数,bzero需要2个内存约束,额外参数需要4个字节,大部分时间用于将所有内容设置为0

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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