![](/img/trans.png)
[英]How to create sockaddr from sockaddr_in / sockaddr_in6 structure
[英]When zeroing a struct such as sockaddr_in, sockaddr_in6 and addrinfo before use, which is correct: memset, an initializer or either?
每當我在書籍,手冊頁和網站中查看真實代碼或示例套接字代碼時,我幾乎總會看到類似下面的內容:
struct sockaddr_in foo;
memset(&foo, 0, sizeof foo);
/* or bzero(), which POSIX marks as LEGACY, and is not in standard C */
foo.sin_port = htons(42);
代替:
struct sockaddr_in foo = { 0 };
/* if at least one member is initialized, all others are set to
zero (as though they had static storage duration) as per
ISO/IEC 9899:1999 6.7.8 Initialization */
foo.sin_port = htons(42);
要么:
struct sockaddr_in foo = { .sin_port = htons(42) }; /* New in C99 */
要么:
static struct sockaddr_in foo;
/* static storage duration will also behave as if
all members are explicitly assigned 0 */
foo.sin_port = htons(42);
例如,在將結構addrinfo提示傳遞給getaddrinfo之前,也可以找到相同的結果。
為什么是這樣? 據我所知,不使用memset的例子很可能等同於那樣做的例子,如果不是更好的話。 我意識到存在差異:
將這些結構設置為零時,這些差異中的任何一個是相關的還是必需的行為,因此使用初始化器是錯誤的? 如果是,為什么,以及哪個標准或其他來源驗證了這一點?
如果兩者都正確,為什么memset / bzero傾向於出現而不是初始化器? 這只是風格問題嗎? 如果是這樣,那很好,我不認為我們需要一個主觀的答案,哪個是更好的風格。
通常的做法是優先使用初始化程序而不是memset,因為通常不需要所有位零,而是我們希望類型的正確表示為零。 這些與套接字相關的結構是否相反?
在我的研究中,我發現POSIX似乎只需要在http://www.opengroup.org/onlinepubs/000095399/basedefs/netinet/in.h.html中將sockaddr_in6(而不是sockaddr_in)歸零,但沒有提到如何它應該歸零(memset或初始化器?)。 我認為BSD套接字早於POSIX,它不是唯一的標准,它們對遺留系統或現代非POSIX系統的兼容性考慮因素是什么?
就個人而言,我更喜歡從一種風格(也許是良好的實踐)的觀點來使用初始化器並完全避免memset,但我不情願因為:
部分初始化方法(即' { 0 }
')的一個問題是GCC會警告您初始化程序是不完整的(如果警告級別足夠高;我通常使用' -Wall
'並經常使用' -Wextra
') 。 使用指定的初始化方法,不應該給出警告,但C99仍然沒有得到廣泛使用 - 盡管這些部分可以廣泛使用,但可能在Microsoft的世界中。
我傾向於贊成一種方法:
static const struct sockaddr_in zero_sockaddr_in;
其次是:
struct sockaddr_in foo = zero_sockaddr_in;
在靜態常量中省略初始化程序意味着一切都為零 - 但編譯器不會干擾(不應該干擾)。 賦值使用編譯器的固有內存副本,除非編譯器嚴重不足,否則它不會比函數調用慢。
GCC版本4.4.2至4.6.0從GCC 4.7.1生成不同的警告。 具體來說,GCC 4.7.1將= { 0 }
初始化程序識別為“特殊情況”並且不會抱怨,而GCC 4.6.0等則抱怨。
考慮文件init.c
:
struct xyz
{
int x;
int y;
int z;
};
struct xyz xyz0; // No explicit initializer; no warning
struct xyz xyz1 = { 0 }; // Shorthand, recognized by 4.7.1 but not 4.6.0
struct xyz xyz2 = { 0, 0 }; // Missing an initializer; always a warning
struct xyz xyz3 = { 0, 0, 0 }; // Fully initialized; no warning
使用GCC 4.4.2(在Mac OS X上)編譯時,警告是:
$ /usr/gcc/v4.4.2/bin/gcc -O3 -g -std=c99 -Wall -Wextra -c init.c
init.c:9: warning: missing initializer
init.c:9: warning: (near initialization for ‘xyz1.y’)
init.c:10: warning: missing initializer
init.c:10: warning: (near initialization for ‘xyz2.z’)
$
使用GCC 4.5.1編譯時,警告是:
$ /usr/gcc/v4.5.1/bin/gcc -O3 -g -std=c99 -Wall -Wextra -c init.c
init.c:9:8: warning: missing initializer
init.c:9:8: warning: (near initialization for ‘xyz1.y’)
init.c:10:8: warning: missing initializer
init.c:10:8: warning: (near initialization for ‘xyz2.z’)
$
使用GCC 4.6.0編譯時,警告是:
$ /usr/gcc/v4.6.0/bin/gcc -O3 -g -std=c99 -Wall -Wextra -c init.c
init.c:9:8: warning: missing initializer [-Wmissing-field-initializers]
init.c:9:8: warning: (near initialization for ‘xyz1.y’) [-Wmissing-field-initializers]
init.c:10:8: warning: missing initializer [-Wmissing-field-initializers]
init.c:10:8: warning: (near initialization for ‘xyz2.z’) [-Wmissing-field-initializers]
$
使用GCC 4.7.1編譯時,警告是:
$ /usr/gcc/v4.7.1/bin/gcc -O3 -g -std=c99 -Wall -Wextra -c init.c
init.c:10:8: warning: missing initializer [-Wmissing-field-initializers]
init.c:10:8: warning: (near initialization for ‘xyz2.z’) [-Wmissing-field-initializers]
$
上面的編譯器是由我編譯的。 Apple提供的編譯器名義上是GCC 4.2.1和Clang:
$ /usr/bin/clang -O3 -g -std=c99 -Wall -Wextra -c init.c
init.c:9:23: warning: missing field 'y' initializer [-Wmissing-field-initializers]
struct xyz xyz1 = { 0 };
^
init.c:10:26: warning: missing field 'z' initializer [-Wmissing-field-initializers]
struct xyz xyz2 = { 0, 0 };
^
2 warnings generated.
$ clang --version
Apple clang version 4.1 (tags/Apple/clang-421.11.65) (based on LLVM 3.1svn)
Target: x86_64-apple-darwin11.4.2
Thread model: posix
$ /usr/bin/gcc -O3 -g -std=c99 -Wall -Wextra -c init.c
init.c:9: warning: missing initializer
init.c:9: warning: (near initialization for ‘xyz1.y’)
init.c:10: warning: missing initializer
init.c:10: warning: (near initialization for ‘xyz2.z’)
$ /usr/bin/gcc --version
i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$
正如SecurityMatt在下面的評論中指出的那樣, memset()
對於從內存復制結構的優勢在於,內存中的副本更昂貴,需要訪問兩個內存位置(源和目標)而不是一個。 相比之下,將值設置為零不必訪問內存以獲取源,而在現代系統上,內存是瓶頸。 因此,對於簡單的初始化器(其中相同的值,通常是所有零字節,正在放置在目標存儲器中), memset()
編碼應該比復制更快。 如果初始化器是值的復雜混合(不是所有零字節),那么可以改變平衡以支持初始化器,如果沒有別的話,可以用於符號緊湊性和可靠性。
沒有一個剪切和干燥的答案......可能從來沒有,現在沒有。 我仍傾向於使用初始化器,但memset()
通常是一種有效的替代方法。
我要說的是,無論是正確的,因為你永遠不應該創建一個類型的對象sockaddr_
任何自己。 而是始終使用getaddrinfo
(或有時是getsockname
或getpeername
)來獲取地址。
“struct sockaddr_in foo = {0};” 僅在第一次有效,而“memset(&foo,0,sizeof foo);” 每次運行該功能時都會清除它。
兩種方法都不應該有問題 - 填充字節的值無關緊要。 我懷疑memset()的使用源於早期使用Berkeley-ism bzero(),這可能早於結構化初始化器的引入或更高效。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.