사용하기 전에 sockaddr_in, sockaddr_in6 및 addrinfo와 같은 구조물을 제로화 할 때, Memset, Initializer 또는 어느 쪽?

StackOverflow https://stackoverflow.com/questions/894300

  •  23-08-2019
  •  | 
  •  

문제

책, 남자 페이지 및 웹 사이트에서 실제 코드 또는 예제 소켓 코드를 볼 때마다 나는 거의 항상 다음과 같은 것을 본다.

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);

예를 들어 GetAddrinfo로 전달하기 전에 Struct Addrinfo 힌트를 0으로 설정하는 것도 동일하게 찾을 수 있습니다.

왜 이런거야? 내가 이해하는 한, Memset을 사용하지 않는 예는 더 좋지 않더라도 그렇지 않은 것과 동일 할 수 있습니다. 차이점이 있다는 것을 알고 있습니다.

  • Memset은 모든 비트를 0으로 설정하며, 이는 각 멤버를 0으로 설정하기위한 올바른 비트 표현 일 필요는 없습니다.
  • Memset은 또한 패딩 비트를 0으로 설정합니다.

이러한 차이 중 하나가 이러한 구조를 0으로 설정할 때 관련이 있거나 필요한 동작입니까? 따라서 이니셜 라이저를 사용하는 대신 잘못된 것입니까? 그렇다면 왜, 그리고 어떤 표준 또는 기타 소스가 이것을 확인합니까?

둘 다 정확하다면 왜 Memset/Bzero가 이니셜 라이저 대신에 나타나는 경향이 있습니까? 스타일의 문제입니까? 그렇다면 괜찮습니다. 더 나은 스타일 인 주관적인 대답이 필요하다고 생각하지 않습니다.

그만큼 보통의 연습은 모든 비트 제로가 일반적으로 원하지 않기 때문에 Memset을 선호하는 초기화를 선호하는 것입니다. 이 소켓 관련 스트러크의 반대가 사실입니까?

내 연구에서 나는 posix가 sockaddr_in6 (그리고 sockaddr_in이 아님)만이 필요하다는 것을 알았습니다. http://www.opengroup.org/onlinepubs/000095399/basedefs/netinet/in.h.html 그러나 그것이 제로화되어야하는 방법에 대해서는 언급하지 않습니다 (Memset 또는 Inerializer?). BSD 소켓은 POSIX를 사전에 미리 알고 있으며 유일한 표준은 아니며 레거시 시스템 또는 최신 비 포스 시스템에 대한 호환성 고려 사항입니까?

개인적으로, 나는 이니셜 라이저를 사용하고 Memset을 완전히 피하기 위해 스타일 (그리고 아마도 좋은 관행)의 관점에서 선호하지만, 나는 다음과 같이 꺼려합니다.

  • 다른 소스 코드 및 반 캐논 텍스트와 같은 유닉스 네트워크 프로그래밍 BZero를 사용하십시오 (예 : 2nd ed. 및 Page 124에서 124 페이지의 Page 101 (나는 둘 다 소유)).
  • 나는 그들이 위에서 언급 한 이유 때문에 그것들이 동일하지 않다는 것을 잘 알고 있습니다.
도움이 되었습니까?

해결책

부분 초기화기 접근 방식의 한 가지 문제 (즉{ 0 }') GCC는 이니셜 라이저가 불완전하다고 경고 할 것입니다 (경고 레벨이 충분히 높으면 보통 사용합니다.'-Wall'그리고 종종'-Wextra'). 지정된 이니셜 라이저 접근 방식을 사용하면 경고가 제공되지 않아야하지만 C99는 여전히 널리 사용되지 않습니다. 그러나 이러한 부분은 Microsoft 세계를 제외하고는 상당히 널리 사용 가능합니다.

추세 접근 방식을 선호하는 데 사용됩니다.

static const struct sockaddr_in zero_sockaddr_in;

다음과 같은

struct sockaddr_in foo = zero_sockaddr_in;

정적 상수에서 이니셜 라이저의 누락은 모든 것이 0이라는 것을 의미하지만 컴파일러는 재치가 없어야합니다 (비터는 아닙니다). 할당은 컴파일러의 타고난 메모리 사본을 사용하여 컴파일러가 심각하게 부족하지 않으면 함수 호출보다 느리지 않습니다.


GCC는 시간이 지남에 따라 변경되었습니다

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() 메모리에서 구조를 복사하는 것은 메모리의 사본이 더 비싸므로 단지 하나가 아닌 두 개의 메모리 위치 (소스 및 대상)에 대한 액세스가 필요하다는 것입니다. 이에 비해 값을 0으로 설정하는 것은 소스의 메모리에 액세스 할 필요가 없으며 최신 시스템에서 메모리는 병목 현상입니다. 그래서, memset() 코딩은 간단한 초기화기의 복사보다 빠르야합니다 (일반적으로 동일한 값, 일반적으로 모든 제로 바이트가 대상 메모리에 배치되는 경우). 이니셜 라이더가 복잡한 값 (모든 제로 바이트가 아님)의 복잡한 혼합 인 경우, 기타 소형성과 신뢰성을 위해 균형이 초기화에 유리하여 변경 될 수 있습니다.

단일 컷과 건조 된 대답은 없습니다 ... 아마도 없었을 것입니다. 나는 여전히 초기화기를 사용하는 경향이 있지만 memset() 종종 유효한 대안입니다.

다른 팁

"struct sockaddr_in foo = {0};" "memset (& foo, 0, foofof foo)"인 반면 처음으로 만 유효합니다. 함수가 실행될 때마다 지우십시오.

나는 당신이 유형의 객체를 만들어서는 안되기 때문에도 옳지 않다고 말할 것입니다. sockaddr_아무것 당신 자신. 대신 항상 사용하십시오 getaddrinfo (또는 가끔 getsockname 또는 getpeername) 주소를 얻기 위해.

두 가지 접근 방식에 문제가 없어야합니다. 패딩 바이트의 값은 중요하지 않습니다. Memset ()의 사용은 Berkeley-ism Bzero ()의 초기 사용에서 비롯된 것으로 생각되며, 이는 구조 초기화기의 도입을 유발했거나보다 효율적 일 수 있습니다.

많은 사람들이 지적한대로 하나는 맞습니다. 또한 이러한 구조물을 함께 할당 할 수 있습니다 Calloc 이미 제로 메모리 블록을 반환합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top