문제

코드 검토 중에 간단한 구조를 다음과 같이 정의하는 일부 코드를 발견했습니다.

class foo {
   unsigned char a;
   unsigned char b;
   unsigned char c;
}

다른 곳에서는 이러한 객체의 배열이 정의됩니다.

foo listOfFoos[SOME_NUM];

나중에 구조물은 버퍼로 생성됩니다.

memcpy(pBuff,listOfFoos,3*SOME_NUM);

이 코드는 다음과 같은 가정에 의존한다.

나는 두 개의 플랫폼 (Redhat 64B, Solaris 9)에서 GNU와 함께 시도했으며 두 가지 모두에서 작동했습니다.

위의 가정이 유효합니까? 그렇지 않은 경우, 어떤 조건 하에서 (예 : OS/컴파일러의 변경) 실패 할 수 있습니까?

도움이 되었습니까?

해결책

객체 배열은 인접해야하므로 물체 사이에 패딩이 절대 패딩되지 않지만 패딩은 물체의 끝에 추가 될 수 있습니다 (거의 동일한 효과를 생성).

당신이 Char와 함께 일하고 있다는 점을 감안할 때 가정은 아마도 종종 옳지 않을 것입니다. 그러나 C ++ 표준은 확실히 그것을 보장하지 않습니다. 다른 컴파일러 또는 전류 컴파일러로 전달 된 플래그의 변화만으로 구조물의 요소 사이에 또는 구조물의 마지막 요소를 따르는 경우 패딩이 삽입 될 수 있습니다.

다른 팁

확실히 더 안전 할 것입니다.

sizeof(foo) * SOME_NUM

이렇게 배열을 복사하면 사용해야합니다.

memcpy(pBuff,listOfFoos,sizeof(listOfFoos));

이것은 Pbuff를 같은 크기로 할당하는 한 항상 작동합니다. 이런 식으로 당신은 패딩과 정렬에 대해 전혀 가정하지 않습니다.

대부분의 컴파일러는 구조물 또는 클래스를 포함 된 최대 유형의 필요한 정렬에 맞게 정렬합니다. 당신의 숯의 경우, 정렬 및 패딩을 의미하지는 않지만, 짧게 추가하면 클래스는 마지막 숯과 짧은 사이에 1 바이트의 패딩이 추가되면 6 바이트가 큰 6 바이트가됩니다.

구조의 모든 필드가 구조물을 정렬하는 숯이기 때문에 이것이 작동하는 이유는 생각합니다. 1을 정렬하지 않는 필드가 하나 이상있는 경우 구조/클래스의 정렬은 1이 아닙니다 (정렬은 필드 순서 및 정렬에 따라 다름).

몇 가지 예를 보자 :

#include <stdio.h>
#include <stddef.h>

typedef struct {
    unsigned char a;
    unsigned char b;
    unsigned char c;
} Foo;
typedef struct {
    서명되지 않은 짧은 i;
    unsigned char  a;
    unsigned char  b;
    unsigned char  c;
} Bar;
typedef struct { Foo F[5]; } F_B;
typedef struct { Bar B[5]; } B_F;


#define ALIGNMENT_OF(t) offsetof( struct { char x; t test; }, test )

int main(void) {
    printf("Foo:: Size: %d; Alignment: %d\n", sizeof(Foo), ALIGNMENT_OF(Foo));
    printf("Bar:: Size: %d; Alignment: %d\n", sizeof(Bar), ALIGNMENT_OF(Bar));
    printf("F_B:: Size: %d; Alignment: %d\n", sizeof(F_B), ALIGNMENT_OF(F_B));
    printf("B_F:: Size: %d; Alignment: %d\n", sizeof(B_F), ALIGNMENT_OF(B_F));
}

실행되면 결과는 다음과 같습니다.

Foo:: Size: 3; Alignment: 1
Bar:: Size: 6; Alignment: 2
F_B:: Size: 15; Alignment: 1
B_F:: Size: 30; Alignment: 2

