구조 패킹은 결정론적입니까?
예를 들어, 두 개의 동등한 구조가 있다고 가정합니다.a
그리고.b
다른 프로젝트:
typedef struct _a
{
int a;
double b;
char c;
} a;
typedef struct _b
{
int d;
double e;
char f;
} b;
만약 내가 어떤 지시도 사용하지 않았다고 가정한다면.#pragma pack
그리고 이 구조체들은 동일한 최적화 설정을 가진 동일한 아키텍처에 대해 동일한 컴파일러에서 컴파일됩니다. 변수들 사이에 동일한 패딩이 있을까요?
컴파일러는 결정론적입니다. 그렇지 않았다면 별도의 컴파일이 불가능했을 것입니다.▁with▁the 두 개의 다른 단위struct
선언은 § 6.2.7/1에 의해 보장되며, 호환되는 유형 및 복합 유형.
또한 동일한 플랫폼에 있는 두 개의 서로 다른 컴파일러가 상호 운용되어야 하지만, 이는 표준에 의해 보장되지 않습니다.(이는 구현 품질 문제입니다.)상호 운용성을 허용하기 위해 컴파일러 작성자는 복합 유형이 표현되는 방법에 대한 정확한 사양을 포함하는 플랫폼 ABI(Application Binary Interface)에 동의합니다.이러한 방식으로 한 컴파일러로 컴파일된 프로그램이 다른 컴파일러로 컴파일된 라이브러리 모듈을 사용할 수 있습니다.
그러나 여러분은 결정론에만 관심이 있는 것이 아니라 두 가지 다른 유형의 레이아웃이 동일하기를 원합니다.
2 에따면르준, 2.struct
멤버(순서대로 나열)가 호환되고 태그와 멤버 이름이 동일한 경우 유형이 호환됩니다.당신의 예시 이후로structs
태그와 이름이 서로 다르며 멤버 유형이 호환되지 않으므로 다른 태그와 이름이 필요한 곳에서는 사용할 수 없습니다.
표준에서 태그와 구성원 이름이 호환성에 영향을 미치는 것을 허용하는 것은 이상하게 보일 수 있습니다.표준에서는 구조체의 구성원을 선언 순서로 배치해야 하므로 이름은 구조체 내의 구성원 순서를 변경할 수 없습니다.그렇다면, 왜 그것들이 패딩에 영향을 미칠 수 있을까요?어디에 있는지는 모르지만, 표준의 유연성은 정확한 실행을 보장하는 데 필요한 최소 요건이어야 한다는 원칙에 기초하고 있습니다.서로 다른 태그가 지정된 구조체는 변환 단위 내에서 앨리어싱이 허용되지 않으므로 서로 다른 변환 단위 간에 앨리어싱을 허용할 필요가 없습니다.은 그것을 (를 삽입하는 입니다.)struct
이러한 정보를 위한 공간을 제공하기 위해 패딩을 결정적으로 추가해야 하는 경우에도 의 패딩 바이트입니다.이 일한제의패딩을의 첫 앞에 수입니다.struct
.)
플랫폼 ABI는 태그 또는 구성원 이름을 참조하지 않고 복합 유형의 레이아웃을 지정할 수 있습니다.특정 플랫폼에서, 그러한 사양을 가진 플랫폼 ABI와 플랫폼 ABI를 준수하도록 문서화된 컴파일러를 사용하면 기술적으로 정확하지는 않지만 별칭을 피할 수 있으며, 분명히 전제 조건으로 인해 이식할 수 없습니다.
C 표준 자체는 그것에 대해 아무 것도 말하지 않기 때문에 원칙적으로 당신은 확신할 수 없습니다.
그러나: 대부분의 컴파일러는 특정 ABI를 준수합니다. 그렇지 않으면 다른 라이브러리 및 운영 체제와 통신하는 것은 악몽이 될 것입니다.이 마지막 경우, ABI는 일반적으로 포장이 어떻게 작동하는지 정확하게 규정합니다.
예:
x86_64 Linux/BSD의 경우 SystemV AMD64 ABI가 참조입니다.여기서 (§3.1)의 모든 원시 프로세서 데이터 유형에 대해 C 유형과의 대응, 크기 및 정렬 요구 사항을 자세히 설명하고, 이 데이터를 사용하여 비트 필드, 구조체 및 유니언의 메모리 레이아웃을 구성하는 방법을 설명합니다. 패딩의 실제 내용 외에 모든 것이 지정되고 결정론적입니다.다른 많은 아키텍처에서도 마찬가지입니다. 다음 링크를 참조하십시오.
ARM은 프로세서에 대해 EABI를 권장하며, 일반적으로 Linux와 Windows 모두를 따르고 있습니다. Aggregate 정렬은 "ARM 아키텍처 설명서의 프로시저 호출 표준" § 4.3에 명시되어 있습니다.
윈도우에는 교차 공급업체 표준이 없지만 VC++는 사실상 모든 컴파일러가 고수하는 ABI를 지시합니다. 여기에서는 x86_64, 여기서는 ARM에 대해 찾을 수 있습니다(그러나 이 질문의 관심 부분에서는 ARM EABI만 언급합니다).
모든 정상 컴파일러는 두 구조체에 대해 동일한 메모리 레이아웃을 생성합니다.컴파일러는 일반적으로 완벽하게 결정론적인 프로그램으로 작성됩니다.비결정론은 명시적이고 의도적으로 추가될 필요가 있을 것이며, 저는 그렇게 하는 것의 이점을 보지 못합니다.
하지만, 그것은 당신이 캐스팅하는 것을 허락하지 않습니다.struct _a*
struct _b*
둘 다를 통해 데이터에 액세스할 수 있습니다.가 FAIK를 정렬할 수에 엄격한 입니다. 컴파일러가 다음을 통해 액세스를 다시 정렬할 수 있기 때문입니다.struct _a*
수 있습니다.struct _b*
예측할 수 없고 정의되지 않은 동작을 초래할 수 있습니다.
변수 사이에 동일한 패딩이 있습니까?
실제로, 그들은 대부분 동일한 메모리 레이아웃을 선호합니다.
이론적으로, 표준은 물체에 패딩이 어떻게 사용되어야 하는지에 대해 많은 것을 말하지 않기 때문에, 당신은 요소들 사이의 패딩에 대해 실제로 어떤 것도 가정할 수 없습니다.
또한, 구조물의 구성원들 사이의 패딩에 대해 왜 알고 싶어하는지도 모르겠습니다. 표준 C 코드를 작성하기만 하면 괜찮을 것입니다.
다른 시스템에서 C 언어로 된 구조나 결합의 레이아웃을 결정론적으로 접근할 수 없습니다.
다른 컴파일러에 의해 생성된 레이아웃이 동일한 것처럼 보일 수 있는 경우가 많지만, 표준에 의해 프로그래머에게 남겨진 선택의 자유라는 야망에서 컴파일러 설계의 실용적이고 기능적인 편의에 의해 지시된 수렴을 고려해야 하므로 효과적이지 않습니다.
C11 표준 ISO/IEC 9899:2011은 이전 표준과 거의 변화가 없으며, 단락 6.7.2.1 구조 및 조합 지정자에 명확하게 명시되어 있습니다.
구조체 또는 결합 객체의 각 비트 필드가 아닌 멤버는 해당 유형에 적합한 구현 정의 방식으로 정렬됩니다.
심지어 프로그래머에게 큰 자율성을 맡기는 비트필드의 경우도 최악입니다.
구현은 비트 필드를 저장할 수 있을 만큼 충분히 큰 주소 지정 가능한 저장 장치를 할당할 수 있습니다.충분한 공간이 남아 있는 경우 구조물의 다른 비트 필드 바로 뒤에 오는 비트 필드는 동일한 장치의 인접 비트에 패킹되어야 합니다.공간이 부족할 경우 적합하지 않은 비트 필드를 다음 장치에 넣거나 인접 장치와 겹치는지 여부가 구현 정의됩니다.장치 내 비트 필드 할당 순서(높은 순서에서 낮은 순서 또는 낮은 순서에서 높은 순서)는 구현 정의됩니다.주소 지정 가능한 저장 장치의 정렬이 지정되지 않았습니다.
텍스트에 '구현 정의됨' 및 '지정되지 않음'이라는 용어가 몇 번 나타나는지 계산해 보십시오.
다른 시스템에서 생성된 구조나 결합을 사용하기 전에 컴파일러 버전, 기계 및 대상 아키텍처를 각각 실행하는 것을 확인하는 것은 감당할 수 없는 일이라는 데 동의했습니다.
이제 예, 방법이 있다고 합시다.
이 방법이 반드시 해결책은 아니지만, 데이터 구조 교환이 다른 시스템 간에 공유될 때 찾을 수 있는 공통적인 접근 방식이라는 점을 분명히 해야 합니다. 즉, 값 1(표준 문자 크기)의 팩 구조 요소입니다.
패킹과 정확한 구조 정의를 사용하면 서로 다른 시스템에서 사용할 수 있는 충분히 신뢰할 수 있는 선언을 할 수 있습니다.패킹을 사용하면 컴파일러가 구현 정의 정렬을 제거하여 표준으로 인한 비호환성을 줄일 수 있습니다.또한 비트 필드를 사용하지 않아도 나머지 구현 종속 불일치를 제거할 수 있습니다.마지막으로 요소 사이에 일부 더미 선언을 수동으로 추가하여 정렬 누락으로 인한 액세스 효율성을 다시 생성할 수 있습니다. 이 선언은 올바른 정렬에서 각 필드를 강제로 되돌리는 방식으로 작성됩니다.
나머지 경우에는 일부 컴파일러가 추가하는 구조 끝 부분의 패딩을 고려해야 하지만, 관련된 유용한 데이터가 없기 때문에 무시할 수 있습니다(동적 공간 할당을 제외하고는 다시 처리할 수 있음).
는 두 개의 ISO C에따면 2라고 .struct
태그와 멤버가 동일한 경우 서로 다른 번역 단위의 유형이 호환됩니다.는, 가 여기 : 정확하게는더다표 C99 니입텍스트정확한.
6.2.7 호환되는 유형 및 복합 유형
두 유형의 유형이 동일할 경우 호환되는 유형이 있습니다.두 가지 유형의 호환성 여부를 확인하기 위한 추가 규칙은 형식 지정자의 경우 6.7.2, 형식 한정자의 경우 6.7.3, 선언자의 경우 6.7.5에 설명되어 있습니다.또한 별도의 번역 단위로 선언된 두 개의 구조, 결합 또는 열거형은 태그와 멤버가 다음 요구 사항을 충족할 경우 호환됩니다.하나가 태그로 선언된 경우, 다른 하나는 동일한 태그로 선언되어야 합니다.둘 다 완전한 유형인 경우, 다음과 같은 추가 요구사항이 적용됩니다. 각 구성원 쌍은 호환 가능한 유형으로 선언되어야 하며, 해당 구성원 쌍의 한 구성원이 이름으로 선언되면 다른 구성원이 동일한 이름으로 선언되어야 합니다.두 구조물의 경우, 해당 구성원은 동일한 순서로 선언되어야 합니다.두 구조물 또는 유니언의 경우 해당 비트 필드의 폭이 같아야 합니다.두 개의 열거형에 대해 해당 구성원의 값은 동일해야 합니다.
'뭐, 태그나 멤버 이름이 패딩에 영향을 줄 수 있을까'라는 관점에서 해석하면 굉장히 이상해 보입니다.그러나 기본적으로 규칙은 일반적인 경우를 허용하면서 가능한 한 엄격합니다. 즉, 헤더 파일을 통해 구조 선언의 정확한 텍스트를 공유하는 여러 변환 장치입니다.프로그램이 느슨한 규칙을 따른다면 틀린 것이 아닙니다. 프로그램은 표준의 요구 사항이 아니라 다른 곳의 요구 사항에 의존하고 있습니다.
이 예에서는 구조적 동등성만 있고 태그 및 구성원 이름은 동일하지 않으므로 언어 규칙을 위반하여 실행하고 있습니다.실제로 이것은 실제로 적용되지 않습니다. 서로 다른 태그와 다른 번역 단위의 멤버 이름을 가진 구조체 유형은 사실상 물리적으로 호환됩니다.비 C 언어에서 C 라이브러리로의 바인딩과 같은 모든 종류의 기술이 여기에 의존합니다.
두 프로젝트가 모두 C(또는 C++)인 경우에는 공통 헤더에 정의를 넣는 것이 좋습니다.
크기 필드와 같은 버전 관리 문제에 대한 방어책을 마련하는 것도 좋습니다.
// Widely shared definition between projects affecting interop!
// Do not change any of the members.
// Add new ones only at the end!
typedef struct a
{
size_t size; // of whole structure
int a;
double b;
char c;
} a;
은 그생은누그구사의 를 구성하는 a
해야 합니다.size
에서 의로필까지.sizeof (a)
그런 다음 개체가 다른 소프트웨어 구성 요소(아마도 다른 프로젝트에서)로 전달될 때 크기를 확인할 수 있습니다. sizeof (a)
, 은 크기필더작구성소알다수있을 .a
구성원 수가 적은 이전 선언을 사용하고 있습니다.따라서 존재하지 않는 구성원은 액세스할 수 없습니다.
모든 특정 컴파일러는 결정론적이어야 하지만, 두 컴파일러 간, 심지어 다른 컴파일러 옵션을 가진 동일한 컴파일러 간, 또는 동일한 컴파일러의 다른 버전 간에도 모든 베팅이 해제됩니다.
구조의 세부 사항에 의존하지 않거나, 의존하는 경우 런타임에 실제로 구조가 의존하는 대로 있는지 확인하기 위해 코드를 포함해야 합니다.
이에 대한 좋은 예는 구조에 사용되는 정수의 크기를 변경하지 않더라도 부분 정수의 기본 패킹이 변경된 최근의 32비트 아키텍처입니다. 이전에는 행에 있는 332비트 정수가 완벽하게 패킹되었지만 이제는 두 개의 64비트 슬롯에 패킹됩니다.
구조 패킹과 같이 언어에서 보장하지 않는 세부 정보에 의존하는 경우에는 실행 시에 가정을 확인해야 합니다.
네. 컴파일러에서 항상 결정론적인 행동을 가정해야 합니다.
[편집] 아래 댓글을 보면 위의 질문을 읽는 자바 프로그래머가 많은 것이 분명합니다.C 구조는 객체 파일, 라이브러리 또는 dll에서 이름, 해시 등을 생성하지 않습니다.C 함수 서명도 참조하지 않습니다.즉, 멤버 변수의 유형과 순서가 동일하다면 멤버 이름을 마음대로 변경할 수 있습니다.C에서는 패킹이 변경되지 않으므로 예제의 두 구조가 동일합니다.즉, 다음과 같은 학대는 C에서 완벽하게 유효하며, 가장 널리 사용되는 일부 도서관에서 훨씬 더 나쁜 학대가 발견된다는 것을 의미합니다.
[EDIT2] 누구도 감히 C++에서 다음 중 하나를 해서는 안 됩니다.
/* the 3 structures below are 100% binary compatible */
typedef struct _a { int a; double b; char c; }
typedef struct _b { int d; double e; char f; }
typedef struct SOME_STRUCT { int my_i; double my_f; char my_c[1]; }
struct _a a = { 1, 2.5, 'z' };
struct _b b;
/* the following is valid, copy b -> a */
*(SOME_STRUCT*)&a = *(SOME_STRUCT*)b;
assert((SOME_STRUCT*)&a)->my_c[0] == b.f);
assert(a.c == b.f);
/* more generally these identities are always true. */
assert(sizeof(a) == sizeof(b));
assert(memcmp(&a, &b, sizeof(a)) == 0);
assert(pure_function_requiring_a(&a) == pure_function_requiring_a((_a*)&b));
assert(pure_function_requiring_b((b*)&a) == pure_function_requiring_b(&b));
function_requiring_a_SOME_STRUCT_pointer(&a); /* may generate a warning, but not all compiler will */
/* etc... the name space abuse is limited to the programmer's imagination */
언급URL : https://stackoverflow.com/questions/44485168/is-struct-packing-deterministic
'programing' 카테고리의 다른 글
LIKE가 없는 Oracle 문자열에서 하위 문자열 확인 (0) | 2023.07.01 |
---|---|
Mongoose FindOneAndUpdate 및 실행 Validator가 작동하지 않습니다. (0) | 2023.07.01 |
스프링 부트 자동 배선이 구성 클래스에서 작동하지 않음 (0) | 2023.06.26 |
SQL Server 2005 제약 조건이 있는 드롭 열 (0) | 2023.06.26 |
데이터를 파일이 아닌 문자열로 CSV 형식으로 쓰는 방법은 무엇입니까? (0) | 2023.06.26 |