Bar와 F_B가 정렬 2를 가지고있어 필드가 올바르게 정렬되도록합니다. 막대의 크기가 있음을 알 수 있습니다 6이 아닌 5. 마찬가지로 B_F (5의 5)의 크기는 30은 25가 아닙니다.

그래서 당신이 대신 하드 코드라면 sizeof(...), 당신은 여기서 문제가 생길 것입니다.

도움이 되었기를 바랍니다.

그것은 모두 메모리 정렬로 내려집니다. 일반적인 32 비트 기계는 시도 당 4 바이트의 메모리를 읽거나 씁니다. 이 구조는 혼란스러운 패딩 문제없이 쉽게 4 바이트에 해당하기 때문에 문제로부터 안전합니다.

이제 구조가 그렇게 된 경우 :

class foo {
   unsigned char a;
   unsigned char b;
   unsigned char c;
   unsigned int i;
   unsigned int j;
}

동료 논리가 아마도 이어질 것입니다

memcpy(pBuff,listOfFoos,11*SOME_NUM);

(3 char 's = 3 바이트, 2 int = 2*4 바이트, 따라서 3 + 8)

불행히도, 패딩으로 인해 구조는 실제로 12 바이트를 차지합니다. 이것은 당신이 4 개의 바이트 단어에 3 숯과 int를 맞출 수 없기 때문에, int를 자신의 단어로 밀어 넣는 패딩 된 공간의 바이트가 하나 있습니다. 이것은 데이터 유형이 더 다양할수록 점점 더 많은 문제가됩니다.

이와 같은 물건이 사용되는 상황에서는 피할 수없는 상황에서는 더 이상 추정이 유지되지 않을 때 편집을 중단하려고합니다. 나는 다음과 같은 것을 사용합니다 (또는 boost.staticassert 상황이 허용되는 경우) :

static_assert(sizeof(foo) <= 3);

// Macro for "static-assert" (only usefull on compile-time constant expressions)
#define static_assert(exp)           static_assert_II(exp, __LINE__)
// Macro used by static_assert macro (don't use directly)
#define static_assert_II(exp, line)  static_assert_III(exp, line)
// Macro used by static_assert macro (don't use directly)
#define static_assert_III(exp, line) enum static_assertion##line{static_assert_line_##line = 1/(exp)}

나는 안전했고 Magic Number 3을 sizeof(foo) 나는 생각한다.

내 생각에 미래의 프로세서 아키텍처에 최적화 된 코드는 아마도 어떤 형태의 패딩을 소개 할 것입니다.

그리고 그런 종류의 버그를 추적하려고하는 것은 진정한 고통입니다!

다른 사람들이 말했듯이, Sizeof (foo)를 사용하는 것은 더 안전합니다. 일부 컴파일러 (특히 임베디드 세계의 난해한 컴파일러)는 4 바이트 헤더를 클래스에 추가합니다. 다른 사람들은 컴파일러 설정에 따라 펑키 한 메모리 정렬 트릭을 수행 할 수 있습니다.

주류 플랫폼의 경우 아마도 괜찮지 만 보장은 아닙니다.

두 컴퓨터 사이에 데이터를 전달할 때 Sizeof ()에 여전히 문제가있을 수 있습니다. 그중 하나에서 코드는 패딩으로 컴파일 할 수 있고 다른 하나는없는 경우 ()는 다른 결과를 제공합니다. 배열 데이터가 한 컴퓨터에서 다른 컴퓨터로 전달되면 배열 요소가 예상되는 곳에서 찾을 수 없으므로 잘못 해석됩니다. 한 가지 해결책은 #Pragma Pack (1)가 가능할 때마다 사용되지만 배열에는 충분하지 않을 수 있습니다. 가장 좋은 것은 문제를 예견하고 배열 요소 당 8 바이트의 배를 사용하는 것입니다.

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